bx-mac 1.4.0 → 1.6.1
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/README.md +39 -5
- package/dist/bx.js +107 -18
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -166,7 +166,6 @@ App definitions in TOML format. Each `[<name>]` section becomes a CLI mode — u
|
|
|
166
166
|
[cursor]
|
|
167
167
|
bundle = "com.todesktop.230313mzl4w4u92"
|
|
168
168
|
binary = "Contents/MacOS/Cursor"
|
|
169
|
-
args = ["--no-sandbox"]
|
|
170
169
|
|
|
171
170
|
# Add Zed (explicit path, no discovery)
|
|
172
171
|
[zed]
|
|
@@ -177,6 +176,8 @@ path = "/Applications/Zed.app/Contents/MacOS/zed"
|
|
|
177
176
|
path = "/usr/local/bin/code"
|
|
178
177
|
```
|
|
179
178
|
|
|
179
|
+
**Electron apps:** bx automatically detects Electron-based apps (by checking for `Electron Framework.framework` inside the `.app` bundle) and adds `--no-sandbox` to disable Chromium's internal sandbox, which conflicts with `sandbox-exec`. No manual `args = ["--no-sandbox"]` needed.
|
|
180
|
+
|
|
180
181
|
| Field | Description |
|
|
181
182
|
| --- | --- |
|
|
182
183
|
| `mode` | Inherit from another app (e.g. `"code"`, `"cursor"`) — only `paths` / overrides needed |
|
|
@@ -234,23 +235,32 @@ Unified sandbox rules for your home directory. Paths relative to `$HOME`. Each l
|
|
|
234
235
|
.kube
|
|
235
236
|
.config/gcloud
|
|
236
237
|
|
|
237
|
-
# Allow read-write access to extra
|
|
238
|
+
# Allow read-write access to extra paths
|
|
238
239
|
rw:work/bin
|
|
239
240
|
rw:shared/libs
|
|
241
|
+
rw:~/projects/* # globs supported, ~ is expanded
|
|
240
242
|
|
|
241
243
|
# Allow read-only access (can read but not modify)
|
|
242
244
|
ro:reference/docs
|
|
243
245
|
ro:shared/toolchain
|
|
246
|
+
ro:.npmrc # files work too; overrides built-in block
|
|
244
247
|
```
|
|
245
248
|
|
|
246
|
-
|
|
249
|
+
Plain deny rules have two scopes:
|
|
250
|
+
|
|
251
|
+
- **At `$HOME` top level only** (no recursive `**` walk). `secrets/` matches `~/secrets`, not `~/nested/secrets`; `.config/gcloud` matches as a literal.
|
|
252
|
+
- **Recursively inside each workdir** - same semantics as a project-level `.bxignore`, so `secrets/` and `*.pem` apply across every project you open. The recursive scan skips `node_modules`, `.git`, caches, `DerivedData`, `Pods`, and similar subtrees for speed.
|
|
253
|
+
|
|
254
|
+
Deny rules are applied **in addition** to the built-in protected lists. `rw:` and `ro:` entries however **override** them - `ro:.npmrc` exposes the otherwise-blocked `~/.npmrc` read-only, `rw:.aws` opens the AWS credentials directory completely. Files (not just directories) are accepted, `~/...` is expanded, and globs (`*`, `**`) are matched against `$HOME`. Use override entries deliberately - you are weakening the default protection. `bx` prints a warning on stderr when an override exposes a built-in protected path.
|
|
247
255
|
|
|
248
|
-
|
|
256
|
+
Built-in protected lists:
|
|
249
257
|
|
|
250
258
|
> 🔒 **Dotdirs:** `.ssh` `.gnupg` `.docker` `.zsh_sessions` `.cargo` `.gradle` `.gem`
|
|
251
259
|
>
|
|
252
260
|
> 🏛️ **Library (opinionated):** `Accounts` `Calendars` `Contacts` `Cookies` `Finance` `Mail` `Messages` `Mobile Documents` `Photos` `Safari` and [others (see full list)](src/profile.ts) — plus containers of password managers & finance apps
|
|
253
261
|
|
|
262
|
+
**Limitation:** Overrides only work on whole protected paths. `rw:.aws/profile.json` does not selectively unblock a file inside `~/.aws` - the parent deny still wins (Apple SBPL: deny beats allow). Use `rw:.aws` to open the entire directory.
|
|
263
|
+
|
|
254
264
|
### `<project>/.bxignore`
|
|
255
265
|
|
|
256
266
|
Block paths within the working directory. Uses [`.gitignore`-style pattern matching](https://git-scm.com/docs/gitignore#_pattern_format):
|
|
@@ -284,6 +294,13 @@ my-project/deploy/.bxignore # deployment credentials
|
|
|
284
294
|
|
|
285
295
|
Each `.bxignore` resolves its patterns relative to its own directory.
|
|
286
296
|
|
|
297
|
+
Project `.bxignore` also accepts `ro:` entries to make paths within the workdir read-only (write-protect generated files, vendored libraries, etc.). `rw:` is silently ignored here - the workdir is already read-write by default.
|
|
298
|
+
|
|
299
|
+
```gitignore
|
|
300
|
+
ro:vendor/
|
|
301
|
+
ro:generated/schema.ts
|
|
302
|
+
```
|
|
303
|
+
|
|
287
304
|
### Self-protecting directories
|
|
288
305
|
|
|
289
306
|
You can make any directory protect itself — no global configuration needed. There are two ways:
|
|
@@ -345,7 +362,8 @@ bx detects and prevents problematic scenarios:
|
|
|
345
362
|
- **🔄 Sandbox nesting:** If `CODEBOX_SANDBOX=1` is set (auto-propagated), bx refuses to start — nested sandboxes cause silent failures.
|
|
346
363
|
- **🔍 Unknown sandbox:** On startup, bx probes `~/Documents`, `~/Desktop`, `~/Downloads`. If any return `EPERM`, another sandbox is active — bx aborts.
|
|
347
364
|
- **⚠️ VSCode terminal:** If `VSCODE_PID` is set, bx warns that it will launch a *new* instance, not sandbox the current one.
|
|
348
|
-
- **🧩 App already sandboxed:** For GUI app modes, bx inspects app entitlements (best effort) and warns if Apple App Sandbox is enabled, since
|
|
365
|
+
- **🧩 App already sandboxed:** For GUI app modes, bx inspects app entitlements (best effort) and warns if Apple App Sandbox is enabled, since bx's `sandbox-exec` wrapper may not apply correctly when the app is already sandboxed by macOS.
|
|
366
|
+
- **⚡ Electron auto-detection:** bx detects Electron-based apps (by checking for `Electron Framework.framework` in the `.app` bundle) and automatically adds `--no-sandbox` to disable Chromium's internal sandbox, which conflicts with `sandbox-exec`. This works for VSCode, Cursor, Windsurf, and any other Electron app.
|
|
349
367
|
- **🔁 App already running:** If the target app is already running, bx warns that the new workspace would open in the existing (unsandboxed) instance and asks for confirmation. This is important because Electron apps like VSCode, Cursor, etc. always reuse the running process — `sandbox-exec` has no effect on the already-running instance.
|
|
350
368
|
|
|
351
369
|
### Single-instance apps
|
|
@@ -378,6 +396,22 @@ ls ~/work/other-project/ # ❌ Operation not permitted
|
|
|
378
396
|
cat ./src/index.ts # ✅ Works!
|
|
379
397
|
```
|
|
380
398
|
|
|
399
|
+
**Drag and drop from `~/Downloads` (or other blocked dirs)** — if dragging files from Finder into a sandboxed app fails silently, the source folder is blocked. Grant read-only access by adding it to `~/.bxignore`:
|
|
400
|
+
|
|
401
|
+
```gitignore
|
|
402
|
+
ro:Downloads
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
The app can then read (and copy) the dragged file, but not modify the original. Same trick works for `ro:Desktop`, `ro:Documents`, etc.
|
|
406
|
+
|
|
407
|
+
**`npm install` fails with auth or registry errors** — `.npmrc` is blocked by default (may contain tokens). If your install needs the registry config but not write access, expose it read-only:
|
|
408
|
+
|
|
409
|
+
```gitignore
|
|
410
|
+
ro:.npmrc
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Same pattern applies to other tools whose config dotfiles are on the protected list (`.pypirc`, ...).
|
|
414
|
+
|
|
381
415
|
## ⚠️ Known limitations
|
|
382
416
|
|
|
383
417
|
- **⚠️ Sandbox profile is static:** The sandbox rules are generated **once at launch** by scanning the current state of `$HOME`. Directories or files created **after** the sandbox starts are **not protected** — for example, if a tool creates `~/new-project/` while the sandbox is running, that directory will be fully accessible. Similarly, project-level `.bxignore` patterns only match files that exist at launch time; files matching a blocked pattern (e.g. `.env`) that are created later will **not** be denied. Re-run `bx` to pick up changes.
|
package/dist/bx.js
CHANGED
|
@@ -1045,6 +1045,50 @@ function toGlobPattern(line) {
|
|
|
1045
1045
|
function resolveGlobMatches(pattern, baseDir) {
|
|
1046
1046
|
return globSync(toGlobPattern(pattern), { cwd: baseDir }).map((match) => resolve(baseDir, match));
|
|
1047
1047
|
}
|
|
1048
|
+
const SCAN_EXCLUDE_NAMES = new Set([
|
|
1049
|
+
"Library",
|
|
1050
|
+
"Applications",
|
|
1051
|
+
"node_modules",
|
|
1052
|
+
".git",
|
|
1053
|
+
".Trash",
|
|
1054
|
+
".cache",
|
|
1055
|
+
".npm",
|
|
1056
|
+
".pnpm-store",
|
|
1057
|
+
".yarn",
|
|
1058
|
+
".cargo",
|
|
1059
|
+
".rustup",
|
|
1060
|
+
".gradle",
|
|
1061
|
+
".gem",
|
|
1062
|
+
".m2",
|
|
1063
|
+
".nvm",
|
|
1064
|
+
".bun",
|
|
1065
|
+
".deno",
|
|
1066
|
+
"DerivedData",
|
|
1067
|
+
"Pods"
|
|
1068
|
+
]);
|
|
1069
|
+
function scanExcludeFilter(entry) {
|
|
1070
|
+
const name = typeof entry === "string" ? entry.split("/").pop() ?? "" : entry?.name ?? "";
|
|
1071
|
+
return SCAN_EXCLUDE_NAMES.has(name);
|
|
1072
|
+
}
|
|
1073
|
+
function resolveGlobMatchesBatch(patterns, baseDir) {
|
|
1074
|
+
if (patterns.length === 0) return [];
|
|
1075
|
+
return globSync(patterns.map(toGlobPattern), {
|
|
1076
|
+
cwd: baseDir,
|
|
1077
|
+
exclude: scanExcludeFilter
|
|
1078
|
+
}).map((match) => resolve(baseDir, match));
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Resolve patterns against `baseDir` at top level only (no recursive `**`
|
|
1082
|
+
* expansion). Trailing slashes are stripped; leading slashes are stripped
|
|
1083
|
+
* (already anchored). Used for `~/.bxignore` in $HOME to keep the lookup
|
|
1084
|
+
* cheap while still matching literal/glob paths at the home root.
|
|
1085
|
+
*/
|
|
1086
|
+
function resolveTopLevelMatches(patterns, baseDir) {
|
|
1087
|
+
if (patterns.length === 0) return [];
|
|
1088
|
+
const cleaned = patterns.map((p) => p.startsWith("/") ? p.slice(1) : p).map((p) => p.endsWith("/") ? p.slice(0, -1) : p).filter((p) => p.length > 0);
|
|
1089
|
+
if (cleaned.length === 0) return [];
|
|
1090
|
+
return globSync(cleaned, { cwd: baseDir }).map((m) => resolve(baseDir, m));
|
|
1091
|
+
}
|
|
1048
1092
|
/**
|
|
1049
1093
|
* A directory is self-protected if it contains a `.bxprotect` file
|
|
1050
1094
|
* or a `.bxignore` with a bare `/` entry. Self-protected directories
|
|
@@ -1055,19 +1099,27 @@ function isSelfProtected(dir) {
|
|
|
1055
1099
|
if (existsSync(join(dir, ".bxprotect"))) return true;
|
|
1056
1100
|
return parseLines(join(dir, ".bxignore")).some((l) => l === "/" || l === ".");
|
|
1057
1101
|
}
|
|
1058
|
-
function applyIgnoreFile(filePath, baseDir, ignored) {
|
|
1102
|
+
function applyIgnoreFile(filePath, baseDir, ignored, readOnly) {
|
|
1059
1103
|
for (const line of parseLines(filePath)) {
|
|
1060
1104
|
if (line === "/" || line === ".") continue;
|
|
1105
|
+
const accessMatch = line.match(ACCESS_PREFIX_RE);
|
|
1106
|
+
if (accessMatch) {
|
|
1107
|
+
if (!readOnly) continue;
|
|
1108
|
+
const [, prefix, rawPath] = accessMatch;
|
|
1109
|
+
if (prefix.toUpperCase() !== "RO") continue;
|
|
1110
|
+
for (const m of resolveGlobMatches(rawPath.trim(), baseDir)) readOnly.add(realpathSafe(m));
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1061
1113
|
ignored.push(...resolveGlobMatches(line, baseDir));
|
|
1062
1114
|
}
|
|
1063
1115
|
}
|
|
1064
|
-
function collectIgnoreFilesRecursive(dir, ignored) {
|
|
1116
|
+
function collectIgnoreFilesRecursive(dir, ignored, readOnly) {
|
|
1065
1117
|
if (isSelfProtected(dir)) {
|
|
1066
1118
|
ignored.push(dir);
|
|
1067
1119
|
return;
|
|
1068
1120
|
}
|
|
1069
1121
|
const ignoreFile = join(dir, ".bxignore");
|
|
1070
|
-
if (existsSync(ignoreFile)) applyIgnoreFile(ignoreFile, dir, ignored);
|
|
1122
|
+
if (existsSync(ignoreFile)) applyIgnoreFile(ignoreFile, dir, ignored, readOnly);
|
|
1071
1123
|
let entries;
|
|
1072
1124
|
try {
|
|
1073
1125
|
entries = readdirSync(dir);
|
|
@@ -1078,11 +1130,29 @@ function collectIgnoreFilesRecursive(dir, ignored) {
|
|
|
1078
1130
|
if (name.startsWith(".") || name === "node_modules") continue;
|
|
1079
1131
|
const fullPath = join(dir, name);
|
|
1080
1132
|
try {
|
|
1081
|
-
if (statSync(fullPath).isDirectory()) collectIgnoreFilesRecursive(fullPath, ignored);
|
|
1133
|
+
if (statSync(fullPath).isDirectory()) collectIgnoreFilesRecursive(fullPath, ignored, readOnly);
|
|
1082
1134
|
} catch {}
|
|
1083
1135
|
}
|
|
1084
1136
|
}
|
|
1085
1137
|
const ACCESS_PREFIX_RE = /^(RW|RO):(.+)$/i;
|
|
1138
|
+
function expandHomePath(home, raw) {
|
|
1139
|
+
const trimmed = raw.trim();
|
|
1140
|
+
if (trimmed === "~") return home;
|
|
1141
|
+
if (trimmed.startsWith("~/")) return join(home, trimmed.slice(2));
|
|
1142
|
+
return resolve(home, trimmed);
|
|
1143
|
+
}
|
|
1144
|
+
function realpathSafe(p) {
|
|
1145
|
+
try {
|
|
1146
|
+
return realpathSync(p);
|
|
1147
|
+
} catch {
|
|
1148
|
+
return p;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function resolveAccessTargets(home, raw) {
|
|
1152
|
+
const expanded = expandHomePath(home, raw);
|
|
1153
|
+
if (existsSync(expanded)) return [realpathSafe(expanded)];
|
|
1154
|
+
return globSync(raw.trim().replace(/^~\//, ""), { cwd: home }).map((m) => realpathSafe(join(home, m))).filter((p) => existsSync(p));
|
|
1155
|
+
}
|
|
1086
1156
|
function parseHomeConfig(home, workDirs) {
|
|
1087
1157
|
const allowed = new Set(workDirs);
|
|
1088
1158
|
const readOnly = /* @__PURE__ */ new Set();
|
|
@@ -1090,10 +1160,10 @@ function parseHomeConfig(home, workDirs) {
|
|
|
1090
1160
|
const match = line.match(ACCESS_PREFIX_RE);
|
|
1091
1161
|
if (!match) continue;
|
|
1092
1162
|
const [, prefix, rawPath] = match;
|
|
1093
|
-
const
|
|
1094
|
-
if (
|
|
1095
|
-
|
|
1096
|
-
|
|
1163
|
+
const targets = resolveAccessTargets(home, rawPath);
|
|
1164
|
+
if (targets.length === 0) continue;
|
|
1165
|
+
const target = prefix.toUpperCase() === "RW" ? allowed : readOnly;
|
|
1166
|
+
for (const t of targets) target.add(t);
|
|
1097
1167
|
}
|
|
1098
1168
|
return {
|
|
1099
1169
|
allowed,
|
|
@@ -1143,22 +1213,26 @@ function collectProtectedContainers(home) {
|
|
|
1143
1213
|
}
|
|
1144
1214
|
return matched;
|
|
1145
1215
|
}
|
|
1216
|
+
function isOverridden(path, overrides) {
|
|
1217
|
+
return overrides.has(path) || overrides.has(realpathSafe(path));
|
|
1218
|
+
}
|
|
1146
1219
|
function collectReadOnlyDotfiles(home, overrides = /* @__PURE__ */ new Set()) {
|
|
1147
|
-
return PROTECTED_HOME_DOTFILES_RO.map((f) => join(home, f)).filter((p) => !
|
|
1220
|
+
return PROTECTED_HOME_DOTFILES_RO.map((f) => join(home, f)).filter((p) => !isOverridden(p, overrides));
|
|
1148
1221
|
}
|
|
1149
|
-
function collectIgnoredPaths(home, workDirs, overrides = /* @__PURE__ */ new Set()) {
|
|
1222
|
+
function collectIgnoredPaths(home, workDirs, overrides = /* @__PURE__ */ new Set(), readOnly) {
|
|
1150
1223
|
const ignored = [
|
|
1151
1224
|
...PROTECTED_DOTDIRS.map((d) => join(home, d)),
|
|
1152
1225
|
...PROTECTED_HOME_DOTFILES.map((f) => join(home, f)),
|
|
1153
1226
|
...PROTECTED_LIBRARY_DIRS.map((d) => join(home, "Library", d)),
|
|
1154
1227
|
...new Set(collectProtectedContainers(home))
|
|
1155
|
-
].filter((p) => !
|
|
1228
|
+
].filter((p) => !isOverridden(p, overrides));
|
|
1156
1229
|
const globalIgnore = join(home, ".bxignore");
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1230
|
+
const globalDenyLines = existsSync(globalIgnore) ? parseLines(globalIgnore).filter((l) => !ACCESS_PREFIX_RE.test(l)) : [];
|
|
1231
|
+
for (const m of resolveTopLevelMatches(globalDenyLines, home)) if (!isOverridden(m, overrides)) ignored.push(m);
|
|
1232
|
+
for (const workDir of workDirs) {
|
|
1233
|
+
for (const m of resolveGlobMatchesBatch(globalDenyLines, workDir)) if (!isOverridden(m, overrides)) ignored.push(m);
|
|
1234
|
+
collectIgnoreFilesRecursive(workDir, ignored, readOnly);
|
|
1160
1235
|
}
|
|
1161
|
-
for (const workDir of workDirs) collectIgnoreFilesRecursive(workDir, ignored);
|
|
1162
1236
|
return ignored;
|
|
1163
1237
|
}
|
|
1164
1238
|
function sbplEscape(path) {
|
|
@@ -1409,6 +1483,11 @@ function executableFromBundle(bundlePath, app) {
|
|
|
1409
1483
|
if (app.binary) return join(bundlePath, app.binary);
|
|
1410
1484
|
return join(bundlePath, "Contents", "MacOS", basename(bundlePath, ".app"));
|
|
1411
1485
|
}
|
|
1486
|
+
function isElectronApp(resolvedPath) {
|
|
1487
|
+
const bundle = appBundleFromPath(resolvedPath);
|
|
1488
|
+
if (!bundle) return false;
|
|
1489
|
+
return existsSync(join(bundle, "Contents", "Frameworks", "Electron Framework.framework"));
|
|
1490
|
+
}
|
|
1412
1491
|
const SANDBOX_KEY = "com.apple.security.app-sandbox";
|
|
1413
1492
|
function hasAppSandboxEntitlement(entitlements) {
|
|
1414
1493
|
if (new RegExp(`<key>\\s*${SANDBOX_KEY.replace(/\./g, "\\.")}\\s*</key>\\s*<true\\s*/>`, "i").test(entitlements)) return true;
|
|
@@ -1491,6 +1570,7 @@ function buildAppCommand(mode, workDirs, home, profileSandbox, appArgs, apps) {
|
|
|
1491
1570
|
args.push("--extensions-dir", join(dataDir, "extensions"));
|
|
1492
1571
|
}
|
|
1493
1572
|
if (app.args) args.push(...app.args);
|
|
1573
|
+
if (!args.includes("--no-sandbox") && isElectronApp(resolvedPath)) args.push("--no-sandbox");
|
|
1494
1574
|
if (appArgs.length > 0) args.push(...appArgs);
|
|
1495
1575
|
args.push(...getPassPaths(app, workDirs, home));
|
|
1496
1576
|
return {
|
|
@@ -1535,7 +1615,7 @@ function getNestedSandboxWarning(mode, apps) {
|
|
|
1535
1615
|
"pipe",
|
|
1536
1616
|
"pipe"
|
|
1537
1617
|
]
|
|
1538
|
-
}))) return
|
|
1618
|
+
}))) return `"${mode}" has Apple App Sandbox enabled — bx sandbox may not apply correctly`;
|
|
1539
1619
|
} catch {}
|
|
1540
1620
|
return null;
|
|
1541
1621
|
}
|
|
@@ -1699,7 +1779,7 @@ function printDryRunTree({ home, blockedDirs, ignoredPaths, readOnlyDirs, workDi
|
|
|
1699
1779
|
}
|
|
1700
1780
|
//#endregion
|
|
1701
1781
|
//#region src/index.ts
|
|
1702
|
-
const VERSION = "1.
|
|
1782
|
+
const VERSION = "1.6.1";
|
|
1703
1783
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1704
1784
|
if (process$1.argv.includes("--version") || process$1.argv.includes("-v")) {
|
|
1705
1785
|
console.log(`bx ${VERSION}`);
|
|
@@ -1749,8 +1829,9 @@ async function main() {
|
|
|
1749
1829
|
if (profileSandbox) await setupVSCodeProfile(HOME, profileSandbox);
|
|
1750
1830
|
const { allowed, readOnly } = parseHomeConfig(HOME, workDirs);
|
|
1751
1831
|
const allAccessible = new Set([...allowed, ...readOnly]);
|
|
1832
|
+
warnDangerousOverrides(HOME, allAccessible);
|
|
1752
1833
|
const blockedDirs = collectBlockedDirs(HOME, HOME, __dirname, allAccessible);
|
|
1753
|
-
const ignoredPaths = collectIgnoredPaths(HOME, workDirs, allAccessible);
|
|
1834
|
+
const ignoredPaths = collectIgnoredPaths(HOME, workDirs, allAccessible, readOnly);
|
|
1754
1835
|
const readOnlyDotfiles = collectReadOnlyDotfiles(HOME, allAccessible);
|
|
1755
1836
|
printPolicySummary(mode, workDirs, blockedDirs, ignoredPaths, readOnly);
|
|
1756
1837
|
const profile = generateProfile(workDirs, blockedDirs, ignoredPaths, [...readOnly], HOME, readOnlyDotfiles);
|
|
@@ -1878,6 +1959,14 @@ function printPolicySummary(mode, workDirs, blockedDirs, ignoredPaths, readOnly)
|
|
|
1878
1959
|
if (extraIgnored > 0) parts.push(`${extraIgnored} from .bxignore`);
|
|
1879
1960
|
console.error(fmt.detail(parts.join(" · ")));
|
|
1880
1961
|
}
|
|
1962
|
+
function warnDangerousOverrides(home, accessible) {
|
|
1963
|
+
const hits = [
|
|
1964
|
+
...PROTECTED_DOTDIRS.map((d) => join(home, d)),
|
|
1965
|
+
...PROTECTED_HOME_DOTFILES.map((f) => join(home, f)),
|
|
1966
|
+
...PROTECTED_LIBRARY_DIRS.map((d) => join(home, "Library", d))
|
|
1967
|
+
].filter((p) => accessible.has(p));
|
|
1968
|
+
for (const p of hits) console.error(fmt.detail(`warning: ~/.bxignore override exposes built-in protected path ${p}`));
|
|
1969
|
+
}
|
|
1881
1970
|
function printLaunchDetails(cmd, cwd) {
|
|
1882
1971
|
const quote = (a) => JSON.stringify(a);
|
|
1883
1972
|
console.error(fmt.detail(`bin: ${cmd.bin}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bx-mac",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Sandbox any macOS app — only your project directory stays accessible",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"darwin"
|
|
48
48
|
],
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@types/node": "^25.
|
|
51
|
-
"rolldown": "^1.0.0-rc.
|
|
50
|
+
"@types/node": "^25.6.0",
|
|
51
|
+
"rolldown": "^1.0.0-rc.16",
|
|
52
52
|
"smol-toml": "^1.6.1",
|
|
53
|
-
"vitest": "^4.1.
|
|
53
|
+
"vitest": "^4.1.4"
|
|
54
54
|
},
|
|
55
55
|
"engines": {
|
|
56
56
|
"node": ">=22"
|