bx-mac 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +31 -11
  2. package/dist/bx.js +20 -2
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -14,6 +14,12 @@ bx ~/work/my-project
14
14
 
15
15
  That's it. 🎉 VSCode opens with full access to `~/work/my-project` and nothing else.
16
16
 
17
+ Need multiple directories? No problem:
18
+
19
+ ```bash
20
+ bx ~/work/my-project ~/work/shared-lib
21
+ ```
22
+
17
23
  ## ✅ What it does
18
24
 
19
25
  - 🔒 Blocks `~/Documents`, `~/Desktop`, `~/Downloads`, and all other personal folders
@@ -21,8 +27,9 @@ That's it. 🎉 VSCode opens with full access to `~/work/my-project` and nothing
21
27
  - 🛡️ Protects sensitive dotdirs like `~/.ssh`, `~/.gnupg`, `~/.docker`, `~/.cargo`
22
28
  - ⚙️ Keeps VSCode, extensions, shell, Node.js, and other tooling fully functional
23
29
  - 🔍 Generates sandbox rules dynamically based on your actual `$HOME` contents
24
- - 📝 Supports `.bxignore` to hide secrets like `.env` files within a project
30
+ - 📝 Supports `.bxignore` files (searched recursively) to hide secrets like `.env` files within a project
25
31
  - 📂 Supports `~/.bxallow` to grant access to shared utility directories
32
+ - 🗂️ Supports multiple working directories in a single sandbox
26
33
 
27
34
  ## 🚫 What it doesn't do
28
35
 
@@ -54,13 +61,13 @@ pnpm link -g
54
61
 
55
62
  | Command | What it launches |
56
63
  |---|---|
57
- | `bx [workdir]` | 🖥️ VSCode (default) |
58
- | `bx code [workdir]` | 🖥️ VSCode (explicit) |
59
- | `bx term [workdir]` | 💻 Sandboxed login shell (`$SHELL -l`) |
60
- | `bx claude [workdir]` | 🤖 Claude Code CLI |
61
- | `bx exec [workdir] -- cmd` | ⚡ Any command you want |
64
+ | `bx [workdir...]` | 🖥️ VSCode (default) |
65
+ | `bx code [workdir...]` | 🖥️ VSCode (explicit) |
66
+ | `bx term [workdir...]` | 💻 Sandboxed login shell (`$SHELL -l`) |
67
+ | `bx claude [workdir...]` | 🤖 Claude Code CLI |
68
+ | `bx exec [workdir...] -- cmd` | ⚡ Any command you want |
62
69
 
63
- If no directory is given, the current directory is used.
70
+ If no directory is given, the current directory is used. All modes accept multiple directories.
64
71
 
65
72
  ### Examples
66
73
 
@@ -68,6 +75,9 @@ If no directory is given, the current directory is used.
68
75
  # 🖥️ VSCode with sandbox protection
69
76
  bx ~/work/my-project
70
77
 
78
+ # 📂 Multiple working directories
79
+ bx ~/work/my-project ~/work/shared-lib
80
+
71
81
  # 💻 Work on a project in a sandboxed terminal
72
82
  bx term ~/work/my-project
73
83
 
@@ -90,7 +100,7 @@ bx --verbose ~/work/my-project
90
100
 
91
101
  ## 📝 Configuration
92
102
 
93
- bx uses three optional config files — one entry per line, `#` for comments.
103
+ bx uses three optional config files — one entry per line, `#` for comments. Project `.bxignore` files are discovered recursively.
94
104
 
95
105
  ### `~/.bxallow`
96
106
 
@@ -119,7 +129,7 @@ These are blocked **in addition** to the built-in protected list:
119
129
 
120
130
  ### `<project>/.bxignore`
121
131
 
122
- Block paths within the working directory. Supports glob patterns.
132
+ Block paths within the working directory. Supports glob patterns. bx searches for `.bxignore` files **recursively** through the entire project tree (skipping `.`-prefixed dirs and `node_modules`), so you can place them in subdirectories to hide secrets close to where they live.
123
133
 
124
134
  ```gitignore
125
135
  .env
@@ -129,15 +139,25 @@ secrets/
129
139
  **/*.key
130
140
  ```
131
141
 
