bx-mac 0.1.5
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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/bx.js +204 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dirk Holtwick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# bx
|
|
2
|
+
|
|
3
|
+
Launch VSCode, a terminal, Claude Code, or any command in a macOS sandbox. Your tools can only see the project you're working on — not your private files, SSH keys, or other repositories.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
AI-powered coding tools like Claude Code, Copilot, or Cline run with broad file system access. A misguided tool call or hallucinated path could accidentally read your SSH keys, credentials, tax documents, or private photos.
|
|
8
|
+
|
|
9
|
+
**bx** wraps any application in a macOS sandbox (`sandbox-exec`) that blocks access to everything except the project directory you explicitly specify. No containers, no VMs, no setup — just one command.
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
- Blocks access to `~/Documents`, `~/Desktop`, `~/Downloads`, and all other personal folders
|
|
14
|
+
- Blocks access to sibling projects — only the directory you specify is accessible
|
|
15
|
+
- Protects sensitive dotdirs like `~/.ssh`, `~/.gnupg`, `~/.docker`, `~/.cargo` by default
|
|
16
|
+
- Keeps VSCode, extensions, shell, Node.js, and other tooling fully functional
|
|
17
|
+
- Generates sandbox rules dynamically based on your actual `$HOME` contents
|
|
18
|
+
- Supports `.bxignore` to hide secrets like `.env` files within a project
|
|
19
|
+
- Supports `~/.bxallow` to grant access to shared utility directories
|
|
20
|
+
|
|
21
|
+
## What it doesn't do
|
|
22
|
+
|
|
23
|
+
- **No network restrictions** — API calls, git push/pull, npm install all work normally
|
|
24
|
+
- **No process isolation** — this is file-level sandboxing, not a container
|
|
25
|
+
- **No protection against root/sudo** — the sandbox applies to the user-level process
|
|
26
|
+
- **macOS only** — relies on `sandbox-exec` which is an Apple-specific technology
|
|
27
|
+
- **Not a security guarantee** — `sandbox-exec` is undocumented and may have limitations; treat this as a safety net, not a vault
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- macOS (tested on Sequoia / macOS 15)
|
|
32
|
+
- Node.js >= 22
|
|
33
|
+
- Visual Studio Code installed in `/Applications` (for `code` mode)
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Homebrew
|
|
39
|
+
brew install holtwick/tap/bx
|
|
40
|
+
|
|
41
|
+
# npm
|
|
42
|
+
npm install -g bx-mac
|
|
43
|
+
|
|
44
|
+
# From source
|
|
45
|
+
git clone https://github.com/holtwick/bx-mac.git
|
|
46
|
+
cd bx-mac
|
|
47
|
+
pnpm install && pnpm build
|
|
48
|
+
pnpm link -g
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Quick start
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Launch VSCode with sandbox protection
|
|
55
|
+
bx ~/work/my-project
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That's it. VSCode opens with full access to `~/work/my-project` and nothing else.
|
|
59
|
+
|
|
60
|
+
## Modes
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
bx [workdir] # VSCode (default)
|
|
64
|
+
bx code [workdir] # VSCode (explicit)
|
|
65
|
+
bx term [workdir] # sandboxed login shell ($SHELL -l)
|
|
66
|
+
bx claude [workdir] # Claude Code CLI
|
|
67
|
+
bx exec [workdir] -- command [args...] # arbitrary command
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If no directory is given, the current directory is used.
|
|
71
|
+
|
|
72
|
+
### Examples
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Work on a project in a sandboxed terminal
|
|
76
|
+
bx term ~/work/my-project
|
|
77
|
+
|
|
78
|
+
# Let Claude Code work on a project without access to anything else
|
|
79
|
+
bx claude ~/work/my-project
|
|
80
|
+
|
|
81
|
+
# Run a script in a sandbox
|
|
82
|
+
bx exec ~/work/my-project -- python train.py
|
|
83
|
+
|
|
84
|
+
# VSCode with verbose output
|
|
85
|
+
bx --verbose ~/work/my-project
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Options
|
|
89
|
+
|
|
90
|
+
| Option | Description |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `--verbose` | Print the generated sandbox profile to stderr |
|
|
93
|
+
| `--profile-sandbox` | Use an isolated VSCode profile (separate extensions and settings, `code` mode only) |
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
bx uses three optional config files, all with the same format: one entry per line, `#` for comments.
|
|
98
|
+
|
|
99
|
+
### `~/.bxallow`
|
|
100
|
+
|
|
101
|
+
Allow extra directories beyond the working directory. Paths are relative to `$HOME`.
|
|
102
|
+
|
|
103
|
+
```gitignore
|
|
104
|
+
# Shared shell scripts and utilities
|
|
105
|
+
work/bin
|
|
106
|
+
# Shared libraries used across projects
|
|
107
|
+
shared/libs
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `~/.bxignore`
|
|
111
|
+
|
|
112
|
+
Block additional dotdirs or files in your home. Paths are relative to `$HOME`.
|
|
113
|
+
|
|
114
|
+
```gitignore
|
|
115
|
+
# Cloud provider credentials
|
|
116
|
+
.aws
|
|
117
|
+
.azure
|
|
118
|
+
.kube
|
|
119
|
+
.config/gcloud
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
These are blocked **in addition** to the built-in protected list:
|
|
123
|
+
|
|
124
|
+
> `.Trash` `.ssh` `.gnupg` `.docker` `.zsh_sessions` `.cargo` `.gradle` `.gem`
|
|
125
|
+
|
|
126
|
+
### `<project>/.bxignore`
|
|
127
|
+
|
|
128
|
+
Block paths within the working directory itself. Supports glob patterns.
|
|
129
|
+
|
|
130
|
+
```gitignore
|
|
131
|
+
# Environment files with secrets
|
|
132
|
+
.env
|
|
133
|
+
.env.*
|
|
134
|
+
|
|
135
|
+
# Credentials and keys
|
|
136
|
+
secrets/
|
|
137
|
+
**/*.pem
|
|
138
|
+
**/*.key
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## How it works
|
|
142
|
+
|
|
143
|
+
bx generates a macOS sandbox profile at launch time:
|
|
144
|
+
|
|
145
|
+
1. **Scan** `$HOME` for non-hidden directories
|
|
146
|
+
2. **Block** each one individually with a `(deny file* (subpath ...))` rule
|
|
147
|
+
3. **Skip** the working directory, `~/Library`, dotfiles, and any paths from `~/.bxallow`
|
|
148
|
+
4. **Descend** into parent directories of allowed paths to block only siblings (because SBPL deny rules always override allow rules — you can't deny a parent and allow a child)
|
|
149
|
+
5. **Append** deny rules for protected dotdirs and `.bxignore` entries
|
|
150
|
+
6. **Write** the profile to `/tmp`, launch VSCode via `sandbox-exec`, clean up on exit
|
|
151
|
+
|
|
152
|
+
### Why not a simple deny-all + allow?
|
|
153
|
+
|
|
154
|
+
Apple's Sandbox Profile Language (SBPL) has a critical quirk: **`deny` always wins over `allow`**, regardless of rule order. This means:
|
|
155
|
+
|
|
156
|
+
```scheme
|
|
157
|
+
;; Does NOT work — the deny still blocks myproject
|
|
158
|
+
(deny file* (subpath "/Users/me/work"))
|
|
159
|
+
(allow file* (subpath "/Users/me/work/myproject"))
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Additionally, a broad `(deny file* (subpath HOME))` breaks `kqueue`/FSEvents file watchers and SQLite `fcntl` locks, causing VSCode errors even for paths that should be allowed.
|
|
163
|
+
|
|
164
|
+
bx avoids both issues by **never denying a parent of an allowed path**. Instead, it walks the directory tree and denies only the specific siblings that should be blocked.
|
|
165
|
+
|
|
166
|
+
## Tips
|
|
167
|
+
|
|
168
|
+
**See what's happening:** Use `--verbose` to inspect the generated profile before trusting it with sensitive work.
|
|
169
|
+
|
|
170
|
+
**Test the sandbox:** Try reading a blocked file from VSCode's terminal:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
cat ~/Documents/something.txt # Should fail with "Operation not permitted"
|
|
174
|
+
cat ~/Desktop/file.txt # Should fail
|
|
175
|
+
ls ~/work/other-project/ # Should fail
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Safety checks
|
|
179
|
+
|
|
180
|
+
bx detects and prevents problematic scenarios:
|
|
181
|
+
|
|
182
|
+
- **Sandbox nesting:** If `CODEBOX_SANDBOX=1` is set (automatically passed to child processes), bx refuses to start — nested `sandbox-exec` causes silent failures.
|
|
183
|
+
- **Unknown sandbox:** On startup, bx probes `~/Documents`, `~/Desktop`, and `~/Downloads`. If any return `EPERM`, another sandbox is already active — bx aborts.
|
|
184
|
+
- **VSCode terminal:** If `VSCODE_PID` is set, bx warns that it will launch a *new* instance, not sandbox the current one.
|
|
185
|
+
|
|
186
|
+
## Known limitations
|
|
187
|
+
|
|
188
|
+
- **File watcher warnings:** VSCode may log `EPERM` errors for `fs.watch()` on some paths. These are cosmetic and don't affect functionality.
|
|
189
|
+
- **SQLite warnings:** `state.vscdb` errors may appear in logs when VSCode's state database paths are affected. Extensions still work correctly.
|
|
190
|
+
- **`sandbox-exec` is undocumented:** Apple provides no official documentation for SBPL. The tool works in practice but could change with OS updates.
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
MIT — see [LICENSE](LICENSE).
|
package/dist/bx.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { accessSync, constants, cpSync, existsSync, globSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
if (process.env.CODEBOX_SANDBOX === "1") {
|
|
9
|
+
console.error("sandbox: ERROR — already running inside a bx sandbox.");
|
|
10
|
+
console.error("sandbox: Nesting sandbox-exec causes silent failures. Aborting.");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
if (process.env.VSCODE_PID) {
|
|
14
|
+
console.error("sandbox: WARNING — running from inside a VSCode terminal.");
|
|
15
|
+
console.error("sandbox: This will launch a *new* instance in a sandbox.");
|
|
16
|
+
console.error("sandbox: The current VSCode instance will NOT be sandboxed.");
|
|
17
|
+
}
|
|
18
|
+
function isAlreadySandboxed() {
|
|
19
|
+
for (const dir of [
|
|
20
|
+
"Documents",
|
|
21
|
+
"Desktop",
|
|
22
|
+
"Downloads"
|
|
23
|
+
]) {
|
|
24
|
+
const target = join(process.env.HOME, dir);
|
|
25
|
+
try {
|
|
26
|
+
accessSync(target, constants.R_OK);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
if (e.code === "EPERM") return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (isAlreadySandboxed()) {
|
|
34
|
+
console.error("sandbox: ERROR — already running inside a sandbox!");
|
|
35
|
+
console.error("sandbox: Nesting sandbox-exec may cause silent failures. Aborting.");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const MODES = [
|
|
39
|
+
"code",
|
|
40
|
+
"term",
|
|
41
|
+
"claude",
|
|
42
|
+
"exec"
|
|
43
|
+
];
|
|
44
|
+
const rawArgs = process.argv.slice(2);
|
|
45
|
+
const verbose = rawArgs.includes("--verbose");
|
|
46
|
+
const profileSandbox = rawArgs.includes("--profile-sandbox");
|
|
47
|
+
const positional = rawArgs.filter((a) => !a.startsWith("--"));
|
|
48
|
+
const doubleDashIdx = rawArgs.indexOf("--");
|
|
49
|
+
const execCmd = doubleDashIdx >= 0 ? rawArgs.slice(doubleDashIdx + 1) : [];
|
|
50
|
+
const beforeDash = doubleDashIdx >= 0 ? rawArgs.slice(0, doubleDashIdx).filter((a) => !a.startsWith("--")) : positional;
|
|
51
|
+
let mode = "code";
|
|
52
|
+
let workArg = ".";
|
|
53
|
+
if (beforeDash.length > 0 && MODES.includes(beforeDash[0])) {
|
|
54
|
+
mode = beforeDash[0];
|
|
55
|
+
workArg = beforeDash[1] ?? ".";
|
|
56
|
+
} else if (beforeDash.length > 0) workArg = beforeDash[0];
|
|
57
|
+
if (mode === "exec" && execCmd.length === 0) {
|
|
58
|
+
console.error("sandbox: exec mode requires a command after \"--\"");
|
|
59
|
+
console.error("usage: bx exec [workdir] -- command [args...]");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const HOME = process.env.HOME;
|
|
63
|
+
const SCRIPT_DIR = __dirname;
|
|
64
|
+
const WORK_DIR = resolve(workArg);
|
|
65
|
+
const allowedDirs = new Set([WORK_DIR]);
|
|
66
|
+
const sandboxAllowPath = join(HOME, ".bxallow");
|
|
67
|
+
if (existsSync(sandboxAllowPath)) for (const raw of readFileSync(sandboxAllowPath, "utf-8").split("\n")) {
|
|
68
|
+
const line = raw.trim();
|
|
69
|
+
if (!line || line.startsWith("#")) continue;
|
|
70
|
+
const absolute = resolve(HOME, line);
|
|
71
|
+
if (existsSync(absolute) && statSync(absolute).isDirectory()) allowedDirs.add(absolute);
|
|
72
|
+
}
|
|
73
|
+
const VSCODE_APP = "/Applications/Visual Studio Code.app/Contents/MacOS/Electron";
|
|
74
|
+
const VSCODE_DATA = join(HOME, ".vscode-sandbox");
|
|
75
|
+
const VSCODE_EXTENSIONS_GLOBAL = join(HOME, ".vscode", "extensions");
|
|
76
|
+
const VSCODE_EXTENSIONS_LOCAL = join(VSCODE_DATA, "extensions");
|
|
77
|
+
if (mode === "code" && profileSandbox) {
|
|
78
|
+
mkdirSync(VSCODE_DATA, { recursive: true });
|
|
79
|
+
if (!existsSync(VSCODE_EXTENSIONS_LOCAL) && existsSync(VSCODE_EXTENSIONS_GLOBAL)) {
|
|
80
|
+
console.error("sandbox: copying extensions from global install...");
|
|
81
|
+
cpSync(VSCODE_EXTENSIONS_GLOBAL, VSCODE_EXTENSIONS_LOCAL, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function collectBlockedDirs(parentDir) {
|
|
85
|
+
const blocked = [];
|
|
86
|
+
for (const name of readdirSync(parentDir)) {
|
|
87
|
+
if (name.startsWith(".")) continue;
|
|
88
|
+
const fullPath = join(parentDir, name);
|
|
89
|
+
if (!statSync(fullPath).isDirectory()) continue;
|
|
90
|
+
if (parentDir === HOME && name === "Library") continue;
|
|
91
|
+
if (SCRIPT_DIR.startsWith(fullPath + "/") || SCRIPT_DIR === fullPath) continue;
|
|
92
|
+
if (allowedDirs.has(fullPath)) continue;
|
|
93
|
+
if ([...allowedDirs].some((d) => d.startsWith(fullPath + "/"))) {
|
|
94
|
+
blocked.push(...collectBlockedDirs(fullPath));
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
blocked.push(fullPath);
|
|
98
|
+
}
|
|
99
|
+
return blocked;
|
|
100
|
+
}
|
|
101
|
+
const blockedDirs = collectBlockedDirs(HOME);
|
|
102
|
+
const PROTECTED_DOTDIRS = [
|
|
103
|
+
".Trash",
|
|
104
|
+
".ssh",
|
|
105
|
+
".gnupg",
|
|
106
|
+
".docker",
|
|
107
|
+
".zsh_sessions",
|
|
108
|
+
".cargo",
|
|
109
|
+
".gradle",
|
|
110
|
+
".gem"
|
|
111
|
+
];
|
|
112
|
+
const ignoredPaths = PROTECTED_DOTDIRS.map((d) => join(HOME, d));
|
|
113
|
+
function parseSandboxIgnore(filePath, baseDir) {
|
|
114
|
+
if (!existsSync(filePath)) return;
|
|
115
|
+
for (const raw of readFileSync(filePath, "utf-8").split("\n")) {
|
|
116
|
+
const line = raw.trim();
|
|
117
|
+
if (!line || line.startsWith("#")) continue;
|
|
118
|
+
const matches = globSync(line, { cwd: baseDir });
|
|
119
|
+
for (const match of matches) ignoredPaths.push(resolve(baseDir, match));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
parseSandboxIgnore(join(HOME, ".bxignore"), HOME);
|
|
123
|
+
parseSandboxIgnore(join(WORK_DIR, ".bxignore"), WORK_DIR);
|
|
124
|
+
const extraIgnored = ignoredPaths.length - PROTECTED_DOTDIRS.length;
|
|
125
|
+
if (extraIgnored > 0) console.error(`sandbox: .bxignore hides ${extraIgnored} extra path(s)`);
|
|
126
|
+
const profile = `; Auto-generated sandbox profile
|
|
127
|
+
; Working directory: ${WORK_DIR}
|
|
128
|
+
|
|
129
|
+
(version 1)
|
|
130
|
+
(allow default)
|
|
131
|
+
|
|
132
|
+
; Blocked directories (auto-generated from $HOME contents)
|
|
133
|
+
(deny file*
|
|
134
|
+
${blockedDirs.map((dir) => ` (subpath "${dir}")`).join("\n")}
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
${ignoredPaths.length > 0 ? `
|
|
138
|
+
; Hidden paths from .bxignore
|
|
139
|
+
(deny file*
|
|
140
|
+
${ignoredPaths.map((p) => {
|
|
141
|
+
return existsSync(p) && statSync(p).isDirectory() ? ` (subpath "${p}")` : ` (literal "${p}")`;
|
|
142
|
+
}).join("\n")}
|
|
143
|
+
)
|
|
144
|
+
` : ""}
|
|
145
|
+
`;
|
|
146
|
+
const profilePath = join("/tmp", `bx-${process.pid}.sb`);
|
|
147
|
+
writeFileSync(profilePath, profile);
|
|
148
|
+
console.error(`sandbox: ${mode} mode, working directory: ${WORK_DIR}`);
|
|
149
|
+
if (verbose) {
|
|
150
|
+
console.error("\n--- Generated sandbox profile ---");
|
|
151
|
+
console.error(profile);
|
|
152
|
+
console.error("--- End of profile ---\n");
|
|
153
|
+
}
|
|
154
|
+
function buildCommand() {
|
|
155
|
+
switch (mode) {
|
|
156
|
+
case "code": {
|
|
157
|
+
const args = ["--no-sandbox"];
|
|
158
|
+
if (profileSandbox) {
|
|
159
|
+
args.push("--user-data-dir", join(VSCODE_DATA, "data"));
|
|
160
|
+
args.push("--extensions-dir", VSCODE_EXTENSIONS_LOCAL);
|
|
161
|
+
}
|
|
162
|
+
args.push(WORK_DIR);
|
|
163
|
+
return {
|
|
164
|
+
bin: VSCODE_APP,
|
|
165
|
+
args
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "term": return {
|
|
169
|
+
bin: process.env.SHELL ?? "/bin/zsh",
|
|
170
|
+
args: ["-l"]
|
|
171
|
+
};
|
|
172
|
+
case "claude": return {
|
|
173
|
+
bin: "claude",
|
|
174
|
+
args: [WORK_DIR]
|
|
175
|
+
};
|
|
176
|
+
case "exec": return {
|
|
177
|
+
bin: execCmd[0],
|
|
178
|
+
args: execCmd.slice(1)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const cmd = buildCommand();
|
|
183
|
+
spawn("sandbox-exec", [
|
|
184
|
+
"-f",
|
|
185
|
+
profilePath,
|
|
186
|
+
"-D",
|
|
187
|
+
`HOME=${HOME}`,
|
|
188
|
+
"-D",
|
|
189
|
+
`WORK=${WORK_DIR}`,
|
|
190
|
+
cmd.bin,
|
|
191
|
+
...cmd.args
|
|
192
|
+
], {
|
|
193
|
+
cwd: WORK_DIR,
|
|
194
|
+
stdio: "inherit",
|
|
195
|
+
env: {
|
|
196
|
+
...process.env,
|
|
197
|
+
CODEBOX_SANDBOX: "1"
|
|
198
|
+
}
|
|
199
|
+
}).on("close", (code) => {
|
|
200
|
+
rmSync(profilePath, { force: true });
|
|
201
|
+
process.exit(code ?? 0);
|
|
202
|
+
});
|
|
203
|
+
//#endregion
|
|
204
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bx-mac",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Launch apps in a macOS sandbox — only the project directory is accessible",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bx": "dist/bx.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "rolldown -c",
|
|
14
|
+
"release": "./scripts/release.sh",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"sandbox",
|
|
19
|
+
"macos",
|
|
20
|
+
"security",
|
|
21
|
+
"vscode",
|
|
22
|
+
"claude"
|
|
23
|
+
],
|
|
24
|
+
"author": "Dirk Holtwick",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"rolldown": "^1.0.0-rc.12"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"packageManager": "pnpm@10.33.0"
|
|
33
|
+
}
|