sandboxbox 2.0.4 ā 2.0.7
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/CLAUDE.md +138 -0
- package/Dockerfile +94 -94
- package/Dockerfile.local-workspace +56 -0
- package/README.md +242 -242
- package/cli.js +269 -278
- package/launch-sandboxbox.bat +75 -0
- package/launch-sandboxbox.sh +81 -0
- 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/test-cross-platform.sh +78 -0
- package/test-merge-workflow.sh +58 -0
- package/test-sandbox-workflow.sh +45 -0
- package/utils/claude-workspace.js +77 -0
- package/utils/colors.js +15 -0
- package/utils/podman.js +116 -0
- package/bin/.gitkeep +0 -1
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# SandboxBox Launcher with Claude Auth Transfer
|
4
|
+
# Usage: ./launch-sandboxbox.sh <repository-path> [command]
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
REPO_PATH="${1:-.}"
|
9
|
+
COMMAND="${2:-claude}"
|
10
|
+
|
11
|
+
echo "š Launching SandboxBox with Claude Code..."
|
12
|
+
echo "š Repository: $REPO_PATH"
|
13
|
+
echo "š§ Command: $COMMAND"
|
14
|
+
|
15
|
+
# Get absolute path of repository
|
16
|
+
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
|
17
|
+
REPO_ABS_PATH=$(realpath "$REPO_PATH")
|
18
|
+
else
|
19
|
+
REPO_ABS_PATH=$(realpath "$REPO_PATH")
|
20
|
+
fi
|
21
|
+
|
22
|
+
echo "š Absolute path: $REPO_ABS_PATH"
|
23
|
+
|
24
|
+
# Check if it's a git repository
|
25
|
+
if [ ! -d "$REPO_ABS_PATH/.git" ]; then
|
26
|
+
echo "ā Error: $REPO_ABS_PATH is not a git repository"
|
27
|
+
echo "Please ensure the directory contains a .git folder"
|
28
|
+
exit 1
|
29
|
+
fi
|
30
|
+
|
31
|
+
# Collect all Anthropic and Claude environment variables
|
32
|
+
ENV_ARGS=""
|
33
|
+
for var in $(env | grep -E "^(ANTHROPIC|CLAUDE)" | cut -d= -f1); do
|
34
|
+
ENV_ARGS="$ENV_ARGS -e $var=${!var}"
|
35
|
+
done
|
36
|
+
|
37
|
+
# Add common development environment variables
|
38
|
+
for var in HOME USER SHELL PWD OLDPWD; do
|
39
|
+
if [ -n "${!var}" ]; then
|
40
|
+
ENV_ARGS="$ENV_ARGS -e $var=${!var}"
|
41
|
+
fi
|
42
|
+
done
|
43
|
+
|
44
|
+
echo "š Environment variables transferred: $(echo $ENV_ARGS | wc -w)"
|
45
|
+
|
46
|
+
# Find SandboxBox podman binary
|
47
|
+
PODMAN_PATH=""
|
48
|
+
if [ -f "bin/podman.exe" ]; then
|
49
|
+
PODMAN_PATH="bin/podman.exe"
|
50
|
+
elif [ -f "bin/podman" ]; then
|
51
|
+
PODMAN_PATH="bin/podman"
|
52
|
+
else
|
53
|
+
# Try to find system podman
|
54
|
+
PODMAN_PATH=$(which podman 2>/dev/null || echo "")
|
55
|
+
fi
|
56
|
+
|
57
|
+
if [ -z "$PODMAN_PATH" ]; then
|
58
|
+
echo "ā Error: Podman binary not found"
|
59
|
+
echo "Please ensure SandboxBox is properly installed"
|
60
|
+
exit 1
|
61
|
+
fi
|
62
|
+
|
63
|
+
echo "š³ Using Podman: $PODMAN_PATH"
|
64
|
+
|
65
|
+
# Build the command with complete Claude session transfer
|
66
|
+
PODMAN_CMD="$PODMAN_PATH run --rm -it"
|
67
|
+
PODMAN_CMD="$PODMAN_CMD -v \"$REPO_ABS_PATH:/project\""
|
68
|
+
PODMAN_CMD="$PODMAN_CMD -v \"$HOME/.ssh:/root/.ssh:ro\""
|
69
|
+
PODMAN_CMD="$PODMAN_CMD -v \"$HOME/.gitconfig:/root/.gitconfig:ro\""
|
70
|
+
PODMAN_CMD="$PODMAN_CMD -v \"$HOME/.claude:/root/.claude\""
|
71
|
+
PODMAN_CMD="$PODMAN_CMD $ENV_ARGS"
|
72
|
+
PODMAN_CMD="$PODMAN_CMD --env REPO_PATH=/project"
|
73
|
+
PODMAN_CMD="$PODMAN_CMD --env HOME=/root"
|
74
|
+
PODMAN_CMD="$PODMAN_CMD sandboxbox-auth:latest"
|
75
|
+
PODMAN_CMD="$PODMAN_CMD $COMMAND"
|
76
|
+
|
77
|
+
echo "šÆ Running: $PODMAN_CMD"
|
78
|
+
echo ""
|
79
|
+
|
80
|
+
# Execute the command
|
81
|
+
eval $PODMAN_CMD
|
@@ -0,0 +1 @@
|
|
1
|
+
{"name": "test", "scripts": {"test": "echo Hello"}}
|
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.7",
|
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
|
+
}
|