sandboxbox 2.2.8 ā 2.3.0
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/CLAUDE.md +20 -34
- package/cli.js +1 -1
- package/npx-test-sandboxbox/package.json +12 -0
- package/package.json +1 -1
- package/utils/commands/claude.js +90 -0
- package/utils/commands/container.js +99 -0
- package/utils/commands/index.js +26 -0
- package/utils/podman.js +36 -0
- package/utils/commands.js +0 -193
package/CLAUDE.md
CHANGED
@@ -5,10 +5,11 @@ Portable containerized environments using Podman with automatic WSL management a
|
|
5
5
|
|
6
6
|
## Core Components
|
7
7
|
|
8
|
-
### CLI
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
8
|
+
### CLI Architecture
|
9
|
+
- **Entry**: cli.js (main entry point)
|
10
|
+
- **Commands**: utils/commands/container.js (build, run, shell), utils/commands/claude.js (claude), utils/commands/index.js (version + exports)
|
11
|
+
- **Pattern**: Modular command structure, each file <100 lines
|
12
|
+
- **Shell execution**: `shell: process.platform === 'win32'` for all execSync calls
|
12
13
|
|
13
14
|
### Podman Downloader (scripts/download-podman.js)
|
14
15
|
- Cross-platform binary downloads from GitHub releases
|
@@ -18,7 +19,6 @@ Portable containerized environments using Podman with automatic WSL management a
|
|
18
19
|
- Auto-detects existing installations
|
19
20
|
- Auto-triggers on first use if Podman not found
|
20
21
|
- Verifies binary existence post-download
|
21
|
-
- Auto-initializes Podman machine on Windows after download
|
22
22
|
|
23
23
|
### Container Images
|
24
24
|
- **sandboxbox:latest**: Full development environment
|
@@ -55,36 +55,22 @@ if (process.platform === 'win32') {
|
|
55
55
|
}
|
56
56
|
```
|
57
57
|
|
58
|
-
###
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
```
|
73
|
-
|
74
|
-
### Rootless vs Rootful Mode
|
75
|
-
- **Rootless (default)**: Runs without administrator privileges, portable across systems
|
76
|
-
- **Configuration**: All machines initialized with `--rootful=false` flag
|
77
|
-
- **Benefits**: No elevated permissions required, better security, true portability
|
78
|
-
|
79
|
-
### Portable Podman Architecture
|
80
|
-
- **Direct Podman Execution**: Uses bundled Podman binary without complex machine management
|
81
|
-
- **Rootless Operation**: Always runs in rootless mode for portability (--rootful=false)
|
82
|
-
- **Self-Contained**: All dependencies included in the package
|
83
|
-
- **Simple Configuration**: Minimal backend setup only when needed
|
84
|
-
- **Auto-Download**: Downloads platform-specific binaries automatically
|
58
|
+
### Automatic Backend Setup
|
59
|
+
**Implementation**: `ensureBackend(podmanPath)` in utils/podman.js
|
60
|
+
- Linux: Returns immediately (native execution, no backend needed)
|
61
|
+
- Windows/macOS: Checks `podman info` connectivity
|
62
|
+
- Auto-initializes on first run: `podman machine init --rootful=false` (Windows) or `podman machine init` (macOS)
|
63
|
+
- Auto-starts machine: `podman machine start`
|
64
|
+
- Shows progress: "Initializing Podman backend (first run, takes 2-3 minutes)"
|
65
|
+
- Detects existing machines via `podman machine list --format json`
|
66
|
+
- Called before all container operations (build, run, shell, claude)
|
67
|
+
|
68
|
+
### Portable Architecture
|
69
|
+
- **Binary Auto-Download**: Platform-specific Podman binary (amd64/arm64) from GitHub releases
|
70
|
+
- **Rootless Mode**: Windows uses `--rootful=false` for portability
|
71
|
+
- **Backend Automation**: Fully automatic backend initialization on first container operation
|
85
72
|
- **NPX Compatible**: Works via npx without global installation
|
86
|
-
- **
|
87
|
-
- **Cross-Platform**: Windows, macOS, Linux - all with amd64/arm64 support
|
73
|
+
- **Cross-Platform**: Windows (amd64/arm64), macOS (amd64/arm64), Linux (amd64/arm64)
|
88
74
|
|
89
75
|
## Isolation Architecture
|
90
76
|
|
package/cli.js
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
import { resolve } from 'path';
|
9
9
|
import { color } from './utils/colors.js';
|
10
10
|
import { showBanner, showHelp } from './utils/ui.js';
|
11
|
-
import { buildCommand, runCommand, shellCommand, claudeCommand, versionCommand } from './utils/commands.js';
|
11
|
+
import { buildCommand, runCommand, shellCommand, claudeCommand, versionCommand } from './utils/commands/index.js';
|
12
12
|
|
13
13
|
async function main() {
|
14
14
|
const args = process.argv.slice(2);
|
package/package.json
CHANGED
@@ -0,0 +1,90 @@
|
|
1
|
+
import { existsSync, writeFileSync } from 'fs';
|
2
|
+
import { execSync } from 'child_process';
|
3
|
+
import { resolve, dirname } from 'path';
|
4
|
+
import { fileURLToPath } from 'url';
|
5
|
+
import { color } from '../colors.js';
|
6
|
+
import { checkPodman, ensureBackend, getPodmanPath } from '../podman.js';
|
7
|
+
import { buildClaudeContainerCommand, createClaudeDockerfile } from '../claude-workspace.js';
|
8
|
+
import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
|
9
|
+
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
11
|
+
const __dirname = dirname(__filename);
|
12
|
+
|
13
|
+
export function claudeCommand(projectDir, command = 'claude') {
|
14
|
+
if (!existsSync(projectDir)) {
|
15
|
+
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
16
|
+
return false;
|
17
|
+
}
|
18
|
+
|
19
|
+
if (!existsSync(resolve(projectDir, '.git'))) {
|
20
|
+
console.log(color('red', `ā Not a git repository: ${projectDir}`));
|
21
|
+
console.log(color('yellow', 'Please run this command in a git repository directory'));
|
22
|
+
return false;
|
23
|
+
}
|
24
|
+
|
25
|
+
const podmanPath = getPodmanPath();
|
26
|
+
try {
|
27
|
+
execSync(`"${podmanPath}" image inspect sandboxbox-local:latest`, {
|
28
|
+
stdio: 'pipe',
|
29
|
+
shell: process.platform === 'win32'
|
30
|
+
});
|
31
|
+
} catch {
|
32
|
+
console.log(color('yellow', 'š¦ Building Claude Code container...'));
|
33
|
+
if (!buildClaudeContainer()) {
|
34
|
+
return false;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
console.log(color('blue', 'š Starting Claude Code in isolated environment...'));
|
39
|
+
console.log(color('yellow', `Project: ${projectDir}`));
|
40
|
+
console.log(color('yellow', `Command: ${command}`));
|
41
|
+
console.log(color('cyan', 'š¦ Note: Changes will be isolated and will NOT affect the original repository\n'));
|
42
|
+
|
43
|
+
const buildPodman = checkPodman();
|
44
|
+
if (!buildPodman) return false;
|
45
|
+
if (!ensureBackend(buildPodman)) return false;
|
46
|
+
|
47
|
+
try {
|
48
|
+
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
49
|
+
setupCleanupHandlers(cleanup);
|
50
|
+
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
51
|
+
const containerCommand = buildClaudeContainerCommand(tempProjectDir, buildPodman, command, mounts);
|
52
|
+
|
53
|
+
execSync(containerCommand, {
|
54
|
+
stdio: 'inherit',
|
55
|
+
shell: process.platform === 'win32'
|
56
|
+
});
|
57
|
+
|
58
|
+
cleanup();
|
59
|
+
console.log(color('green', '\nā
Claude Code session completed! (Isolated - no host changes)'));
|
60
|
+
return true;
|
61
|
+
} catch (error) {
|
62
|
+
console.log(color('red', `\nā Claude Code failed: ${error.message}`));
|
63
|
+
return false;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
function buildClaudeContainer() {
|
68
|
+
const dockerfilePath = resolve(__dirname, '..', '..', 'Dockerfile.claude');
|
69
|
+
const dockerfileContent = createClaudeDockerfile();
|
70
|
+
|
71
|
+
writeFileSync(dockerfilePath, dockerfileContent);
|
72
|
+
console.log(color('blue', 'šļø Building Claude Code container...'));
|
73
|
+
|
74
|
+
const podmanPath = checkPodman();
|
75
|
+
if (!podmanPath) return false;
|
76
|
+
if (!ensureBackend(podmanPath)) return false;
|
77
|
+
|
78
|
+
try {
|
79
|
+
execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox-local:latest .`, {
|
80
|
+
stdio: 'inherit',
|
81
|
+
cwd: resolve(__dirname, '..', '..'),
|
82
|
+
shell: process.platform === 'win32'
|
83
|
+
});
|
84
|
+
console.log(color('green', '\nā
Claude Code container built successfully!'));
|
85
|
+
return true;
|
86
|
+
} catch (error) {
|
87
|
+
console.log(color('red', `\nā Build failed: ${error.message}`));
|
88
|
+
return false;
|
89
|
+
}
|
90
|
+
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import { existsSync } from 'fs';
|
2
|
+
import { execSync } from 'child_process';
|
3
|
+
import { dirname } from 'path';
|
4
|
+
import { color } from '../colors.js';
|
5
|
+
import { checkPodman, ensureBackend } from '../podman.js';
|
6
|
+
import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from '../isolation.js';
|
7
|
+
|
8
|
+
export function buildCommand(dockerfilePath) {
|
9
|
+
if (!existsSync(dockerfilePath)) {
|
10
|
+
console.log(color('red', `ā Dockerfile not found: ${dockerfilePath}`));
|
11
|
+
return false;
|
12
|
+
}
|
13
|
+
|
14
|
+
console.log(color('blue', 'šļø Building container...'));
|
15
|
+
console.log(color('yellow', `Dockerfile: ${dockerfilePath}\n`));
|
16
|
+
|
17
|
+
const podmanPath = checkPodman();
|
18
|
+
if (!podmanPath) return false;
|
19
|
+
if (!ensureBackend(podmanPath)) return false;
|
20
|
+
|
21
|
+
try {
|
22
|
+
execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
|
23
|
+
stdio: 'inherit',
|
24
|
+
cwd: dirname(dockerfilePath),
|
25
|
+
shell: process.platform === 'win32'
|
26
|
+
});
|
27
|
+
console.log(color('green', '\nā
Container built successfully!'));
|
28
|
+
return true;
|
29
|
+
} catch (error) {
|
30
|
+
console.log(color('red', `\nā Build failed: ${error.message}`));
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
export function runCommand(projectDir, cmd = 'bash') {
|
36
|
+
if (!existsSync(projectDir)) {
|
37
|
+
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
|
41
|
+
console.log(color('blue', 'š Running project in isolated container...'));
|
42
|
+
console.log(color('yellow', `Project: ${projectDir}`));
|
43
|
+
console.log(color('yellow', `Command: ${cmd}\n`));
|
44
|
+
console.log(color('cyan', 'š¦ Note: Changes will NOT affect host files (isolated environment)'));
|
45
|
+
|
46
|
+
const podmanPath = checkPodman();
|
47
|
+
if (!podmanPath) return false;
|
48
|
+
if (!ensureBackend(podmanPath)) return false;
|
49
|
+
|
50
|
+
try {
|
51
|
+
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
52
|
+
setupCleanupHandlers(cleanup);
|
53
|
+
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
54
|
+
|
55
|
+
execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest ${cmd}`, {
|
56
|
+
stdio: 'inherit',
|
57
|
+
shell: process.platform === 'win32'
|
58
|
+
});
|
59
|
+
|
60
|
+
cleanup();
|
61
|
+
console.log(color('green', '\nā
Container execution completed! (Isolated - no host changes)'));
|
62
|
+
return true;
|
63
|
+
} catch (error) {
|
64
|
+
console.log(color('red', `\nā Run failed: ${error.message}`));
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
export function shellCommand(projectDir) {
|
70
|
+
if (!existsSync(projectDir)) {
|
71
|
+
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
72
|
+
return false;
|
73
|
+
}
|
74
|
+
|
75
|
+
console.log(color('blue', 'š Starting interactive shell in isolated container...'));
|
76
|
+
console.log(color('yellow', `Project: ${projectDir}\n`));
|
77
|
+
console.log(color('cyan', 'š¦ Note: Changes will NOT affect host files (isolated environment)'));
|
78
|
+
|
79
|
+
const podmanPath = checkPodman();
|
80
|
+
if (!podmanPath) return false;
|
81
|
+
if (!ensureBackend(podmanPath)) return false;
|
82
|
+
|
83
|
+
try {
|
84
|
+
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
85
|
+
setupCleanupHandlers(cleanup);
|
86
|
+
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
87
|
+
|
88
|
+
execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest /bin/bash`, {
|
89
|
+
stdio: 'inherit',
|
90
|
+
shell: process.platform === 'win32'
|
91
|
+
});
|
92
|
+
|
93
|
+
cleanup();
|
94
|
+
return true;
|
95
|
+
} catch (error) {
|
96
|
+
console.log(color('red', `\nā Shell failed: ${error.message}`));
|
97
|
+
return false;
|
98
|
+
}
|
99
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { readFileSync } from 'fs';
|
2
|
+
import { resolve, dirname } from 'path';
|
3
|
+
import { fileURLToPath } from 'url';
|
4
|
+
import { color } from '../colors.js';
|
5
|
+
import { checkPodman } from '../podman.js';
|
6
|
+
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
8
|
+
const __dirname = dirname(__filename);
|
9
|
+
|
10
|
+
export { buildCommand, runCommand, shellCommand } from './container.js';
|
11
|
+
export { claudeCommand } from './claude.js';
|
12
|
+
|
13
|
+
export function versionCommand() {
|
14
|
+
try {
|
15
|
+
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '..', '..', 'package.json'), 'utf-8'));
|
16
|
+
console.log(color('green', `SandboxBox v${packageJson.version}`));
|
17
|
+
console.log(color('cyan', 'Portable containers with Claude Code integration'));
|
18
|
+
if (checkPodman()) {
|
19
|
+
console.log('');
|
20
|
+
}
|
21
|
+
return true;
|
22
|
+
} catch (error) {
|
23
|
+
console.log(color('red', 'ā Could not read version'));
|
24
|
+
return false;
|
25
|
+
}
|
26
|
+
}
|
package/utils/podman.js
CHANGED
@@ -26,6 +26,42 @@ export function getPodmanPath() {
|
|
26
26
|
return 'podman';
|
27
27
|
}
|
28
28
|
|
29
|
+
export function ensureBackend(podmanPath) {
|
30
|
+
if (process.platform === 'linux') return true;
|
31
|
+
|
32
|
+
const execOptions = { encoding: 'utf-8', stdio: 'pipe', shell: true };
|
33
|
+
|
34
|
+
try {
|
35
|
+
execSync(`"${podmanPath}" info`, execOptions);
|
36
|
+
return true;
|
37
|
+
} catch (infoError) {
|
38
|
+
if (!infoError.message.includes('Cannot connect to Podman')) throw infoError;
|
39
|
+
|
40
|
+
console.log(color('yellow', '\nš§ Initializing Podman backend (first run, takes 2-3 minutes)...'));
|
41
|
+
|
42
|
+
try {
|
43
|
+
const machineListOutput = execSync(`"${podmanPath}" machine list --format json`, execOptions);
|
44
|
+
const machines = JSON.parse(machineListOutput || '[]');
|
45
|
+
|
46
|
+
if (machines.length === 0) {
|
47
|
+
console.log(color('cyan', ' Creating Podman machine...'));
|
48
|
+
const initCmd = process.platform === 'win32'
|
49
|
+
? `"${podmanPath}" machine init --rootful=false`
|
50
|
+
: `"${podmanPath}" machine init`;
|
51
|
+
execSync(initCmd, { stdio: 'inherit', shell: true });
|
52
|
+
}
|
53
|
+
|
54
|
+
console.log(color('cyan', ' Starting Podman machine...'));
|
55
|
+
execSync(`"${podmanPath}" machine start`, { stdio: 'inherit', shell: true });
|
56
|
+
console.log(color('green', 'ā
Podman backend ready\n'));
|
57
|
+
return true;
|
58
|
+
} catch (backendError) {
|
59
|
+
console.log(color('red', `\nā Backend setup failed: ${backendError.message}`));
|
60
|
+
return false;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
29
65
|
export function checkPodman() {
|
30
66
|
const podmanPath = getPodmanPath();
|
31
67
|
const isBundled = podmanPath.includes('bin');
|
package/utils/commands.js
DELETED
@@ -1,193 +0,0 @@
|
|
1
|
-
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
2
|
-
import { execSync } from 'child_process';
|
3
|
-
import { resolve, dirname } from 'path';
|
4
|
-
import { fileURLToPath } from 'url';
|
5
|
-
import { color } from './colors.js';
|
6
|
-
import { checkPodman, getPodmanPath } from './podman.js';
|
7
|
-
import { buildClaudeContainerCommand, createClaudeDockerfile } from './claude-workspace.js';
|
8
|
-
import { createIsolatedEnvironment, setupCleanupHandlers, buildContainerMounts } from './isolation.js';
|
9
|
-
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
11
|
-
const __dirname = dirname(__filename);
|
12
|
-
|
13
|
-
export function buildCommand(dockerfilePath) {
|
14
|
-
if (!existsSync(dockerfilePath)) {
|
15
|
-
console.log(color('red', `ā Dockerfile not found: ${dockerfilePath}`));
|
16
|
-
return false;
|
17
|
-
}
|
18
|
-
|
19
|
-
console.log(color('blue', 'šļø Building container...'));
|
20
|
-
console.log(color('yellow', `Dockerfile: ${dockerfilePath}\n`));
|
21
|
-
|
22
|
-
const podmanPath = checkPodman();
|
23
|
-
if (!podmanPath) return false;
|
24
|
-
|
25
|
-
try {
|
26
|
-
execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox:latest .`, {
|
27
|
-
stdio: 'inherit',
|
28
|
-
cwd: dirname(dockerfilePath),
|
29
|
-
shell: process.platform === 'win32'
|
30
|
-
});
|
31
|
-
console.log(color('green', '\nā
Container built successfully!'));
|
32
|
-
return true;
|
33
|
-
} catch (error) {
|
34
|
-
console.log(color('red', `\nā Build failed: ${error.message}`));
|
35
|
-
return false;
|
36
|
-
}
|
37
|
-
}
|
38
|
-
|
39
|
-
export function runCommand(projectDir, cmd = 'bash') {
|
40
|
-
if (!existsSync(projectDir)) {
|
41
|
-
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
42
|
-
return false;
|
43
|
-
}
|
44
|
-
|
45
|
-
console.log(color('blue', 'š Running project in isolated container...'));
|
46
|
-
console.log(color('yellow', `Project: ${projectDir}`));
|
47
|
-
console.log(color('yellow', `Command: ${cmd}\n`));
|
48
|
-
console.log(color('cyan', 'š¦ Note: Changes will NOT affect host files (isolated environment)'));
|
49
|
-
|
50
|
-
const podmanPath = checkPodman();
|
51
|
-
if (!podmanPath) return false;
|
52
|
-
|
53
|
-
try {
|
54
|
-
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
55
|
-
setupCleanupHandlers(cleanup);
|
56
|
-
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
57
|
-
|
58
|
-
execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest ${cmd}`, {
|
59
|
-
stdio: 'inherit',
|
60
|
-
shell: process.platform === 'win32'
|
61
|
-
});
|
62
|
-
|
63
|
-
cleanup();
|
64
|
-
console.log(color('green', '\nā
Container execution completed! (Isolated - no host changes)'));
|
65
|
-
return true;
|
66
|
-
} catch (error) {
|
67
|
-
console.log(color('red', `\nā Run failed: ${error.message}`));
|
68
|
-
return false;
|
69
|
-
}
|
70
|
-
}
|
71
|
-
|
72
|
-
export function shellCommand(projectDir) {
|
73
|
-
if (!existsSync(projectDir)) {
|
74
|
-
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
75
|
-
return false;
|
76
|
-
}
|
77
|
-
|
78
|
-
console.log(color('blue', 'š Starting interactive shell in isolated container...'));
|
79
|
-
console.log(color('yellow', `Project: ${projectDir}\n`));
|
80
|
-
console.log(color('cyan', 'š¦ Note: Changes will NOT affect host files (isolated environment)'));
|
81
|
-
|
82
|
-
const podmanPath = checkPodman();
|
83
|
-
if (!podmanPath) return false;
|
84
|
-
|
85
|
-
try {
|
86
|
-
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
87
|
-
setupCleanupHandlers(cleanup);
|
88
|
-
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
89
|
-
|
90
|
-
execSync(`"${podmanPath}" run --rm -it ${mounts.join(' ')} -w /workspace sandboxbox:latest /bin/bash`, {
|
91
|
-
stdio: 'inherit',
|
92
|
-
shell: process.platform === 'win32'
|
93
|
-
});
|
94
|
-
|
95
|
-
cleanup();
|
96
|
-
return true;
|
97
|
-
} catch (error) {
|
98
|
-
console.log(color('red', `\nā Shell failed: ${error.message}`));
|
99
|
-
return false;
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
export function claudeCommand(projectDir, command = 'claude') {
|
104
|
-
if (!existsSync(projectDir)) {
|
105
|
-
console.log(color('red', `ā Project directory not found: ${projectDir}`));
|
106
|
-
return false;
|
107
|
-
}
|
108
|
-
|
109
|
-
if (!existsSync(resolve(projectDir, '.git'))) {
|
110
|
-
console.log(color('red', `ā Not a git repository: ${projectDir}`));
|
111
|
-
console.log(color('yellow', 'Please run this command in a git repository directory'));
|
112
|
-
return false;
|
113
|
-
}
|
114
|
-
|
115
|
-
const podmanPath = getPodmanPath();
|
116
|
-
try {
|
117
|
-
execSync(`"${podmanPath}" image inspect sandboxbox-local:latest`, {
|
118
|
-
stdio: 'pipe',
|
119
|
-
shell: process.platform === 'win32'
|
120
|
-
});
|
121
|
-
} catch {
|
122
|
-
console.log(color('yellow', 'š¦ Building Claude Code container...'));
|
123
|
-
if (!buildClaudeContainer()) {
|
124
|
-
return false;
|
125
|
-
}
|
126
|
-
}
|
127
|
-
|
128
|
-
console.log(color('blue', 'š Starting Claude Code in isolated environment...'));
|
129
|
-
console.log(color('yellow', `Project: ${projectDir}`));
|
130
|
-
console.log(color('yellow', `Command: ${command}`));
|
131
|
-
console.log(color('cyan', 'š¦ Note: Changes will be isolated and will NOT affect the original repository\n'));
|
132
|
-
|
133
|
-
const buildPodman = checkPodman();
|
134
|
-
if (!buildPodman) return false;
|
135
|
-
|
136
|
-
try {
|
137
|
-
const { tempProjectDir, cleanup } = createIsolatedEnvironment(projectDir);
|
138
|
-
setupCleanupHandlers(cleanup);
|
139
|
-
const mounts = buildContainerMounts(tempProjectDir, projectDir);
|
140
|
-
const containerCommand = buildClaudeContainerCommand(tempProjectDir, buildPodman, command, mounts);
|
141
|
-
|
142
|
-
execSync(containerCommand, {
|
143
|
-
stdio: 'inherit',
|
144
|
-
shell: process.platform === 'win32'
|
145
|
-
});
|
146
|
-
|
147
|
-
cleanup();
|
148
|
-
console.log(color('green', '\nā
Claude Code session completed! (Isolated - no host changes)'));
|
149
|
-
return true;
|
150
|
-
} catch (error) {
|
151
|
-
console.log(color('red', `\nā Claude Code failed: ${error.message}`));
|
152
|
-
return false;
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
export function versionCommand() {
|
157
|
-
try {
|
158
|
-
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8'));
|
159
|
-
console.log(color('green', `SandboxBox v${packageJson.version}`));
|
160
|
-
console.log(color('cyan', 'Portable containers with Claude Code integration'));
|
161
|
-
if (checkPodman()) {
|
162
|
-
console.log('');
|
163
|
-
}
|
164
|
-
return true;
|
165
|
-
} catch (error) {
|
166
|
-
console.log(color('red', 'ā Could not read version'));
|
167
|
-
return false;
|
168
|
-
}
|
169
|
-
}
|
170
|
-
|
171
|
-
function buildClaudeContainer() {
|
172
|
-
const dockerfilePath = resolve(__dirname, '../Dockerfile.claude');
|
173
|
-
const dockerfileContent = createClaudeDockerfile();
|
174
|
-
|
175
|
-
writeFileSync(dockerfilePath, dockerfileContent);
|
176
|
-
console.log(color('blue', 'šļø Building Claude Code container...'));
|
177
|
-
|
178
|
-
const podmanPath = checkPodman();
|
179
|
-
if (!podmanPath) return false;
|
180
|
-
|
181
|
-
try {
|
182
|
-
execSync(`"${podmanPath}" build -f "${dockerfilePath}" -t sandboxbox-local:latest .`, {
|
183
|
-
stdio: 'inherit',
|
184
|
-
cwd: dirname(__dirname),
|
185
|
-
shell: process.platform === 'win32'
|
186
|
-
});
|
187
|
-
console.log(color('green', '\nā
Claude Code container built successfully!'));
|
188
|
-
return true;
|
189
|
-
} catch (error) {
|
190
|
-
console.log(color('red', `\nā Build failed: ${error.message}`));
|
191
|
-
return false;
|
192
|
-
}
|
193
|
-
}
|