142
+ For example, a monorepo might have:
143
+
144
+ ```text
145
+ my-project/.bxignore # top-level rules
146
+ my-project/services/api/.bxignore # API-specific secrets
147
+ my-project/deploy/.bxignore # deployment credentials
148
+ ```
149
+
150
+ Each `.bxignore` resolves its patterns relative to its own directory.
151
+
132
152
  ## 🔧 How it works
133
153
 
134
154
  bx generates a macOS sandbox profile at launch time:
135
155
 
136
156
  1. **Scan** `$HOME` for non-hidden directories
137
157
  2. **Block** each one individually with `(deny file* (subpath ...))`
138
- 3. **Skip** the working directory, `~/Library`, dotfiles, and `~/.bxallow` paths
158
+ 3. **Skip** all working directories, `~/Library`, dotfiles, and `~/.bxallow` paths
139
159
  4. **Descend** into parent directories of allowed paths to block only siblings (because SBPL deny rules always override allow rules)
140
- 5. **Append** deny rules for protected dotdirs and `.bxignore` entries
160
+ 5. **Append** deny rules for protected dotdirs, `~/.bxignore`, and `.bxignore` files found recursively in each working directory
141
161
  6. **Write** the profile to `/tmp`, launch the app via `sandbox-exec`, clean up on exit
142
162
 
143
163
  ### Why not a simple deny-all + allow?
package/dist/bx.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const __VERSION__ = "0.3.0";
2
+ const __VERSION__ = "0.4.0";
3
3
  import { accessSync, constants, cpSync, existsSync, globSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { spawn } from "node:child_process";
@@ -27,6 +27,23 @@ function checkVSCodeTerminal() {
27
27
  }
28
28
  }
29
29
  /**
30
+ * Abort if any workdir IS $HOME or is not inside $HOME.
31
+ */
32
+ function checkWorkDirs(workDirs, home) {
33
+ for (const dir of workDirs) {
34
+ if (dir === home) {
35
+ console.error("sandbox: ERROR — working directory cannot be $HOME itself.");
36
+ console.error("sandbox: Sandboxing your entire home directory is not supported. Aborting.");
37
+ process.exit(1);
38
+ }
39
+ if (!dir.startsWith(home + "/")) {
40
+ console.error(`sandbox: ERROR — working directory is outside $HOME: ${dir}`);
41
+ console.error("sandbox: Only directories inside $HOME are supported. Aborting.");
42
+ process.exit(1);
43
+ }
44
+ }
45
+ }
46
+ /**
30
47
  * Detect if we're inside an unknown sandbox by probing well-known
31
48
  * directories that exist on every Mac but would be blocked.
32
49
  */
@@ -232,7 +249,7 @@ function buildCommand(mode, workDirs, home, profileSandbox, execCmd) {
232
249
  };
233
250
  case "claude": return {
234
251
  bin: "claude",
235
- args: [workDirs[0]]
252
+ args: []
236
253
  };
237
254
  case "exec": return {
238
255
  bin: execCmd[0],
@@ -278,6 +295,7 @@ checkExternalSandbox();
278
295
  const { mode, workArgs, verbose, profileSandbox, execCmd } = parseArgs();
279
296
  const HOME = process.env.HOME;
280
297
  const WORK_DIRS = workArgs.map((a) => resolve(a));
298
+ checkWorkDirs(WORK_DIRS, HOME);
281
299
  if (mode === "code" && profileSandbox) setupVSCodeProfile(HOME);
282
300
  const blockedDirs = collectBlockedDirs(HOME, HOME, __dirname, parseAllowedDirs(HOME, WORK_DIRS));
283
301
  const ignoredPaths = collectIgnoredPaths(HOME, WORK_DIRS);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bx-mac",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Launch apps in a macOS sandbox — only the project directory is accessible",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "rolldown -c",
14
+ "test": "vitest run",
14
15
  "prepublishOnly": "npm run build",
15
16
  "post:release": "./scripts/release.sh"
16
17
  },
@@ -25,7 +26,8 @@
25
26
  "license": "MIT",
26
27
  "devDependencies": {
27
28
  "@types/node": "^25.5.0",
28
- "rolldown": "^1.0.0-rc.12"
29
+ "rolldown": "^1.0.0-rc.12",
30
+ "vitest": "^4.1.2"
29
31
  },
30
32
  "engines": {
31
33
  "node": ">=22"