sandboxbox 2.0.4 ā 2.0.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/0.60 +0 -0
- package/Dockerfile +94 -94
- package/README.md +242 -242
- package/cli.js +341 -277
- package/npx-test/package.json +1 -0
- package/package.json +38 -38
- package/scripts/download-podman.js +237 -148
- package/test/Dockerfile +19 -0
- package/test/index.js +16 -0
- package/test/package.json +13 -0
- package/bin/.gitkeep +0 -1
package/package.json
CHANGED
@@ -1,38 +1,38 @@
|
|
1
|
-
{
|
2
|
-
"name": "sandboxbox",
|
3
|
-
"version": "2.0.
|
4
|
-
"description": "Portable container runner with Podman - Claude Code & Playwright support. Works on Windows, macOS, and Linux.",
|
5
|
-
"type": "module",
|
6
|
-
"main": "cli.js",
|
7
|
-
"bin": {
|
8
|
-
"sandboxbox": "./cli.js"
|
9
|
-
},
|
10
|
-
"scripts": {
|
11
|
-
"start": "node cli.js",
|
12
|
-
"postinstall": "node scripts/download-podman.js"
|
13
|
-
},
|
14
|
-
"keywords": [
|
15
|
-
"containers",
|
16
|
-
"podman",
|
17
|
-
"playwright",
|
18
|
-
"claude-code",
|
19
|
-
"sandbox",
|
20
|
-
"isolation",
|
21
|
-
"cross-platform",
|
22
|
-
"windows",
|
23
|
-
"macos",
|
24
|
-
"linux",
|
25
|
-
"portable"
|
26
|
-
],
|
27
|
-
"author": "",
|
28
|
-
"license": "MIT",
|
29
|
-
"dependencies": {},
|
30
|
-
"repository": {
|
31
|
-
"type": "git",
|
32
|
-
"url": "https://github.com/AnEntrypoint/sandboxbox.git"
|
33
|
-
},
|
34
|
-
"homepage": "https://github.com/AnEntrypoint/sandboxbox#readme",
|
35
|
-
"bugs": {
|
36
|
-
"url": "https://github.com/AnEntrypoint/sandboxbox/issues"
|
37
|
-
}
|
38
|
-
}
|
1
|
+
{
|
2
|
+
"name": "sandboxbox",
|
3
|
+
"version": "2.0.6",
|
4
|
+
"description": "Portable container runner with Podman - Claude Code & Playwright support. Works on Windows, macOS, and Linux.",
|
5
|
+
"type": "module",
|
6
|
+
"main": "cli.js",
|
7
|
+
"bin": {
|
8
|
+
"sandboxbox": "./cli.js"
|
9
|
+
},
|
10
|
+
"scripts": {
|
11
|
+
"start": "node cli.js",
|
12
|
+
"postinstall": "node scripts/download-podman.js"
|
13
|
+
},
|
14
|
+
"keywords": [
|
15
|
+
"containers",
|
16
|
+
"podman",
|
17
|
+
"playwright",
|
18
|
+
"claude-code",
|
19
|
+
"sandbox",
|
20
|
+
"isolation",
|
21
|
+
"cross-platform",
|
22
|
+
"windows",
|
23
|
+
"macos",
|
24
|
+
"linux",
|
25
|
+
"portable"
|
26
|
+
],
|
27
|
+
"author": "",
|
28
|
+
"license": "MIT",
|
29
|
+
"dependencies": {},
|
30
|
+
"repository": {
|
31
|
+
"type": "git",
|
32
|
+
"url": "https://github.com/AnEntrypoint/sandboxbox.git"
|
33
|
+
},
|
34
|
+
"homepage": "https://github.com/AnEntrypoint/sandboxbox#readme",
|
35
|
+
"bugs": {
|
36
|
+
"url": "https://github.com/AnEntrypoint/sandboxbox/issues"
|
37
|
+
}
|
38
|
+
}
|
@@ -1,148 +1,237 @@
|
|
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
|
-
|
16
|
-
|
17
|
-
const
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
const
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
get
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
}
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
}
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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, createReadStream, readdirSync, statSync } from 'fs';
|
9
|
+
import { get as httpsGet } from 'https';
|
10
|
+
import { get as httpGet } from 'http';
|
11
|
+
import { join, dirname, sep } from 'path';
|
12
|
+
import { fileURLToPath } from 'url';
|
13
|
+
import { execSync } from 'child_process';
|
14
|
+
import { createGunzip } from 'zlib';
|
15
|
+
import { pipeline } from 'stream/promises';
|
16
|
+
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
18
|
+
const __dirname = dirname(__filename);
|
19
|
+
const binDir = join(__dirname, '..', 'bin');
|
20
|
+
|
21
|
+
// Podman remote client versions
|
22
|
+
const PODMAN_VERSION = '4.9.3';
|
23
|
+
|
24
|
+
// Get architecture
|
25
|
+
const ARCH = process.arch === 'arm64' ? 'arm64' : 'amd64';
|
26
|
+
|
27
|
+
const DOWNLOADS = {
|
28
|
+
win32: {
|
29
|
+
url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-windows_amd64.zip`,
|
30
|
+
binary: 'podman.exe',
|
31
|
+
extract: 'unzip'
|
32
|
+
},
|
33
|
+
darwin: {
|
34
|
+
url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-release-darwin_${ARCH}.tar.gz`,
|
35
|
+
binary: 'podman',
|
36
|
+
extract: 'tar'
|
37
|
+
},
|
38
|
+
linux: {
|
39
|
+
url: `https://github.com/containers/podman/releases/download/v${PODMAN_VERSION}/podman-remote-static-linux_${ARCH}.tar.gz`,
|
40
|
+
binary: `podman-remote-static-linux_${ARCH}`,
|
41
|
+
extract: 'tar'
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
45
|
+
function download(url, dest) {
|
46
|
+
return new Promise((resolve, reject) => {
|
47
|
+
const get = url.startsWith('https') ? httpsGet : httpGet;
|
48
|
+
|
49
|
+
get(url, (response) => {
|
50
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
51
|
+
return download(response.headers.location, dest).then(resolve).catch(reject);
|
52
|
+
}
|
53
|
+
|
54
|
+
if (response.statusCode !== 200) {
|
55
|
+
reject(new Error(`Download failed: ${response.statusCode}`));
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
const file = createWriteStream(dest);
|
60
|
+
response.pipe(file);
|
61
|
+
|
62
|
+
file.on('finish', () => {
|
63
|
+
file.close();
|
64
|
+
resolve();
|
65
|
+
});
|
66
|
+
|
67
|
+
file.on('error', reject);
|
68
|
+
}).on('error', reject);
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
// Simple ZIP extraction using built-in Node.js modules
|
73
|
+
// Basic implementation that handles standard ZIP files without compression complications
|
74
|
+
async function extractZip(zipPath, extractTo) {
|
75
|
+
return new Promise((resolve, reject) => {
|
76
|
+
try {
|
77
|
+
// For Windows, we'll use PowerShell's built-in ZIP extraction capability
|
78
|
+
// This is more reliable than expecting external tools
|
79
|
+
const psCommand = `Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory('${zipPath.replace(/'/g, "''")}', '${extractTo.replace(/'/g, "''")}')`;
|
80
|
+
|
81
|
+
execSync(`powershell -Command "${psCommand}"`, {
|
82
|
+
stdio: 'pipe',
|
83
|
+
cwd: __dirname,
|
84
|
+
shell: true
|
85
|
+
});
|
86
|
+
|
87
|
+
resolve();
|
88
|
+
} catch (error) {
|
89
|
+
reject(error);
|
90
|
+
}
|
91
|
+
});
|
92
|
+
}
|
93
|
+
|
94
|
+
// Extract tar.gz files using Node.js built-in modules
|
95
|
+
async function extractTarGz(tarPath, extractTo, stripComponents = 0) {
|
96
|
+
return new Promise(async (resolve, reject) => {
|
97
|
+
try {
|
98
|
+
// First, decompress the .gz file
|
99
|
+
const tarWithoutGz = tarPath.replace('.gz', '');
|
100
|
+
const readStream = createReadStream(tarPath);
|
101
|
+
const writeStream = createWriteStream(tarWithoutGz);
|
102
|
+
const gunzip = createGunzip();
|
103
|
+
|
104
|
+
await pipeline(readStream, gunzip, writeStream);
|
105
|
+
|
106
|
+
// For tar extraction, we need to use system tar since implementing a full tar parser is complex
|
107
|
+
// But we'll ensure it works across platforms by providing fallbacks
|
108
|
+
try {
|
109
|
+
execSync(`tar -xf "${tarWithoutGz}" -C "${extractTo}"${stripComponents ? ` --strip-components=${stripComponents}` : ''}`, {
|
110
|
+
stdio: 'pipe',
|
111
|
+
shell: process.platform === 'win32'
|
112
|
+
});
|
113
|
+
} catch (tarError) {
|
114
|
+
// If system tar fails, try with specific flags for different platforms
|
115
|
+
if (process.platform === 'win32') {
|
116
|
+
// On Windows, try with different tar implementations
|
117
|
+
try {
|
118
|
+
execSync(`bsdtar -xf "${tarWithoutGz}" -C "${extractTo}"${stripComponents ? ` --strip-components=${stripComponents}` : ''}`, {
|
119
|
+
stdio: 'pipe',
|
120
|
+
shell: true
|
121
|
+
});
|
122
|
+
} catch (bsdtarError) {
|
123
|
+
throw new Error(`Failed to extract tar archive. Please install tar or bsdtar: ${tarError.message}`);
|
124
|
+
}
|
125
|
+
} else {
|
126
|
+
throw tarError;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
// Clean up the intermediate .tar file
|
131
|
+
unlinkSync(tarWithoutGz);
|
132
|
+
|
133
|
+
resolve();
|
134
|
+
} catch (error) {
|
135
|
+
reject(error);
|
136
|
+
}
|
137
|
+
});
|
138
|
+
}
|
139
|
+
|
140
|
+
async function main() {
|
141
|
+
const platform = process.platform;
|
142
|
+
|
143
|
+
console.log(`\nš¦ Setting up Podman portable binaries for ${platform}...`);
|
144
|
+
|
145
|
+
if (!DOWNLOADS[platform]) {
|
146
|
+
console.log(`ā ļø Platform ${platform} not supported for auto-download`);
|
147
|
+
console.log(` Skipping auto-download (will use system Podman if available)`);
|
148
|
+
return;
|
149
|
+
}
|
150
|
+
|
151
|
+
// Create bin directory
|
152
|
+
if (!existsSync(binDir)) {
|
153
|
+
mkdirSync(binDir, { recursive: true });
|
154
|
+
}
|
155
|
+
|
156
|
+
const { url, binary, extract } = DOWNLOADS[platform];
|
157
|
+
const binaryPath = join(binDir, binary);
|
158
|
+
|
159
|
+
// Check if already downloaded
|
160
|
+
if (existsSync(binaryPath)) {
|
161
|
+
console.log(`ā
Podman already installed at ${binaryPath}`);
|
162
|
+
return;
|
163
|
+
}
|
164
|
+
|
165
|
+
console.log(`š„ Downloading Podman remote v${PODMAN_VERSION}...`);
|
166
|
+
|
167
|
+
const archiveName = url.split('/').pop();
|
168
|
+
const archivePath = join(binDir, archiveName);
|
169
|
+
|
170
|
+
try {
|
171
|
+
// Clean up any previous failed extractions using fs operations
|
172
|
+
const extractedDir = join(binDir, 'podman-4.9.3');
|
173
|
+
if (existsSync(extractedDir)) {
|
174
|
+
const fs = await import('fs/promises');
|
175
|
+
await fs.rm(extractedDir, { recursive: true, force: true });
|
176
|
+
}
|
177
|
+
|
178
|
+
// Download archive
|
179
|
+
console.log(` Downloading from GitHub releases...`);
|
180
|
+
await download(url, archivePath);
|
181
|
+
console.log(`ā
Downloaded successfully`);
|
182
|
+
|
183
|
+
// Extract based on platform
|
184
|
+
console.log(`š¦ Extracting...`);
|
185
|
+
if (extract === 'tar') {
|
186
|
+
await extractTarGz(archivePath, binDir, 1);
|
187
|
+
} else if (extract === 'unzip') {
|
188
|
+
await extractZip(archivePath, binDir);
|
189
|
+
|
190
|
+
// Windows-specific: move podman.exe from nested directory to bin/
|
191
|
+
if (platform === 'win32') {
|
192
|
+
const extractedDir = join(binDir, `podman-4.9.3`);
|
193
|
+
const extractedPodman = join(extractedDir, 'usr', 'bin', 'podman.exe');
|
194
|
+
const targetPodman = join(binDir, binary);
|
195
|
+
|
196
|
+
if (existsSync(extractedPodman)) {
|
197
|
+
// Use fs operations instead of shell commands for cross-platform compatibility
|
198
|
+
const fs = await import('fs/promises');
|
199
|
+
await fs.copyFile(extractedPodman, targetPodman);
|
200
|
+
|
201
|
+
// Clean up the extracted directory
|
202
|
+
await fs.rm(extractedDir, { recursive: true, force: true });
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
// Make executable on Unix
|
208
|
+
if (platform !== 'win32' && existsSync(binaryPath)) {
|
209
|
+
chmodSync(binaryPath, 0o755);
|
210
|
+
}
|
211
|
+
|
212
|
+
console.log(`ā
Podman remote installed successfully!`);
|
213
|
+
console.log(` Binary: ${binaryPath}\n`);
|
214
|
+
|
215
|
+
// Clean up archive
|
216
|
+
if (existsSync(archivePath)) {
|
217
|
+
unlinkSync(archivePath);
|
218
|
+
}
|
219
|
+
|
220
|
+
} catch (error) {
|
221
|
+
console.error(`ā ļø Auto-download failed: ${error.message}`);
|
222
|
+
console.log(`\nš” No problem! You can install Podman manually:`);
|
223
|
+
if (platform === 'win32') {
|
224
|
+
console.log(` winget install RedHat.Podman`);
|
225
|
+
} else if (platform === 'darwin') {
|
226
|
+
console.log(` brew install podman && podman machine init && podman machine start`);
|
227
|
+
} else {
|
228
|
+
console.log(` sudo apt-get install podman # Ubuntu/Debian`);
|
229
|
+
}
|
230
|
+
console.log(`\n Or it will use system Podman if installed.\n`);
|
231
|
+
// Don't fail the npm install
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
main().catch(() => {
|
236
|
+
// Silently fail - will use system Podman
|
237
|
+
});
|
package/test/Dockerfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Test Dockerfile for SandboxBox
|
2
|
+
FROM node:18-alpine
|
3
|
+
|
4
|
+
WORKDIR /app
|
5
|
+
|
6
|
+
# Copy package files
|
7
|
+
COPY package*.json ./
|
8
|
+
|
9
|
+
# Install dependencies (skip postinstall scripts for testing)
|
10
|
+
RUN npm install --ignore-scripts
|
11
|
+
|
12
|
+
# Copy source code
|
13
|
+
COPY . .
|
14
|
+
|
15
|
+
# Expose port
|
16
|
+
EXPOSE 3000
|
17
|
+
|
18
|
+
# Default command
|
19
|
+
CMD ["npm", "start"]
|
package/test/index.js
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
console.log('Hello from SandboxBox test container!');
|
2
|
+
console.log('Node.js version:', process.version);
|
3
|
+
console.log('Platform:', process.platform);
|
4
|
+
console.log('Current working directory:', process.cwd());
|
5
|
+
|
6
|
+
const http = require('http');
|
7
|
+
|
8
|
+
const server = http.createServer((req, res) => {
|
9
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
10
|
+
res.end('Hello from SandboxBox container!\n');
|
11
|
+
});
|
12
|
+
|
13
|
+
const PORT = process.env.PORT || 3000;
|
14
|
+
server.listen(PORT, () => {
|
15
|
+
console.log(`Server running on port ${PORT}`);
|
16
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{
|
2
|
+
"name": "test-project",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Test project for SandboxBox",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"start": "node index.js",
|
8
|
+
"test": "echo 'Running tests...' && exit 0"
|
9
|
+
},
|
10
|
+
"keywords": ["test", "sandbox"],
|
11
|
+
"author": "",
|
12
|
+
"license": "ISC"
|
13
|
+
}
|
package/bin/.gitkeep
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# Downloaded Podman binaries will go here
|