clabox 0.1.1 → 0.2.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/docs/guideline.md +10 -5
- package/lib/{aliases-DXyz-ufw.d.ts → aliases-BNdrOn9E.d.ts} +7 -3
- package/lib/{aliases-DKGcMHHe.js → aliases-KGjds7dK.js} +2 -2
- package/lib/aliases-KGjds7dK.js.map +1 -0
- package/lib/{app-DzQ5yZfD.d.ts → app-B1djcEnN.d.ts} +2 -2
- package/lib/{app-CQmESEdh.js → app-ChnKbgkD.js} +3 -3
- package/lib/{app-CQmESEdh.js.map → app-ChnKbgkD.js.map} +1 -1
- package/lib/cli.js +4 -3
- package/lib/cli.js.map +1 -1
- package/lib/config-BQ44iVWT.js.map +1 -1
- package/lib/{config-DQWueb4a.d.ts → config-jZAB2Zg8.d.ts} +29 -2
- package/lib/extras-B2ss2Cgh.js +46 -0
- package/lib/extras-B2ss2Cgh.js.map +1 -0
- package/lib/extras-B_dzBrCF.d.ts +34 -0
- package/lib/{ghostty-By4zmOuk.d.ts → ghostty-DFwFh4aL.d.ts} +14 -5
- package/lib/{ghostty-DcMEZ6Ey.js → ghostty-Dw4QLyl-.js} +2 -2
- package/lib/ghostty-Dw4QLyl-.js.map +1 -0
- package/lib/index.d.ts +10 -9
- package/lib/index.js +7 -6
- package/lib/init/aliases.d.ts +1 -1
- package/lib/init/aliases.js +1 -1
- package/lib/init/app.d.ts +1 -1
- package/lib/init/app.js +1 -1
- package/lib/init/ghostty.d.ts +1 -1
- package/lib/init/ghostty.js +1 -1
- package/lib/init/raycast.d.ts +1 -1
- package/lib/init/scaffold.d.ts +1 -1
- package/lib/init/scaffold.js +1 -1
- package/lib/{profile-BeM41NXc.d.ts → profile-7CiMUPu9.d.ts} +2 -2
- package/lib/{raycast-DM7c559f.d.ts → raycast-fc5U1x7E.d.ts} +2 -2
- package/lib/{run-Cx8cuTh5.d.ts → run-BWGDNJiY.d.ts} +11 -3
- package/lib/{run-CNehSQ-S.js → run-yUsOaKFx.js} +22 -3
- package/lib/run-yUsOaKFx.js.map +1 -0
- package/lib/sandbox/extras.d.ts +2 -0
- package/lib/sandbox/extras.js +2 -0
- package/lib/sandbox/profile.d.ts +1 -1
- package/lib/sandbox/run.d.ts +2 -2
- package/lib/sandbox/run.js +2 -2
- package/lib/{scaffold-B7pUVGoC.js → scaffold-C1tqCL_8.js} +47 -15
- package/lib/scaffold-C1tqCL_8.js.map +1 -0
- package/lib/{scaffold-ByIbYAeS.d.ts → scaffold-CbsKxlL0.d.ts} +3 -1
- package/lib/utils/config.d.ts +2 -2
- package/package.json +1 -1
- package/lib/aliases-DKGcMHHe.js.map +0 -1
- package/lib/ghostty-DcMEZ6Ey.js.map +0 -1
- package/lib/run-CNehSQ-S.js.map +0 -1
- package/lib/scaffold-B7pUVGoC.js.map +0 -1
package/docs/guideline.md
CHANGED
|
@@ -36,7 +36,8 @@ src/
|
|
|
36
36
|
├── sandbox/
|
|
37
37
|
│ ├── profile.ts # pure SBPL builder: buildProfile, detectPackagePaths,
|
|
38
38
|
│ │ # subpath/literal/regex/globalName/ipcName/reEscape helpers
|
|
39
|
-
│
|
|
39
|
+
│ ├── extras.ts # pure: boxSlug, buildBoxExtras (per-box mcp/systemPrompt → args+files)
|
|
40
|
+
│ └── run.ts # I/O: profilePath, generateProfile, writeExtraFiles, runClaude
|
|
40
41
|
├── init/
|
|
41
42
|
│ ├── aliases.ts # pure: aliasName, buildIndex, buildWrapper, buildAliasFiles
|
|
42
43
|
│ ├── ghostty.ts # pure: buildGhosttyConfig, buildCommand, buildLauncherSource,
|
|
@@ -50,6 +51,7 @@ src/
|
|
|
50
51
|
tests/
|
|
51
52
|
├── profile.test.ts # bun:test — unit (profile text) + functional (real sandbox-exec)
|
|
52
53
|
├── init.test.ts # bun:test — alias/ghostty text + scaffold & app boxes (tmp-dir fs)
|
|
54
|
+
├── extras.test.ts # bun:test — per-box mcp/systemPrompt → claude args + mcp json
|
|
53
55
|
└── box.test.ts # bun:test — named-box resolution (tmp-dir fs)
|
|
54
56
|
lib/ # build output (tsdown) — gitignored
|
|
55
57
|
docs/
|
|
@@ -106,18 +108,21 @@ clabox run → loadConfig() → buildProfile() → <TMPDIR>/…sb
|
|
|
106
108
|
```
|
|
107
109
|
|
|
108
110
|
### `utils/config.ts`
|
|
109
|
-
Builds the effective config in three layers (later wins): `defaultConfig` → env vars → a JS config file. `findConfigFile(explicit?)` looks up the explicit path (the `--config` CLI flag, falling back to `CLABOX_CONFIG`) → `./clabox.config.mjs` / `./clabox.config.js` → `~/.config/clabox/config.mjs`; the `--config` flag wins over `CLABOX_CONFIG`. `loadConfig(explicit?)` forwards that path, dynamically imports the file and accepts either a plain object (merged via `mergeConfig`, a deep merge over the defaults) or a function `(defaults) => config`. `expandHome()` expands a leading `~`. Exports the `Config`/`BotConfig`/`PathRules`/`LoadedConfig` types.
|
|
111
|
+
Builds the effective config in three layers (later wins): `defaultConfig` → env vars → a JS config file. `findConfigFile(explicit?)` looks up the explicit path (the `--config` CLI flag, falling back to `CLABOX_CONFIG`) → `./clabox.config.mjs` / `./clabox.config.js` → `~/.config/clabox/config.mjs`; the `--config` flag wins over `CLABOX_CONFIG`. `loadConfig(explicit?)` forwards that path, dynamically imports the file and accepts either a plain object (merged via `mergeConfig`, a deep merge over the defaults) or a function `(defaults) => config`. `expandHome()` expands a leading `~`. Exports the `Config`/`BotConfig`/`PathRules`/`McpServer`/`LoadedConfig` types. The `Config` carries optional `mcp` (`Record<string, McpServer>`) and `systemPrompt` (`string | string[]`) — declarative per-box MCP / pre-prompt compiled by `sandbox/extras.ts` (see below).
|
|
110
112
|
|
|
111
113
|
**Named boxes.** `configsDir()` returns the global box dir (`~/.config/clabox/configs`, overridable via `CLABOX_CONFIGS_DIR`). `resolveBox(name, dir?)` maps a name to its config file there, preferring `<name>.config.mjs` over a bare `<name>.mjs` and throwing (with the available boxes listed) when none exists; a `_`-prefixed name is refused (it's a shared partial, not a box), so `-b` matches exactly what `listBoxes` advertises. `listBoxes(dir?)` returns the sorted, de-duplicated box names, skipping `_`-prefixed shared partials (e.g. `_presets.mjs`). The CLI's `-b`/`--box <name>` flag resolves the name and feeds the path to `loadConfig` as the explicit config (so `-b` wins over `--config`).
|
|
112
114
|
|
|
113
115
|
### `sandbox/profile.ts` (pure)
|
|
114
116
|
Assembles the SBPL profile text from typed helpers (`subpath`, `literal`, `regex`, `globalName`, `ipcName`, `reEscape`) — no I/O beyond `fs.existsSync` for autodetection. `detectPackagePaths()` finds installed package managers (Homebrew `/opt/homebrew` or `/usr/local/Homebrew`, `~/.local`, Nix `/nix/store`) to grant read/exec. `buildProfile(config, { projectDir, detectedPaths })` returns the full profile and sanity-checks it carries `(version 1)`.
|
|
115
117
|
|
|
118
|
+
### `sandbox/extras.ts` (pure)
|
|
119
|
+
Compiles a box's **declarative** `mcp` / `systemPrompt` config into claude args (and the files they reference), so user config stays pure data and clabox owns the wiring. `boxSlug(configFile, projectDir)` derives a stable slug — the config-file basename minus `.config.mjs`/`.mjs`, else the project-dir basename. `buildBoxExtras(config, slug)` returns `{ claudeArgs, files }`: when `config.mcp` (a `Record<string, McpServer>`) is non-empty it writes `<configDir>/mcp/<slug>.json` (`{ mcpServers: … }`) and emits `--strict-mcp-config --mcp-config <file>` so a **shared** `configDir`'s *global/plugin* MCP servers are ignored — each box loads exactly its own; `config.systemPrompt` (`string | string[]`, joined with blank lines) is appended inline via `--append-system-prompt` (no file). The json lives under `configDir` because the sandbox grants it RW — and because `~/.config/*` is hard-denied, it can't sit next to the box config.
|
|
120
|
+
|
|
116
121
|
### `sandbox/run.ts` (I/O)
|
|
117
|
-
`profilePath()` returns the deterministic `$TMPDIR/clabox-<dir>-<hash>.sb` path. `resolveProjectDir(config)` returns the effective project dir — `config.cwd` (with `~` expanded) when set, else `process.cwd()` — and is what `generateProfile`/`runClaude` default to. `generateProfile()` requires `sandbox-exec`, builds the profile and writes it. `runClaude()` resolves the project dir + `claude` binary (config → PATH → `~/.local/bin/claude`), forces the bot git identity + hardening env (`buildEnvArgs`, with `config.env` appended last so it wins), sets the terminal title, and execs `sh -c 'ulimit -u N; exec sandbox-exec -f <sb> env … claude
|
|
122
|
+
`profilePath()` returns the deterministic `$TMPDIR/clabox-<dir>-<hash>.sb` path. `resolveProjectDir(config)` returns the effective project dir — `config.cwd` (with `~` expanded) when set, else `process.cwd()` — and is what `generateProfile`/`runClaude` default to. `generateProfile()` requires `sandbox-exec`, builds the profile and writes it. `writeExtraFiles(files)` `mkdir -p`s and writes the per-box extras (the MCP json). `runClaude()` resolves the project dir + `claude` binary (config → PATH → `~/.local/bin/claude`), compiles + materializes the box extras (`buildBoxExtras` + `writeExtraFiles`), forces the bot git identity + hardening env (`buildEnvArgs`, with `config.env` appended last so it wins), sets the terminal title, and execs `sh -c 'ulimit -u N; exec sandbox-exec -f <sb> env … claude <config.claudeArgs> <extras> <CLI args>'` **in the resolved project dir** (`spawnSync` `cwd`), returning the exit code. (`runInit` also materializes the extras at `init` time via `scaffold.ts#materializeExtras`, slug = box name.)
|
|
118
123
|
|
|
119
124
|
### `init/aliases.ts` (pure) + `init/scaffold.ts` (I/O)
|
|
120
|
-
`clabox init` turns a directory of box configs into ready-to-use shell commands. `aliases.ts` is pure text: `aliasName(profile)` yields `clabox-<name>`; `buildIndex()` renders the source-able `index.sh` (a `_clabox_run` helper that runs `CLABOX_CONFIGS_DIR=<configsDir> clabox -b "$1"` plus one function per box); `buildWrapper()` renders a standalone `.sh` that sources `index.sh` and calls one function; `buildAliasFiles()` returns the full file set. There is **one command per box** — yolo vs. safe is decided by the box's own preset (`claudeArgs`), not by a `-safe` alias. `scaffold.ts` does the I/O: `discoverProfiles(configsDir)` returns the sorted box names via `listBoxes` (both `<name>.mjs`/`<name>.config.mjs`, `_`-partials skipped; throws if the dir is missing or has no boxes), and `runInit({ baseDir, buildApps, only })` (async) resolves `<baseDir>/configs` + `<baseDir>/scripts` (default `<cwd>/__`), prunes its own prior artifacts (`index.sh`, `clabox-*.sh`), then writes the new ones `chmod +x`. Absolute paths are baked in so the scripts run from any cwd. It returns `{ profiles, written, apps, ghosttyConfigs, warnings, … }`.
|
|
125
|
+
`clabox init` turns a directory of box configs into ready-to-use shell commands. `aliases.ts` is pure text: `aliasName(profile)` yields `clabox-<name>`; `buildIndex()` renders the source-able `index.sh` (a `_clabox_run` helper that runs `CLABOX_CONFIGS_DIR=<configsDir> clabox -b "$1"` plus one function per box); `buildWrapper()` renders a standalone `.sh` that sources `index.sh` and calls one function; `buildAliasFiles()` returns the full file set. There is **one command per box** — yolo vs. safe is decided by the box's own preset (`claudeArgs`), not by a `-safe` alias. `scaffold.ts` does the I/O: `discoverProfiles(configsDir)` returns the sorted box names via `listBoxes` (both `<name>.mjs`/`<name>.config.mjs`, `_`-partials skipped; throws if the dir is missing or has no boxes), and `runInit({ baseDir, buildApps, only })` (async) resolves `<baseDir>/configs` + `<baseDir>/scripts` (default `<cwd>/__`), prunes its own prior artifacts (`index.sh`, `clabox-*.sh`), then writes the new ones `chmod +x`. It also materializes each box's MCP json (`materializeExtras`, best-effort per box). Absolute paths are baked in so the scripts run from any cwd. It returns `{ profiles, written, apps, ghosttyConfigs, raycastCommands, extraFiles, warnings, … }`.
|
|
121
126
|
|
|
122
127
|
### `init/ghostty.ts` + `init/raycast.ts` (pure) + `init/app.ts` (I/O) — standalone Ghostty apps
|
|
123
128
|
A box becomes a real macOS app by carrying an `app` object (`AppConfig`: `name`, `title?`, `emoji?`, `icon?`, `macosIcon?`, `ghostty?`, `bundleId?`). When `runInit` runs with `buildApps` (the default), it `loadConfig`s each box and, for every box with an `app`, writes `<baseDir>/ghostty/<name>.config`, a `<baseDir>/raycast/<name>.sh` Raycast command, and clones a `.app`. `ghostty.ts` is pure text: `buildGhosttyConfig()` renders the config (a `command = zsh -lic 'cd <cwd> && CLABOX_CONFIGS_DIR=<dir> <claboxBin> -b <name>; exec zsh'` — a **login + interactive** zsh so the GUI-launched app inherits the user's PATH (`/etc/zprofile`→`path_helper` for Homebrew, `~/.zshrc` for fnm/nvm/volta); a bare `bash -c` gets only launchd's minimal PATH and can't find `node` for clabox's `#!/usr/bin/env node` shebang — plus `title`/`macos-icon`/extra `app.ghostty` lines and an optional leading `config-file`); `buildLauncherSource()` renders a tiny C launcher that finds `ghostty.real` next to itself and re-execs it with `--config-file=<config>` baked in; `appBundlePath()`/`bundleId()` derive the bundle path/id. `raycast.ts` renders the Raycast script command (`buildRaycastCommand()`: `@raycast.*` metadata + `open <appPath>`; `raycastIcon()` picks `app.emoji`, else the title's leading emoji, else 👻). `app.ts` is the macOS-only I/O (replaces the old `ghostty-app-builder.sh`): `canBuildApps(builder)` gates on darwin + donor app + `cc`; `buildApp()` extracts the donor's entitlements, `cp -R` clones Ghostty.app into `<appsDir>/<name>.app`, patches `Info.plist` (identity + Sparkle off), swaps the binary for the compiled launcher, installs the icon (`.icns` copy or `.png`→`sips`+`iconutil`, plus `plutil -remove CFBundleIconName` — Ghostty's plist references an icon inside its compiled `Assets.car`, which macOS prefers over the loose `.icns`, so the name must be dropped for our icon to take effect), and `codesign`s (`appBuilder.signId`, else ad-hoc `-`). Machine-wide build settings live in `config.appBuilder` (`ghosttyApp`, `appsDir`, `signId`, `baseGhosttyConfig`, `claboxBin`). Builds are best-effort: a non-buildable host or a thrown build records a `warning` and the aliases (plus the ghostty config + raycast script) are still emitted. `init` prunes its own `<baseDir>/ghostty/*.config` and `<baseDir>/raycast/*.sh` on a full run (not under `--app`, which would orphan other apps' artifacts); built `.app` bundles are **not** auto-deleted.
|
|
@@ -130,7 +135,7 @@ The yargs CLI (`scriptName('clabox')`). Commands: `run [claudeArgs..]` (default)
|
|
|
130
135
|
- **Read-write:** the project dir (`config.cwd` if set, else the shell CWD), the Claude config dir (`configDir`), `/tmp`, `/private/tmp`, `/private/var/folders/…`, `~/Library/Keychains` (OAuth refresh), plus `paths.readWrite`.
|
|
131
136
|
- **Network:** `(allow network*)` when `network: true` (default).
|
|
132
137
|
- **Two deny tiers** (Seatbelt evaluates rules in order — the *last* match wins — so placement is deliberate):
|
|
133
|
-
- **Soft privacy deny (overridable):** `denyHome` (`~/Documents`, `~/Desktop`, …) and `paths.deny`, emitted *before* the extra `readOnly`/`readWrite` and the project dir. An explicit grant therefore overrides it — handy for a project that lives under `~/Documents`,
|
|
138
|
+
- **Soft privacy deny (overridable):** `denyHome` (`~/Documents`, `~/Desktop`, …) and `paths.deny`, emitted *before* the extra `readOnly`/`readWrite` and the project dir. An explicit grant therefore overrides it — handy for a project that lives under `~/Documents`, a debug box that wants `readOnly: ['/']` to roam the disk, or a **root box that wants `readWrite: ['/']`** to read *and write* anywhere on disk (the hard secret deny below still wins, so credentials and private keys stay protected even with a whole-disk RW grant).
|
|
134
139
|
- **Hard secret deny (always wins):** `denyDotConfigs` (`~/.aws`, `~/.gnupg`, `~/.kube`, `~/.docker`, `~/.config`) and personal SSH keys `~/.ssh/id_*`, `*.pem`, `*.key`, emitted as the **very last file rule** — after every allow, the extra paths and the project dir. No `readOnly`/`readWrite` grant can re-expose these. Only the bot key subdir (`bot.sshDir`, not matched by the patterns) stays readable. (The `~/.config/git` RO carve-out is shadowed by the hard deny on `~/.config`; git still reads `~/.gitconfig` outside `~/.config`.)
|
|
135
140
|
|
|
136
141
|
### Git/ssh bot identity
|
|
@@ -10,8 +10,12 @@ interface InitFile {
|
|
|
10
10
|
}
|
|
11
11
|
/** Absolute locations for the generated artifacts. */
|
|
12
12
|
interface AliasPaths {
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Dir holding the box config files, baked as `CLABOX_CONFIGS_DIR` so `-b`
|
|
15
|
+
* resolves boxes from any cwd, or null to omit it — when null, `-b` finds the
|
|
16
|
+
* box via the runtime default (`~/.config/clabox/configs`) at run time.
|
|
17
|
+
*/
|
|
18
|
+
configsDir: string | null;
|
|
15
19
|
/** Dir to emit `index.sh` and the per-box wrappers into. */
|
|
16
20
|
scriptsDir: string;
|
|
17
21
|
}
|
|
@@ -28,4 +32,4 @@ declare function buildWrapper(fnName: string, indexPath: string): string;
|
|
|
28
32
|
declare function buildAliasFiles(profiles: string[], paths: AliasPaths): InitFile[];
|
|
29
33
|
//#endregion
|
|
30
34
|
export { buildIndex as a, buildAliasFiles as i, InitFile as n, buildWrapper as o, aliasName as r, AliasPaths as t };
|
|
31
|
-
//# sourceMappingURL=aliases-
|
|
35
|
+
//# sourceMappingURL=aliases-BNdrOn9E.d.ts.map
|
|
@@ -20,7 +20,7 @@ function buildIndex(profiles, { configsDir, scriptsDir }) {
|
|
|
20
20
|
...usage,
|
|
21
21
|
"",
|
|
22
22
|
"_clabox_run() {",
|
|
23
|
-
` CLABOX_CONFIGS_DIR="${configsDir}" clabox -b "$1" "\${@:2}"`,
|
|
23
|
+
configsDir ? ` CLABOX_CONFIGS_DIR="${configsDir}" clabox -b "$1" "\${@:2}"` : ` clabox -b "$1" "\${@:2}"`,
|
|
24
24
|
"}",
|
|
25
25
|
"",
|
|
26
26
|
...defs
|
|
@@ -57,4 +57,4 @@ function buildAliasFiles(profiles, paths) {
|
|
|
57
57
|
//#endregion
|
|
58
58
|
export { buildWrapper as i, buildAliasFiles as n, buildIndex as r, aliasName as t };
|
|
59
59
|
|
|
60
|
-
//# sourceMappingURL=aliases-
|
|
60
|
+
//# sourceMappingURL=aliases-KGjds7dK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aliases-KGjds7dK.js","names":[],"sources":["../src/init/aliases.ts"],"sourcesContent":["// Pure builder for the shell aliases emitted by `clabox init`.\n//\n// For each box config (`<name>.mjs` / `<name>.config.mjs`) it produces one\n// command `clabox-<name>` that runs that box. Whether it runs yolo or asks for\n// confirmation is decided by the box's own preset (its `claudeArgs`), not here.\n// Plus an `index.sh` defining them all as shell functions to `source`.\n\nimport path from 'node:path';\n\n/** A file to be written by `clabox init`. */\nexport interface InitFile {\n /** Absolute path to write. */\n path: string;\n /** File contents. */\n content: string;\n /** Whether to `chmod +x` after writing. */\n executable: boolean;\n}\n\n/** Absolute locations for the generated artifacts. */\nexport interface AliasPaths {\n /**\n * Dir holding the box config files, baked as `CLABOX_CONFIGS_DIR` so `-b`\n * resolves boxes from any cwd, or null to omit it — when null, `-b` finds the\n * box via the runtime default (`~/.config/clabox/configs`) at run time.\n */\n configsDir: string | null;\n /** Dir to emit `index.sh` and the per-box wrappers into. */\n scriptsDir: string;\n}\n\n/** The `clabox-<name>` command name for a box. */\nexport function aliasName(profile: string): string {\n return `clabox-${profile}`;\n}\n\n/** Build the source-able `index.sh` defining one function per box. */\nexport function buildIndex(profiles: string[], { configsDir, scriptsDir }: AliasPaths): string {\n const indexPath = path.join(scriptsDir, 'index.sh');\n const usage = profiles.map((p) => `# ${aliasName(p)}`);\n const defs = profiles.map((p) => `${aliasName(p)}() { _clabox_run ${p} \"$@\"; }`);\n return `${[\n '#!/usr/bin/env bash',\n '# Generated by `clabox init` — do not edit; rerun it after changing the configs dir.',\n '#',\n '# Source this from ~/.zshrc or ~/.bashrc:',\n `# source ${indexPath}`,\n '#',\n \"# Commands (run from any cwd). yolo vs. safe is set by each box's preset:\",\n ...usage,\n '',\n '_clabox_run() {',\n // Point `-b` at this dir so resolveBox picks the right .mjs/.config.mjs file\n // — unless it's the runtime default, then let `-b` resolve it at run time.\n configsDir\n ? ` CLABOX_CONFIGS_DIR=\"${configsDir}\" clabox -b \"$1\" \"\\${@:2}\"`\n : ` clabox -b \"$1\" \"\\${@:2}\"`,\n '}',\n '',\n ...defs,\n ].join('\\n')}\\n`;\n}\n\n/** Build a standalone wrapper script that sources `index.sh` and calls one fn. */\nexport function buildWrapper(fnName: string, indexPath: string): string {\n return `${[\n '#!/usr/bin/env bash',\n '# Generated by `clabox init`.',\n 'set -euo pipefail',\n `source \"${indexPath}\"`,\n `${fnName} \"$@\"`,\n ].join('\\n')}\\n`;\n}\n\n/** All files `clabox init` writes: the `index.sh` plus a wrapper per box. */\nexport function buildAliasFiles(profiles: string[], paths: AliasPaths): InitFile[] {\n const indexPath = path.join(paths.scriptsDir, 'index.sh');\n const files: InitFile[] = [\n { path: indexPath, content: buildIndex(profiles, paths), executable: true },\n ];\n for (const p of profiles) {\n const fn = aliasName(p);\n files.push({\n path: path.join(paths.scriptsDir, `${fn}.sh`),\n content: buildWrapper(fn, indexPath),\n executable: true,\n });\n }\n return files;\n}\n"],"mappings":";;;AAgCA,SAAgB,UAAU,SAAyB;CACjD,OAAO,UAAU;AACnB;;AAGA,SAAgB,WAAW,UAAoB,EAAE,YAAY,cAAkC;CAC7F,MAAM,YAAY,KAAK,KAAK,YAAY,UAAU;CAClD,MAAM,QAAQ,SAAS,KAAK,MAAM,OAAO,UAAU,CAAC,GAAG;CACvD,MAAM,OAAO,SAAS,KAAK,MAAM,GAAG,UAAU,CAAC,EAAE,mBAAmB,EAAE,SAAS;CAC/E,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACA,GAAG;EACH;EACA;EAGA,aACI,yBAAyB,WAAW,8BACpC;EACJ;EACA;EACA,GAAG;CACL,CAAC,CAAC,KAAK,IAAI,EAAE;AACf;;AAGA,SAAgB,aAAa,QAAgB,WAA2B;CACtE,OAAO,GAAG;EACR;EACA;EACA;EACA,WAAW,UAAU;EACrB,GAAG,OAAO;CACZ,CAAC,CAAC,KAAK,IAAI,EAAE;AACf;;AAGA,SAAgB,gBAAgB,UAAoB,OAA+B;CACjF,MAAM,YAAY,KAAK,KAAK,MAAM,YAAY,UAAU;CACxD,MAAM,QAAoB,CACxB;EAAE,MAAM;EAAW,SAAS,WAAW,UAAU,KAAK;EAAG,YAAY;CAAK,CAC5E;CACA,KAAK,MAAM,KAAK,UAAU;EACxB,MAAM,KAAK,UAAU,CAAC;EACtB,MAAM,KAAK;GACT,MAAM,KAAK,KAAK,MAAM,YAAY,GAAG,GAAG,IAAI;GAC5C,SAAS,aAAa,IAAI,SAAS;GACnC,YAAY;EACd,CAAC;CACH;CACA,OAAO;AACT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AppConfig, t as AppBuilderConfig } from "./config-
|
|
1
|
+
import { n as AppConfig, t as AppBuilderConfig } from "./config-jZAB2Zg8.js";
|
|
2
2
|
|
|
3
3
|
//#region src/init/app.d.ts
|
|
4
4
|
/** Inputs for {@link buildApp}. */
|
|
@@ -29,4 +29,4 @@ declare function installIcon(app: AppConfig, appPath: string, tmpDir: string): v
|
|
|
29
29
|
declare function buildApp(opts: BuildAppOptions): BuildAppResult;
|
|
30
30
|
//#endregion
|
|
31
31
|
export { installIcon as a, canBuildApps as i, BuildAppResult as n, buildApp as r, BuildAppOptions as t };
|
|
32
|
-
//# sourceMappingURL=app-
|
|
32
|
+
//# sourceMappingURL=app-B1djcEnN.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as expandHome } from "./config-BQ44iVWT.js";
|
|
2
|
-
import { a as bundleId, i as buildLauncherSource, t as appBundlePath } from "./ghostty-
|
|
2
|
+
import { a as bundleId, i as buildLauncherSource, t as appBundlePath } from "./ghostty-Dw4QLyl-.js";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
4
|
import fs from "node:fs";
|
|
6
5
|
import os from "node:os";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
7
|
//#region src/init/app.ts
|
|
8
8
|
function run(cmd, args) {
|
|
9
9
|
execFileSync(cmd, args, { stdio: [
|
|
@@ -251,4 +251,4 @@ function buildApp(opts) {
|
|
|
251
251
|
//#endregion
|
|
252
252
|
export { canBuildApps as n, installIcon as r, buildApp as t };
|
|
253
253
|
|
|
254
|
-
//# sourceMappingURL=app-
|
|
254
|
+
//# sourceMappingURL=app-ChnKbgkD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-CQmESEdh.js","names":[],"sources":["../src/init/app.ts"],"sourcesContent":["// I/O for the `clabox init` Ghostty-app builder (macOS-only).\n//\n// Clones Ghostty.app into `<appsDir>/<name>.app`, swaps the binary for a tiny\n// compiled launcher that bakes in `--config-file=<config>`, sets the icon,\n// disables Sparkle, and re-signs. Mirrors the old ghostty-app-builder.sh. The\n// pure text builders live in init/ghostty.ts.\n\nimport { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { type AppBuilderConfig, type AppConfig, expandHome } from '../utils/config.js';\nimport { appBundlePath, buildLauncherSource, bundleId } from './ghostty.js';\n\n/** Inputs for {@link buildApp}. */\nexport interface BuildAppOptions {\n /** The `-b` box name (drives the default bundle id). */\n boxName: string;\n app: AppConfig;\n builder: AppBuilderConfig;\n /** Absolute path to the already-written Ghostty config to bake in. */\n configPath: string;\n}\n\n/** Result of a successful {@link buildApp}. */\nexport interface BuildAppResult {\n appPath: string;\n signed: 'identity' | 'adhoc';\n}\n\nfunction run(cmd: string, args: string[]): void {\n execFileSync(cmd, args, { stdio: ['ignore', 'ignore', 'pipe'] });\n}\n\nfunction has(bin: string): boolean {\n try {\n execFileSync('command', ['-v', bin], { shell: '/bin/sh', stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n/** True when the host can build apps (macOS with the donor app + a C compiler). */\nexport function canBuildApps(builder: AppBuilderConfig): { ok: boolean; reason?: string } {\n if (process.platform !== 'darwin') return { ok: false, reason: 'not macOS' };\n if (!fs.existsSync(expandHome(builder.ghosttyApp))) {\n return { ok: false, reason: `Ghostty not found at ${builder.ghosttyApp}` };\n }\n if (!has('cc')) return { ok: false, reason: 'no C compiler (cc) — install Xcode CLT' };\n return { ok: true };\n}\n\n/** Extract the donor app's entitlements to a tmp file, or null if it has none. */\nfunction extractEntitlements(ghosttyApp: string, tmpDir: string): string | null {\n let xml: string;\n try {\n xml = execFileSync('codesign', ['-d', '--entitlements', '-', '--xml', ghosttyApp], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n } catch {\n return null;\n }\n const start = xml.indexOf('<?xml');\n if (start < 0) return null;\n const file = path.join(tmpDir, 'entitlements.xml');\n fs.writeFileSync(file, xml.slice(start));\n return file;\n}\n\n/** Name of the icon resource referenced by the bundle (default Ghostty.icns). */\nfunction iconResourceName(plist: string): string {\n try {\n const name = execFileSync('plutil', ['-extract', 'CFBundleIconFile', 'raw', plist], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return name.endsWith('.icns') ? name : `${name}.icns`;\n } catch {\n return 'Ghostty.icns';\n }\n}\n\n/** Convert a PNG into a multi-resolution .icns at `out`. */\nfunction pngToIcns(png: string, out: string, tmpDir: string): void {\n const iconset = path.join(tmpDir, 'icon.iconset');\n fs.mkdirSync(iconset, { recursive: true });\n for (const size of [16, 32, 128, 256, 512]) {\n run('sips', [\n '-z',\n `${size}`,\n `${size}`,\n png,\n '--out',\n path.join(iconset, `icon_${size}x${size}.png`),\n ]);\n const d = size * 2;\n run('sips', [\n '-z',\n `${d}`,\n `${d}`,\n png,\n '--out',\n path.join(iconset, `icon_${size}x${size}@2x.png`),\n ]);\n }\n run('iconutil', ['-c', 'icns', iconset, '-o', out]);\n}\n\n/** Install the box icon into the cloned bundle, if `app.icon` is set. */\nexport function installIcon(app: AppConfig, appPath: string, tmpDir: string): void {\n if (!app.icon) return;\n const icon = expandHome(app.icon);\n if (!fs.existsSync(icon)) throw new Error(`icon not found: ${app.icon}`);\n const plist = path.join(appPath, 'Contents', 'Info.plist');\n const dest = path.join(appPath, 'Contents', 'Resources', iconResourceName(plist));\n if (icon.endsWith('.icns')) fs.copyFileSync(icon, dest);\n else if (icon.endsWith('.png')) pngToIcns(icon, dest, tmpDir);\n else throw new Error(`unsupported icon type (need .icns/.png): ${app.icon}`);\n\n // Ghostty ships a compiled asset catalog (Assets.car) and a `CFBundleIconName`\n // pointing into it, which macOS prefers over the loose `CFBundleIconFile`\n // .icns we just replaced — so our icon would be ignored. Drop the asset-catalog\n // reference so macOS falls back to the .icns. (May be absent on other donors.)\n try {\n run('plutil', ['-remove', 'CFBundleIconName', plist]);\n } catch {\n // donor app may not define CFBundleIconName — ignore\n }\n}\n\n/**\n * Build the standalone Ghostty app for a box. Throws on any failure (the caller\n * decides whether to abort or carry on with the other boxes).\n */\nexport function buildApp(opts: BuildAppOptions): BuildAppResult {\n const { app, builder, boxName, configPath } = opts;\n const check = canBuildApps(builder);\n if (!check.ok) throw new Error(`cannot build app: ${check.reason}`);\n\n const ghosttyApp = expandHome(builder.ghosttyApp);\n const appsDir = expandHome(builder.appsDir);\n const appPath = appBundlePath(appsDir, app);\n const plist = path.join(appPath, 'Contents', 'Info.plist');\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'clabox-app-'));\n\n try {\n const entitlements = extractEntitlements(ghosttyApp, tmpDir);\n\n // Full clone (cp -R keeps bundle symlinks/frameworks intact).\n fs.mkdirSync(appsDir, { recursive: true });\n fs.rmSync(appPath, { recursive: true, force: true });\n run('cp', ['-R', ghosttyApp, appPath]);\n\n // Identity.\n run('plutil', ['-replace', 'CFBundleIdentifier', '-string', bundleId(boxName, app), plist]);\n run('plutil', ['-replace', 'CFBundleName', '-string', app.name, plist]);\n run('plutil', ['-replace', 'CFBundleDisplayName', '-string', app.name, plist]);\n run('plutil', ['-replace', 'CFBundleExecutable', '-string', 'ghostty', plist]);\n\n // Disable Sparkle auto-update (would clobber the clone).\n run('plutil', ['-replace', 'SUEnableAutomaticChecks', '-bool', 'NO', plist]);\n try {\n run('plutil', ['-replace', 'SUFeedURL', '-string', '', plist]);\n } catch {\n // donor app may not define SUFeedURL — ignore\n }\n\n // Swap the binary for a launcher that prepends --config-file.\n const bin = path.join(appPath, 'Contents', 'MacOS', 'ghostty');\n fs.renameSync(bin, `${bin}.real`);\n const src = path.join(tmpDir, 'launcher.c');\n fs.writeFileSync(src, buildLauncherSource(configPath));\n run('cc', ['-o', bin, src]);\n\n installIcon(app, appPath, tmpDir);\n\n // Re-sign: inner real binary first, then the whole bundle.\n const signId = builder.signId;\n const entArgs = entitlements ? ['--entitlements', entitlements] : [];\n const idArgs = signId ? ['--sign', signId] : ['--sign', '-'];\n run('codesign', ['--force', ...idArgs, ...entArgs, `${bin}.real`]);\n run('codesign', ['--force', '--deep', ...idArgs, ...entArgs, appPath]);\n\n return { appPath, signed: signId ? 'identity' : 'adhoc' };\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-ChnKbgkD.js","names":[],"sources":["../src/init/app.ts"],"sourcesContent":["// I/O for the `clabox init` Ghostty-app builder (macOS-only).\n//\n// Clones Ghostty.app into `<appsDir>/<name>.app`, swaps the binary for a tiny\n// compiled launcher that bakes in `--config-file=<config>`, sets the icon,\n// disables Sparkle, and re-signs. Mirrors the old ghostty-app-builder.sh. The\n// pure text builders live in init/ghostty.ts.\n\nimport { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { type AppBuilderConfig, type AppConfig, expandHome } from '../utils/config.js';\nimport { appBundlePath, buildLauncherSource, bundleId } from './ghostty.js';\n\n/** Inputs for {@link buildApp}. */\nexport interface BuildAppOptions {\n /** The `-b` box name (drives the default bundle id). */\n boxName: string;\n app: AppConfig;\n builder: AppBuilderConfig;\n /** Absolute path to the already-written Ghostty config to bake in. */\n configPath: string;\n}\n\n/** Result of a successful {@link buildApp}. */\nexport interface BuildAppResult {\n appPath: string;\n signed: 'identity' | 'adhoc';\n}\n\nfunction run(cmd: string, args: string[]): void {\n execFileSync(cmd, args, { stdio: ['ignore', 'ignore', 'pipe'] });\n}\n\nfunction has(bin: string): boolean {\n try {\n execFileSync('command', ['-v', bin], { shell: '/bin/sh', stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\n/** True when the host can build apps (macOS with the donor app + a C compiler). */\nexport function canBuildApps(builder: AppBuilderConfig): { ok: boolean; reason?: string } {\n if (process.platform !== 'darwin') return { ok: false, reason: 'not macOS' };\n if (!fs.existsSync(expandHome(builder.ghosttyApp))) {\n return { ok: false, reason: `Ghostty not found at ${builder.ghosttyApp}` };\n }\n if (!has('cc')) return { ok: false, reason: 'no C compiler (cc) — install Xcode CLT' };\n return { ok: true };\n}\n\n/** Extract the donor app's entitlements to a tmp file, or null if it has none. */\nfunction extractEntitlements(ghosttyApp: string, tmpDir: string): string | null {\n let xml: string;\n try {\n xml = execFileSync('codesign', ['-d', '--entitlements', '-', '--xml', ghosttyApp], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n } catch {\n return null;\n }\n const start = xml.indexOf('<?xml');\n if (start < 0) return null;\n const file = path.join(tmpDir, 'entitlements.xml');\n fs.writeFileSync(file, xml.slice(start));\n return file;\n}\n\n/** Name of the icon resource referenced by the bundle (default Ghostty.icns). */\nfunction iconResourceName(plist: string): string {\n try {\n const name = execFileSync('plutil', ['-extract', 'CFBundleIconFile', 'raw', plist], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return name.endsWith('.icns') ? name : `${name}.icns`;\n } catch {\n return 'Ghostty.icns';\n }\n}\n\n/** Convert a PNG into a multi-resolution .icns at `out`. */\nfunction pngToIcns(png: string, out: string, tmpDir: string): void {\n const iconset = path.join(tmpDir, 'icon.iconset');\n fs.mkdirSync(iconset, { recursive: true });\n for (const size of [16, 32, 128, 256, 512]) {\n run('sips', [\n '-z',\n `${size}`,\n `${size}`,\n png,\n '--out',\n path.join(iconset, `icon_${size}x${size}.png`),\n ]);\n const d = size * 2;\n run('sips', [\n '-z',\n `${d}`,\n `${d}`,\n png,\n '--out',\n path.join(iconset, `icon_${size}x${size}@2x.png`),\n ]);\n }\n run('iconutil', ['-c', 'icns', iconset, '-o', out]);\n}\n\n/** Install the box icon into the cloned bundle, if `app.icon` is set. */\nexport function installIcon(app: AppConfig, appPath: string, tmpDir: string): void {\n if (!app.icon) return;\n const icon = expandHome(app.icon);\n if (!fs.existsSync(icon)) throw new Error(`icon not found: ${app.icon}`);\n const plist = path.join(appPath, 'Contents', 'Info.plist');\n const dest = path.join(appPath, 'Contents', 'Resources', iconResourceName(plist));\n if (icon.endsWith('.icns')) fs.copyFileSync(icon, dest);\n else if (icon.endsWith('.png')) pngToIcns(icon, dest, tmpDir);\n else throw new Error(`unsupported icon type (need .icns/.png): ${app.icon}`);\n\n // Ghostty ships a compiled asset catalog (Assets.car) and a `CFBundleIconName`\n // pointing into it, which macOS prefers over the loose `CFBundleIconFile`\n // .icns we just replaced — so our icon would be ignored. Drop the asset-catalog\n // reference so macOS falls back to the .icns. (May be absent on other donors.)\n try {\n run('plutil', ['-remove', 'CFBundleIconName', plist]);\n } catch {\n // donor app may not define CFBundleIconName — ignore\n }\n}\n\n/**\n * Build the standalone Ghostty app for a box. Throws on any failure (the caller\n * decides whether to abort or carry on with the other boxes).\n */\nexport function buildApp(opts: BuildAppOptions): BuildAppResult {\n const { app, builder, boxName, configPath } = opts;\n const check = canBuildApps(builder);\n if (!check.ok) throw new Error(`cannot build app: ${check.reason}`);\n\n const ghosttyApp = expandHome(builder.ghosttyApp);\n const appsDir = expandHome(builder.appsDir);\n const appPath = appBundlePath(appsDir, app);\n const plist = path.join(appPath, 'Contents', 'Info.plist');\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'clabox-app-'));\n\n try {\n const entitlements = extractEntitlements(ghosttyApp, tmpDir);\n\n // Full clone (cp -R keeps bundle symlinks/frameworks intact).\n fs.mkdirSync(appsDir, { recursive: true });\n fs.rmSync(appPath, { recursive: true, force: true });\n run('cp', ['-R', ghosttyApp, appPath]);\n\n // Identity.\n run('plutil', ['-replace', 'CFBundleIdentifier', '-string', bundleId(boxName, app), plist]);\n run('plutil', ['-replace', 'CFBundleName', '-string', app.name, plist]);\n run('plutil', ['-replace', 'CFBundleDisplayName', '-string', app.name, plist]);\n run('plutil', ['-replace', 'CFBundleExecutable', '-string', 'ghostty', plist]);\n\n // Disable Sparkle auto-update (would clobber the clone).\n run('plutil', ['-replace', 'SUEnableAutomaticChecks', '-bool', 'NO', plist]);\n try {\n run('plutil', ['-replace', 'SUFeedURL', '-string', '', plist]);\n } catch {\n // donor app may not define SUFeedURL — ignore\n }\n\n // Swap the binary for a launcher that prepends --config-file.\n const bin = path.join(appPath, 'Contents', 'MacOS', 'ghostty');\n fs.renameSync(bin, `${bin}.real`);\n const src = path.join(tmpDir, 'launcher.c');\n fs.writeFileSync(src, buildLauncherSource(configPath));\n run('cc', ['-o', bin, src]);\n\n installIcon(app, appPath, tmpDir);\n\n // Re-sign: inner real binary first, then the whole bundle.\n const signId = builder.signId;\n const entArgs = entitlements ? ['--entitlements', entitlements] : [];\n const idArgs = signId ? ['--sign', signId] : ['--sign', '-'];\n run('codesign', ['--force', ...idArgs, ...entArgs, `${bin}.real`]);\n run('codesign', ['--force', '--deep', ...idArgs, ...entArgs, appPath]);\n\n return { appPath, signed: signId ? 'identity' : 'adhoc' };\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n}\n"],"mappings":"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"}
|
package/lib/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { l as resolveBox, s as loadConfig } from "./config-BQ44iVWT.js";
|
|
3
|
-
import { n as runInit } from "./scaffold-
|
|
4
|
-
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath } from "./run-
|
|
3
|
+
import { n as runInit } from "./scaffold-C1tqCL_8.js";
|
|
4
|
+
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath } from "./run-yUsOaKFx.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import yargs from "yargs";
|
|
7
7
|
import { hideBin } from "yargs/helpers";
|
|
@@ -43,13 +43,14 @@ await yargs(hideBin(process.argv)).scriptName("clabox").parserConfiguration({ "u
|
|
|
43
43
|
type: "string",
|
|
44
44
|
describe: "Build only this app box (by box name or app display name)"
|
|
45
45
|
}), async (argv) => {
|
|
46
|
-
const { profiles, indexFile, written, apps, raycastCommands, warnings } = await runInit({
|
|
46
|
+
const { profiles, indexFile, written, apps, raycastCommands, extraFiles, warnings } = await runInit({
|
|
47
47
|
baseDir: argv.dir,
|
|
48
48
|
buildApps: argv.apps,
|
|
49
49
|
only: argv.app ?? null
|
|
50
50
|
});
|
|
51
51
|
console.log(`clabox init: ${profiles.length} profile(s) → ${profiles.join(", ")}`);
|
|
52
52
|
for (const f of written) console.log(` ${path.basename(f)}`);
|
|
53
|
+
for (const f of extraFiles) console.log(` 🔌 ${f}`);
|
|
53
54
|
for (const a of apps) console.log(` 📦 ${a.appPath} (${a.signed})`);
|
|
54
55
|
for (const r of raycastCommands) console.log(` 🚀 ${r}`);
|
|
55
56
|
for (const w of warnings) console.warn(` ⚠️ ${w}`);
|
package/lib/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// clabox — run Claude Code in a sandbox for super-safe YOLO mode.\n// SPDX-License-Identifier: MIT\n//\n// Configure in plain JS: clabox.config.mjs (CWD) or\n// ~/.config/clabox/config.mjs. See clabox.config.example.mjs.\n\nimport path from 'node:path';\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { runInit } from './init/scaffold.js';\nimport { generateProfile, profilePath, resolveProjectDir, runClaude } from './sandbox/run.js';\nimport { loadConfig, resolveBox } from './utils/config.js';\n\n/** Pick the explicit config path: a `--box <name>` wins over `--config <path>`. */\n// Index signature so any yargs argv (incl. commands with an empty builder)\n// is assignable — `box`/`config` are global options, present on every command.\nfunction explicitConfig(argv: {\n box?: unknown;\n config?: unknown;\n [k: string]: unknown;\n}): string | undefined {\n if (argv.box) return resolveBox(argv.box as string);\n return (argv.config as string | undefined) ?? undefined;\n}\n\nawait yargs(hideBin(process.argv))\n .scriptName('clabox')\n // Keep unknown flags (e.g. --dangerously-skip-permissions) as positionals so\n // they pass straight through to claude instead of erroring out.\n .parserConfiguration({ 'unknown-options-as-args': true })\n // clabox-owned flag: a config-file path that wins over CLABOX_CONFIG.\n .option('config', {\n type: 'string',\n describe: 'Path to a JS config file (overrides CLABOX_CONFIG)',\n })\n // clabox-owned flag: run a named config from the global configs dir.\n // (`-p` is left for claude's --print; `-c/-r/-d/-v` are claude flags too.)\n .option('box', {\n alias: 'b',\n type: 'string',\n describe: 'Run a named config from ~/.config/clabox/configs/<name>.config.mjs',\n })\n .command(\n ['run [claudeArgs..]', '$0 [claudeArgs..]'],\n 'Generate the profile and run claude inside the sandbox (default)',\n (y) =>\n y.positional('claudeArgs', {\n describe: 'Arguments passed through to claude',\n array: true,\n default: [] as string[],\n }),\n async (argv) => {\n const { config, configFile } = await loadConfig(explicitConfig(argv));\n const claudeArgs = (argv.claudeArgs ?? []) as string[];\n const code = runClaude(config, claudeArgs, { configFile });\n process.exit(code);\n },\n )\n .command(\n 'generate',\n 'Build the sandbox profile only and print its path',\n (y) => y,\n async (argv) => {\n const { config } = await loadConfig(explicitConfig(argv));\n console.log(generateProfile(config));\n },\n )\n .command(\n 'profile',\n 'Print the sandbox profile path (no build)',\n (y) => y,\n async (argv) => {\n const { config } = await loadConfig(explicitConfig(argv));\n console.log(profilePath(resolveProjectDir(config)));\n },\n )\n .command(\n 'init',\n 'Generate clabox-<name> shell aliases and build Ghostty apps for `app` boxes',\n (y) =>\n y\n .option('dir', {\n type: 'string',\n describe: 'Base dir holding configs/ and scripts/ (default: ./__)',\n })\n .option('apps', {\n type: 'boolean',\n default: true,\n describe: 'Build Ghostty apps for `app` boxes (use --no-apps to skip)',\n })\n .option('app', {\n type: 'string',\n describe: 'Build only this app box (by box name or app display name)',\n }),\n async (argv) => {\n const { profiles, indexFile, written, apps, raycastCommands, warnings }
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// clabox — run Claude Code in a sandbox for super-safe YOLO mode.\n// SPDX-License-Identifier: MIT\n//\n// Configure in plain JS: clabox.config.mjs (CWD) or\n// ~/.config/clabox/config.mjs. See clabox.config.example.mjs.\n\nimport path from 'node:path';\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { runInit } from './init/scaffold.js';\nimport { generateProfile, profilePath, resolveProjectDir, runClaude } from './sandbox/run.js';\nimport { loadConfig, resolveBox } from './utils/config.js';\n\n/** Pick the explicit config path: a `--box <name>` wins over `--config <path>`. */\n// Index signature so any yargs argv (incl. commands with an empty builder)\n// is assignable — `box`/`config` are global options, present on every command.\nfunction explicitConfig(argv: {\n box?: unknown;\n config?: unknown;\n [k: string]: unknown;\n}): string | undefined {\n if (argv.box) return resolveBox(argv.box as string);\n return (argv.config as string | undefined) ?? undefined;\n}\n\nawait yargs(hideBin(process.argv))\n .scriptName('clabox')\n // Keep unknown flags (e.g. --dangerously-skip-permissions) as positionals so\n // they pass straight through to claude instead of erroring out.\n .parserConfiguration({ 'unknown-options-as-args': true })\n // clabox-owned flag: a config-file path that wins over CLABOX_CONFIG.\n .option('config', {\n type: 'string',\n describe: 'Path to a JS config file (overrides CLABOX_CONFIG)',\n })\n // clabox-owned flag: run a named config from the global configs dir.\n // (`-p` is left for claude's --print; `-c/-r/-d/-v` are claude flags too.)\n .option('box', {\n alias: 'b',\n type: 'string',\n describe: 'Run a named config from ~/.config/clabox/configs/<name>.config.mjs',\n })\n .command(\n ['run [claudeArgs..]', '$0 [claudeArgs..]'],\n 'Generate the profile and run claude inside the sandbox (default)',\n (y) =>\n y.positional('claudeArgs', {\n describe: 'Arguments passed through to claude',\n array: true,\n default: [] as string[],\n }),\n async (argv) => {\n const { config, configFile } = await loadConfig(explicitConfig(argv));\n const claudeArgs = (argv.claudeArgs ?? []) as string[];\n const code = runClaude(config, claudeArgs, { configFile });\n process.exit(code);\n },\n )\n .command(\n 'generate',\n 'Build the sandbox profile only and print its path',\n (y) => y,\n async (argv) => {\n const { config } = await loadConfig(explicitConfig(argv));\n console.log(generateProfile(config));\n },\n )\n .command(\n 'profile',\n 'Print the sandbox profile path (no build)',\n (y) => y,\n async (argv) => {\n const { config } = await loadConfig(explicitConfig(argv));\n console.log(profilePath(resolveProjectDir(config)));\n },\n )\n .command(\n 'init',\n 'Generate clabox-<name> shell aliases and build Ghostty apps for `app` boxes',\n (y) =>\n y\n .option('dir', {\n type: 'string',\n describe: 'Base dir holding configs/ and scripts/ (default: ./__)',\n })\n .option('apps', {\n type: 'boolean',\n default: true,\n describe: 'Build Ghostty apps for `app` boxes (use --no-apps to skip)',\n })\n .option('app', {\n type: 'string',\n describe: 'Build only this app box (by box name or app display name)',\n }),\n async (argv) => {\n const { profiles, indexFile, written, apps, raycastCommands, extraFiles, warnings } =\n await runInit({\n baseDir: argv.dir as string | undefined,\n buildApps: argv.apps as boolean,\n only: (argv.app as string | undefined) ?? null,\n });\n console.log(`clabox init: ${profiles.length} profile(s) → ${profiles.join(', ')}`);\n for (const f of written) console.log(` ${path.basename(f)}`);\n for (const f of extraFiles) console.log(` 🔌 ${f}`);\n for (const a of apps) console.log(` 📦 ${a.appPath} (${a.signed})`);\n for (const r of raycastCommands) console.log(` 🚀 ${r}`);\n for (const w of warnings) console.warn(` ⚠️ ${w}`);\n console.log(`\\nAdd to ~/.zshrc: source ${indexFile}`);\n if (raycastCommands.length > 0) {\n console.log(`Add to Raycast (Script Commands dir): ${path.dirname(raycastCommands[0])}`);\n }\n },\n )\n .example('$0 run --dangerously-skip-permissions', 'YOLO mode inside the sandbox')\n .example('$0 -b ax-root', 'Run the ~/.config/clabox/configs/ax-root.config.mjs box')\n .example('$0 init', 'Generate shell aliases from __/configs/*.config.mjs')\n .example('$0 --config ./my.clabox.mjs run', 'Use a specific JS config file')\n .example('CLAUDE_CONFIG_DIR=~/.claude_work $0 run', 'Use a different Claude profile')\n .epilogue(\n [\n 'Config (later wins): defaults -> env vars -> JS config file.',\n 'File: ./clabox.config.mjs or ~/.config/clabox/config.mjs',\n '(or --config /path, or CLABOX_CONFIG=/path).',\n 'Named boxes: -b <name> -> ~/.config/clabox/configs/<name>.config.mjs',\n '(dir overridable via CLABOX_CONFIGS_DIR).',\n ].join('\\n'),\n )\n .version(false)\n .help()\n .alias('h', 'help')\n .fail((msg, err) => {\n console.error(`Error: ${err?.message ?? msg}`);\n process.exit(1);\n })\n .parseAsync();\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-BQ44iVWT.js","names":[],"sources":["../src/utils/config.ts"],"sourcesContent":["// Configuration: sane defaults, env overrides, and an optional JS config file.\n//\n// Resolution order (later wins):\n// 1. defaultConfig (below)\n// 2. env vars (CLAUDE_CONFIG_DIR, CLABOX_*, …)\n// 3. a JS config file (see loadConfig)\n//\n// A config file default-exports either a plain object (merged over the defaults)\n// or a function `(defaults) => config` for full programmatic control.\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport const HOME = os.homedir();\n\n/** Expand a leading `~` / `~/` to the user's home directory. */\nexport function expandHome(p: string): string {\n if (!p) return p;\n if (p === '~') return HOME;\n if (p.startsWith('~/')) return path.join(HOME, p.slice(2));\n return p;\n}\n\nconst env = process.env;\n\n/** Dedicated git/ssh identity for commits made from inside the sandbox. */\nexport interface BotConfig {\n name: string;\n email: string;\n /** If `${sshDir}/id_ed25519` exists, git ssh is pinned to it. */\n sshDir: string;\n}\n\n/**\n * Opt-in marker that turns a box into a standalone Ghostty app. When a box\n * config carries an `app`, `clabox init` generates a Ghostty config for it and\n * builds a cloned `<appsDir>/<name>.app` that launches `clabox -b <box>`.\n */\nexport interface AppConfig {\n /** App display name → `<appsDir>/<name>.app` + CFBundleName. */\n name: string;\n /** Ghostty window title. Defaults to {@link AppConfig.name}. */\n title?: string;\n /** Emoji for the generated Raycast command. Default: the title's leading emoji. */\n emoji?: string;\n /** Path to a `.icns`/`.png` icon for the .app. `.png` is converted. `~` ok. */\n icon?: string;\n /** Ghostty built-in `macos-icon` (e.g. `retro`, `holographic`). */\n macosIcon?: string;\n /** Extra raw `key = value` lines appended to the generated Ghostty config. */\n ghostty?: Record<string, string>;\n /** Bundle id. Default: `com.ghostty.custom.<name dot-joined>`. */\n bundleId?: string;\n}\n\n/** Machine-wide settings for the `clabox init` Ghostty-app builder. */\nexport interface AppBuilderConfig {\n /** Donor app to clone. `~` is expanded. */\n ghosttyApp: string;\n /** Where built apps land. `~` is expanded. */\n appsDir: string;\n /** codesign identity. null → ad-hoc (`codesign -s -`). */\n signId: string | null;\n /** Optional base Ghostty config emitted as a leading `config-file = …`. */\n baseGhosttyConfig: string | null;\n /** `clabox` binary baked into the generated `command`. null → autodetect. */\n claboxBin: string | null;\n}\n\n/** Extra rules layered on top of the built-in base profile. */\nexport interface PathRules {\n /** RW subpaths (beyond project dir + configDir + /tmp). */\n readWrite: string[];\n /** RO subpaths. */\n readOnly: string[];\n /** process-exec subpaths. */\n exec: string[];\n /** explicit deny subpaths (read + write). */\n deny: string[];\n}\n\n/** Effective clabox configuration. */\nexport interface Config {\n /**\n * Working directory to run `claude` in (and grant RW as the project dir).\n * null → the shell's CWD. Handy for named boxes that always target one\n * project regardless of where `clabox` is invoked from. `~` is expanded.\n */\n cwd: string | null;\n /** Path to the `claude` binary. null → autodetect (PATH, then ~/.local/bin). */\n claudeBin: string | null;\n /** Claude config/profile directory — supports multiple accounts. */\n configDir: string;\n /** Extra args always passed to `claude`, before any args from the CLI. */\n claudeArgs: string[];\n bot: BotConfig;\n /**\n * Extra environment variables forced onto the sandboxed `claude` process,\n * layered over the inherited shell env and after the built-in hardening vars\n * (so a key set here wins). Use it to pass secrets like `GITHUB_TOKEN`.\n */\n env: Record<string, string>;\n /** Allow outbound network. `false` → no `(allow network*)` line. */\n network: boolean;\n /** Cap the process table inside the sandbox (fork-bomb guard). 0 → skip. */\n ulimitProcs: number;\n /** Extra directory granted read + execute inside the sandbox. null → disabled. */\n hooksDir: string | null;\n paths: PathRules;\n /** Home subdirectories denied entirely (read + write). */\n denyHome: string[];\n /** Dotfile config dirs under $HOME denied entirely. */\n denyDotConfigs: string[];\n /**\n * Opt-in: build a standalone Ghostty app for this box during `clabox init`.\n * Absent → the box only gets a shell alias (the default).\n */\n app?: AppConfig;\n /** Machine-wide settings for the `clabox init` Ghostty-app builder. */\n appBuilder: AppBuilderConfig;\n}\n\n/** Built-in defaults. Everything here is meant to be overridable. */\nexport const defaultConfig: Config = {\n cwd: env.CLABOX_CWD ?? null,\n claudeBin: env.CLABOX_CLAUDE_BIN ?? null,\n configDir: env.CLAUDE_CONFIG_DIR ?? '~/.claude',\n claudeArgs: ['--settings', '{\"includeCoAuthoredBy\": false}'],\n bot: {\n name: env.CLABOX_BOT_NAME ?? 'claudeBOT',\n email: env.CLABOX_BOT_EMAIL ?? 'bot@example.com',\n sshDir: env.CLABOX_BOT_SSH_DIR ?? '~/.ssh/claudebot',\n },\n env: {},\n network: true,\n ulimitProcs: 1024,\n hooksDir: env.CLABOX_HOOKS_DIR ?? null,\n paths: {\n readWrite: [],\n readOnly: [],\n exec: [],\n deny: [],\n },\n denyHome: ['Documents', 'Desktop', 'Downloads', 'Pictures', 'Movies', 'Music'],\n // `.config/git` is always carved back out for git RO config in the profile.\n denyDotConfigs: ['aws', 'gnupg', 'kube', 'docker', 'config'],\n // `app` is opt-in per box, so there's no default — it stays undefined.\n appBuilder: {\n ghosttyApp: env.CLABOX_GHOSTTY_APP ?? '/Applications/Ghostty.app',\n appsDir: env.CLABOX_APPS_DIR ?? '~/Applications',\n signId: env.CLABOX_SIGN_ID ?? null,\n baseGhosttyConfig: env.CLABOX_GHOSTTY_BASE_CONFIG ?? null,\n claboxBin: env.CLABOX_CLABOX_BIN ?? null,\n },\n};\n\ntype Plain = Record<string, unknown>;\n\nfunction isPlainObject(v: unknown): v is Plain {\n return v != null && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Shallow-deep merge: nested plain objects merge, everything else replaces. */\nfunction deepMerge(base: Plain, override: Plain): Plain {\n const out: Plain = { ...base };\n for (const [key, val] of Object.entries(override)) {\n if (val === undefined) continue;\n const baseVal = base[key];\n out[key] = isPlainObject(val) && isPlainObject(baseVal) ? deepMerge(baseVal, val) : val;\n }\n return out;\n}\n\n/** Merge a (partial) override over a full config, returning a new config. */\nexport function mergeConfig(base: Config, override: unknown): Config {\n if (!isPlainObject(override)) return base;\n return deepMerge(base as unknown as Plain, override) as unknown as Config;\n}\n\n/**\n * Locate a config file: explicit (CLI arg, then `CLABOX_CONFIG` env),\n * then CWD, then ~/.config. The CLI arg wins over the env var.\n */\nexport function findConfigFile(explicit?: string | null): string | null {\n const chosen = explicit ?? env.CLABOX_CONFIG;\n if (chosen) return expandHome(chosen);\n const candidates = [\n path.join(process.cwd(), 'clabox.config.mjs'),\n path.join(process.cwd(), 'clabox.config.js'),\n path.join(HOME, '.config', 'clabox', 'config.mjs'),\n ];\n return candidates.find((c) => fs.existsSync(c)) ?? null;\n}\n\n/**\n * Global directory holding named \"box\" configs (`<name>.config.mjs`), used by\n * the `clabox --box <name>` / `-b` flag. Override with `CLABOX_CONFIGS_DIR`.\n */\nexport function configsDir(): string {\n return expandHome(env.CLABOX_CONFIGS_DIR ?? '~/.config/clabox/configs');\n}\n\nconst BOX_SUFFIXES = ['.config.mjs', '.mjs'];\n\n/** Candidate file paths for a box name, in resolution order. */\nfunction boxCandidates(name: string, dir: string): string[] {\n return BOX_SUFFIXES.map((s) => path.join(dir, `${name}${s}`));\n}\n\n/** Named box configs (sorted, de-duplicated) available in {@link configsDir}. */\nexport function listBoxes(dir: string = configsDir()): string[] {\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n return [];\n }\n const names = new Set<string>();\n for (const f of entries) {\n // `_`-prefixed files are shared partials (e.g. `_presets.mjs`), not boxes.\n if (f.startsWith('_')) continue;\n const suffix = BOX_SUFFIXES.find((s) => f.endsWith(s));\n if (suffix) names.add(f.slice(0, -suffix.length));\n }\n return [...names].sort();\n}\n\n/**\n * Resolve a box name to its config file in {@link configsDir}, preferring\n * `<name>.config.mjs` over a bare `<name>.mjs`.\n *\n * @throws if no matching file exists (the message lists the available boxes).\n */\nexport function resolveBox(name: string, dir: string = configsDir()): string {\n // `_`-prefixed files are shared partials (e.g. `_presets.mjs`), not boxes —\n // keep them un-resolvable so `-b` matches what `listBoxes` advertises.\n if (!name.startsWith('_')) {\n const found = boxCandidates(name, dir).find((c) => fs.existsSync(c));\n if (found) return found;\n }\n const available = listBoxes(dir);\n const hint = available.length ? `available: ${available.join(', ')}` : `none found in ${dir}`;\n throw new Error(`clabox: box '${name}' not found in ${dir} (${hint})`);\n}\n\n/** Result of {@link loadConfig}: the effective config and the file it came from. */\nexport interface LoadedConfig {\n config: Config;\n configFile: string | null;\n}\n\n/**\n * Build the effective config: defaults ⊕ env ⊕ config file.\n *\n * @param explicitConfig optional config-file path (e.g. from `--config`);\n * takes precedence over `CLABOX_CONFIG` and the default lookup locations.\n */\nexport async function loadConfig(explicitConfig?: string | null): Promise<LoadedConfig> {\n let cfg: Config = defaultConfig;\n const file = findConfigFile(explicitConfig);\n if (file) {\n const mod = await import(pathToFileURL(file).href);\n const exported = mod.default ?? mod.config ?? mod;\n const resolved = typeof exported === 'function' ? await exported(defaultConfig) : exported;\n cfg = mergeConfig(defaultConfig, resolved);\n }\n return { config: cfg, configFile: file };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config-BQ44iVWT.js","names":[],"sources":["../src/utils/config.ts"],"sourcesContent":["// Configuration: sane defaults, env overrides, and an optional JS config file.\n//\n// Resolution order (later wins):\n// 1. defaultConfig (below)\n// 2. env vars (CLAUDE_CONFIG_DIR, CLABOX_*, …)\n// 3. a JS config file (see loadConfig)\n//\n// A config file default-exports either a plain object (merged over the defaults)\n// or a function `(defaults) => config` for full programmatic control.\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport const HOME = os.homedir();\n\n/** Expand a leading `~` / `~/` to the user's home directory. */\nexport function expandHome(p: string): string {\n if (!p) return p;\n if (p === '~') return HOME;\n if (p.startsWith('~/')) return path.join(HOME, p.slice(2));\n return p;\n}\n\nconst env = process.env;\n\n/** Dedicated git/ssh identity for commits made from inside the sandbox. */\nexport interface BotConfig {\n name: string;\n email: string;\n /** If `${sshDir}/id_ed25519` exists, git ssh is pinned to it. */\n sshDir: string;\n}\n\n/**\n * Opt-in marker that turns a box into a standalone Ghostty app. When a box\n * config carries an `app`, `clabox init` generates a Ghostty config for it and\n * builds a cloned `<appsDir>/<name>.app` that launches `clabox -b <box>`.\n */\nexport interface AppConfig {\n /** App display name → `<appsDir>/<name>.app` + CFBundleName. */\n name: string;\n /** Ghostty window title. Defaults to {@link AppConfig.name}. */\n title?: string;\n /** Emoji for the generated Raycast command. Default: the title's leading emoji. */\n emoji?: string;\n /** Path to a `.icns`/`.png` icon for the .app. `.png` is converted. `~` ok. */\n icon?: string;\n /** Ghostty built-in `macos-icon` (e.g. `retro`, `holographic`). */\n macosIcon?: string;\n /** Extra raw `key = value` lines appended to the generated Ghostty config. */\n ghostty?: Record<string, string>;\n /** Bundle id. Default: `com.ghostty.custom.<name dot-joined>`. */\n bundleId?: string;\n}\n\n/** Machine-wide settings for the `clabox init` Ghostty-app builder. */\nexport interface AppBuilderConfig {\n /** Donor app to clone. `~` is expanded. */\n ghosttyApp: string;\n /** Where built apps land. `~` is expanded. */\n appsDir: string;\n /** codesign identity. null → ad-hoc (`codesign -s -`). */\n signId: string | null;\n /** Optional base Ghostty config emitted as a leading `config-file = …`. */\n baseGhosttyConfig: string | null;\n /** `clabox` binary baked into the generated `command`. null → autodetect. */\n claboxBin: string | null;\n}\n\n/**\n * A single MCP server entry — the value under a key in claude's `mcpServers`\n * map. Loose on purpose (mirrors claude's mcp.json schema): `stdio` servers use\n * `command`/`args`/`env`, remote `http`/`sse` servers use `url`/`headers`.\n */\nexport interface McpServer {\n type?: 'stdio' | 'http' | 'sse';\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n url?: string;\n headers?: Record<string, string>;\n}\n\n/** Extra rules layered on top of the built-in base profile. */\nexport interface PathRules {\n /** RW subpaths (beyond project dir + configDir + /tmp). */\n readWrite: string[];\n /** RO subpaths. */\n readOnly: string[];\n /** process-exec subpaths. */\n exec: string[];\n /** explicit deny subpaths (read + write). */\n deny: string[];\n}\n\n/** Effective clabox configuration. */\nexport interface Config {\n /**\n * Working directory to run `claude` in (and grant RW as the project dir).\n * null → the shell's CWD. Handy for named boxes that always target one\n * project regardless of where `clabox` is invoked from. `~` is expanded.\n */\n cwd: string | null;\n /** Path to the `claude` binary. null → autodetect (PATH, then ~/.local/bin). */\n claudeBin: string | null;\n /** Claude config/profile directory — supports multiple accounts. */\n configDir: string;\n /** Extra args always passed to `claude`, before any args from the CLI. */\n claudeArgs: string[];\n /**\n * Per-box MCP servers (the `mcpServers` map). clabox compiles them to\n * `<configDir>/mcp/<slug>.json` and launches claude with\n * `--strict-mcp-config --mcp-config <file>`, so a shared configDir's global /\n * plugin MCP servers are ignored — each box gets exactly these and no more.\n * Materialized on every `run` and during `init`. Absent → no MCP flags.\n */\n mcp?: Record<string, McpServer>;\n /**\n * Text appended to claude's system prompt via `--append-system-prompt`.\n * `string[]` is joined with blank lines. Use it for per-box pre-prompts while\n * sharing one configDir (the user-level CLAUDE.md is shared; this is not).\n */\n systemPrompt?: string | string[];\n bot: BotConfig;\n /**\n * Extra environment variables forced onto the sandboxed `claude` process,\n * layered over the inherited shell env and after the built-in hardening vars\n * (so a key set here wins). Use it to pass secrets like `GITHUB_TOKEN`.\n */\n env: Record<string, string>;\n /** Allow outbound network. `false` → no `(allow network*)` line. */\n network: boolean;\n /** Cap the process table inside the sandbox (fork-bomb guard). 0 → skip. */\n ulimitProcs: number;\n /** Extra directory granted read + execute inside the sandbox. null → disabled. */\n hooksDir: string | null;\n paths: PathRules;\n /** Home subdirectories denied entirely (read + write). */\n denyHome: string[];\n /** Dotfile config dirs under $HOME denied entirely. */\n denyDotConfigs: string[];\n /**\n * Opt-in: build a standalone Ghostty app for this box during `clabox init`.\n * Absent → the box only gets a shell alias (the default).\n */\n app?: AppConfig;\n /** Machine-wide settings for the `clabox init` Ghostty-app builder. */\n appBuilder: AppBuilderConfig;\n}\n\n/** Built-in defaults. Everything here is meant to be overridable. */\nexport const defaultConfig: Config = {\n cwd: env.CLABOX_CWD ?? null,\n claudeBin: env.CLABOX_CLAUDE_BIN ?? null,\n configDir: env.CLAUDE_CONFIG_DIR ?? '~/.claude',\n claudeArgs: ['--settings', '{\"includeCoAuthoredBy\": false}'],\n bot: {\n name: env.CLABOX_BOT_NAME ?? 'claudeBOT',\n email: env.CLABOX_BOT_EMAIL ?? 'bot@example.com',\n sshDir: env.CLABOX_BOT_SSH_DIR ?? '~/.ssh/claudebot',\n },\n env: {},\n network: true,\n ulimitProcs: 1024,\n hooksDir: env.CLABOX_HOOKS_DIR ?? null,\n paths: {\n readWrite: [],\n readOnly: [],\n exec: [],\n deny: [],\n },\n denyHome: ['Documents', 'Desktop', 'Downloads', 'Pictures', 'Movies', 'Music'],\n // `.config/git` is always carved back out for git RO config in the profile.\n denyDotConfigs: ['aws', 'gnupg', 'kube', 'docker', 'config'],\n // `app` is opt-in per box, so there's no default — it stays undefined.\n appBuilder: {\n ghosttyApp: env.CLABOX_GHOSTTY_APP ?? '/Applications/Ghostty.app',\n appsDir: env.CLABOX_APPS_DIR ?? '~/Applications',\n signId: env.CLABOX_SIGN_ID ?? null,\n baseGhosttyConfig: env.CLABOX_GHOSTTY_BASE_CONFIG ?? null,\n claboxBin: env.CLABOX_CLABOX_BIN ?? null,\n },\n};\n\ntype Plain = Record<string, unknown>;\n\nfunction isPlainObject(v: unknown): v is Plain {\n return v != null && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Shallow-deep merge: nested plain objects merge, everything else replaces. */\nfunction deepMerge(base: Plain, override: Plain): Plain {\n const out: Plain = { ...base };\n for (const [key, val] of Object.entries(override)) {\n if (val === undefined) continue;\n const baseVal = base[key];\n out[key] = isPlainObject(val) && isPlainObject(baseVal) ? deepMerge(baseVal, val) : val;\n }\n return out;\n}\n\n/** Merge a (partial) override over a full config, returning a new config. */\nexport function mergeConfig(base: Config, override: unknown): Config {\n if (!isPlainObject(override)) return base;\n return deepMerge(base as unknown as Plain, override) as unknown as Config;\n}\n\n/**\n * Locate a config file: explicit (CLI arg, then `CLABOX_CONFIG` env),\n * then CWD, then ~/.config. The CLI arg wins over the env var.\n */\nexport function findConfigFile(explicit?: string | null): string | null {\n const chosen = explicit ?? env.CLABOX_CONFIG;\n if (chosen) return expandHome(chosen);\n const candidates = [\n path.join(process.cwd(), 'clabox.config.mjs'),\n path.join(process.cwd(), 'clabox.config.js'),\n path.join(HOME, '.config', 'clabox', 'config.mjs'),\n ];\n return candidates.find((c) => fs.existsSync(c)) ?? null;\n}\n\n/**\n * Global directory holding named \"box\" configs (`<name>.config.mjs`), used by\n * the `clabox --box <name>` / `-b` flag. Override with `CLABOX_CONFIGS_DIR`.\n */\nexport function configsDir(): string {\n return expandHome(env.CLABOX_CONFIGS_DIR ?? '~/.config/clabox/configs');\n}\n\nconst BOX_SUFFIXES = ['.config.mjs', '.mjs'];\n\n/** Candidate file paths for a box name, in resolution order. */\nfunction boxCandidates(name: string, dir: string): string[] {\n return BOX_SUFFIXES.map((s) => path.join(dir, `${name}${s}`));\n}\n\n/** Named box configs (sorted, de-duplicated) available in {@link configsDir}. */\nexport function listBoxes(dir: string = configsDir()): string[] {\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n return [];\n }\n const names = new Set<string>();\n for (const f of entries) {\n // `_`-prefixed files are shared partials (e.g. `_presets.mjs`), not boxes.\n if (f.startsWith('_')) continue;\n const suffix = BOX_SUFFIXES.find((s) => f.endsWith(s));\n if (suffix) names.add(f.slice(0, -suffix.length));\n }\n return [...names].sort();\n}\n\n/**\n * Resolve a box name to its config file in {@link configsDir}, preferring\n * `<name>.config.mjs` over a bare `<name>.mjs`.\n *\n * @throws if no matching file exists (the message lists the available boxes).\n */\nexport function resolveBox(name: string, dir: string = configsDir()): string {\n // `_`-prefixed files are shared partials (e.g. `_presets.mjs`), not boxes —\n // keep them un-resolvable so `-b` matches what `listBoxes` advertises.\n if (!name.startsWith('_')) {\n const found = boxCandidates(name, dir).find((c) => fs.existsSync(c));\n if (found) return found;\n }\n const available = listBoxes(dir);\n const hint = available.length ? `available: ${available.join(', ')}` : `none found in ${dir}`;\n throw new Error(`clabox: box '${name}' not found in ${dir} (${hint})`);\n}\n\n/** Result of {@link loadConfig}: the effective config and the file it came from. */\nexport interface LoadedConfig {\n config: Config;\n configFile: string | null;\n}\n\n/**\n * Build the effective config: defaults ⊕ env ⊕ config file.\n *\n * @param explicitConfig optional config-file path (e.g. from `--config`);\n * takes precedence over `CLABOX_CONFIG` and the default lookup locations.\n */\nexport async function loadConfig(explicitConfig?: string | null): Promise<LoadedConfig> {\n let cfg: Config = defaultConfig;\n const file = findConfigFile(explicitConfig);\n if (file) {\n const mod = await import(pathToFileURL(file).href);\n const exported = mod.default ?? mod.config ?? mod;\n const resolved = typeof exported === 'function' ? await exported(defaultConfig) : exported;\n cfg = mergeConfig(defaultConfig, resolved);\n }\n return { config: cfg, configFile: file };\n}\n"],"mappings":"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"}
|
|
@@ -43,6 +43,19 @@ interface AppBuilderConfig {
|
|
|
43
43
|
/** `clabox` binary baked into the generated `command`. null → autodetect. */
|
|
44
44
|
claboxBin: string | null;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* A single MCP server entry — the value under a key in claude's `mcpServers`
|
|
48
|
+
* map. Loose on purpose (mirrors claude's mcp.json schema): `stdio` servers use
|
|
49
|
+
* `command`/`args`/`env`, remote `http`/`sse` servers use `url`/`headers`.
|
|
50
|
+
*/
|
|
51
|
+
interface McpServer {
|
|
52
|
+
type?: 'stdio' | 'http' | 'sse';
|
|
53
|
+
command?: string;
|
|
54
|
+
args?: string[];
|
|
55
|
+
env?: Record<string, string>;
|
|
56
|
+
url?: string;
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
}
|
|
46
59
|
/** Extra rules layered on top of the built-in base profile. */
|
|
47
60
|
interface PathRules {
|
|
48
61
|
/** RW subpaths (beyond project dir + configDir + /tmp). */
|
|
@@ -68,6 +81,20 @@ interface Config {
|
|
|
68
81
|
configDir: string;
|
|
69
82
|
/** Extra args always passed to `claude`, before any args from the CLI. */
|
|
70
83
|
claudeArgs: string[];
|
|
84
|
+
/**
|
|
85
|
+
* Per-box MCP servers (the `mcpServers` map). clabox compiles them to
|
|
86
|
+
* `<configDir>/mcp/<slug>.json` and launches claude with
|
|
87
|
+
* `--strict-mcp-config --mcp-config <file>`, so a shared configDir's global /
|
|
88
|
+
* plugin MCP servers are ignored — each box gets exactly these and no more.
|
|
89
|
+
* Materialized on every `run` and during `init`. Absent → no MCP flags.
|
|
90
|
+
*/
|
|
91
|
+
mcp?: Record<string, McpServer>;
|
|
92
|
+
/**
|
|
93
|
+
* Text appended to claude's system prompt via `--append-system-prompt`.
|
|
94
|
+
* `string[]` is joined with blank lines. Use it for per-box pre-prompts while
|
|
95
|
+
* sharing one configDir (the user-level CLAUDE.md is shared; this is not).
|
|
96
|
+
*/
|
|
97
|
+
systemPrompt?: string | string[];
|
|
71
98
|
bot: BotConfig;
|
|
72
99
|
/**
|
|
73
100
|
* Extra environment variables forced onto the sandboxed `claude` process,
|
|
@@ -130,5 +157,5 @@ interface LoadedConfig {
|
|
|
130
157
|
*/
|
|
131
158
|
declare function loadConfig(explicitConfig?: string | null): Promise<LoadedConfig>;
|
|
132
159
|
//#endregion
|
|
133
|
-
export { HOME as a,
|
|
134
|
-
//# sourceMappingURL=config-
|
|
160
|
+
export { HOME as a, PathRules as c, expandHome as d, findConfigFile as f, resolveBox as g, mergeConfig as h, Config as i, configsDir as l, loadConfig as m, AppConfig as n, LoadedConfig as o, listBoxes as p, BotConfig as r, McpServer as s, AppBuilderConfig as t, defaultConfig as u };
|
|
161
|
+
//# sourceMappingURL=config-jZAB2Zg8.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { i as expandHome } from "./config-BQ44iVWT.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/sandbox/extras.ts
|
|
4
|
+
/**
|
|
5
|
+
* Stable per-box slug used to name the materialized files. Prefers the config
|
|
6
|
+
* file's basename (so `-b is-mg` → `is-mg`, matching `init`'s box name), else
|
|
7
|
+
* falls back to the project dir's basename.
|
|
8
|
+
*/
|
|
9
|
+
function boxSlug(configFile, projectDir) {
|
|
10
|
+
if (configFile) return path.basename(configFile).replace(/\.(config\.)?(mjs|cjs|js)$/, "");
|
|
11
|
+
return path.basename(projectDir);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Compile `config.mcp` / `config.systemPrompt` into claude args.
|
|
15
|
+
*
|
|
16
|
+
* - MCP: written to `<configDir>/mcp/<slug>.json` and loaded with
|
|
17
|
+
* `--strict-mcp-config --mcp-config <file>` (global/plugin servers ignored).
|
|
18
|
+
* configDir is sandbox-RW, so the json is readable inside the box.
|
|
19
|
+
* - systemPrompt: appended inline via `--append-system-prompt` (no file —
|
|
20
|
+
* nothing to leave stale; `string[]` is joined with blank lines).
|
|
21
|
+
*/
|
|
22
|
+
function buildBoxExtras(config, slug) {
|
|
23
|
+
const configDir = expandHome(config.configDir);
|
|
24
|
+
const claudeArgs = [];
|
|
25
|
+
const files = [];
|
|
26
|
+
const servers = config.mcp;
|
|
27
|
+
if (servers && Object.keys(servers).length > 0) {
|
|
28
|
+
const mcpFile = path.join(configDir, "mcp", `${slug}.json`);
|
|
29
|
+
files.push({
|
|
30
|
+
path: mcpFile,
|
|
31
|
+
content: `${JSON.stringify({ mcpServers: servers }, null, 2)}\n`
|
|
32
|
+
});
|
|
33
|
+
claudeArgs.push("--strict-mcp-config", "--mcp-config", mcpFile);
|
|
34
|
+
}
|
|
35
|
+
const sp = config.systemPrompt;
|
|
36
|
+
const text = (Array.isArray(sp) ? sp.join("\n\n") : sp ?? "").trim();
|
|
37
|
+
if (text) claudeArgs.push("--append-system-prompt", text);
|
|
38
|
+
return {
|
|
39
|
+
claudeArgs,
|
|
40
|
+
files
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { buildBoxExtras as n, boxSlug as t };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=extras-B2ss2Cgh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extras-B2ss2Cgh.js","names":[],"sources":["../src/sandbox/extras.ts"],"sourcesContent":["// Per-box \"extras\": compile a box's declarative `mcp` / `systemPrompt` config\n// into the claude args (and the files they reference) — so user config stays\n// pure data and clabox owns the wiring. Pure builder; callers (run.ts at launch,\n// scaffold.ts at `init`) do the actual fs writes.\n\nimport path from 'node:path';\nimport { type Config, expandHome } from '../utils/config.js';\n\n/** A file to materialize before launching claude (absolute path + content). */\nexport interface ExtraFile {\n path: string;\n content: string;\n}\n\n/** Compiled box extras: the claude args to inject + the files they reference. */\nexport interface BoxExtras {\n /** Appended after `config.claudeArgs`, before any CLI args. */\n claudeArgs: string[];\n /** Files to write (under configDir, which the sandbox grants RW). */\n files: ExtraFile[];\n}\n\n/**\n * Stable per-box slug used to name the materialized files. Prefers the config\n * file's basename (so `-b is-mg` → `is-mg`, matching `init`'s box name), else\n * falls back to the project dir's basename.\n */\nexport function boxSlug(configFile: string | null | undefined, projectDir: string): string {\n if (configFile) {\n return path.basename(configFile).replace(/\\.(config\\.)?(mjs|cjs|js)$/, '');\n }\n return path.basename(projectDir);\n}\n\n/**\n * Compile `config.mcp` / `config.systemPrompt` into claude args.\n *\n * - MCP: written to `<configDir>/mcp/<slug>.json` and loaded with\n * `--strict-mcp-config --mcp-config <file>` (global/plugin servers ignored).\n * configDir is sandbox-RW, so the json is readable inside the box.\n * - systemPrompt: appended inline via `--append-system-prompt` (no file —\n * nothing to leave stale; `string[]` is joined with blank lines).\n */\nexport function buildBoxExtras(config: Config, slug: string): BoxExtras {\n const configDir = expandHome(config.configDir);\n const claudeArgs: string[] = [];\n const files: ExtraFile[] = [];\n\n const servers = config.mcp;\n if (servers && Object.keys(servers).length > 0) {\n const mcpFile = path.join(configDir, 'mcp', `${slug}.json`);\n files.push({\n path: mcpFile,\n content: `${JSON.stringify({ mcpServers: servers }, null, 2)}\\n`,\n });\n claudeArgs.push('--strict-mcp-config', '--mcp-config', mcpFile);\n }\n\n const sp = config.systemPrompt;\n const text = (Array.isArray(sp) ? sp.join('\\n\\n') : (sp ?? '')).trim();\n if (text) claudeArgs.push('--append-system-prompt', text);\n\n return { claudeArgs, files };\n}\n"],"mappings":";;;;;;;;AA2BA,SAAgB,QAAQ,YAAuC,YAA4B;CACzF,IAAI,YACF,OAAO,KAAK,SAAS,UAAU,CAAC,CAAC,QAAQ,8BAA8B,EAAE;CAE3E,OAAO,KAAK,SAAS,UAAU;AACjC;;;;;;;;;;AAWA,SAAgB,eAAe,QAAgB,MAAyB;CACtE,MAAM,YAAY,WAAW,OAAO,SAAS;CAC7C,MAAM,aAAuB,CAAC;CAC9B,MAAM,QAAqB,CAAC;CAE5B,MAAM,UAAU,OAAO;CACvB,IAAI,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAAG;EAC9C,MAAM,UAAU,KAAK,KAAK,WAAW,OAAO,GAAG,KAAK,MAAM;EAC1D,MAAM,KAAK;GACT,MAAM;GACN,SAAS,GAAG,KAAK,UAAU,EAAE,YAAY,QAAQ,GAAG,MAAM,CAAC,EAAE;EAC/D,CAAC;EACD,WAAW,KAAK,uBAAuB,gBAAgB,OAAO;CAChE;CAEA,MAAM,KAAK,OAAO;CAClB,MAAM,QAAQ,MAAM,QAAQ,EAAE,IAAI,GAAG,KAAK,MAAM,IAAK,MAAM,GAAA,CAAK,KAAK;CACrE,IAAI,MAAM,WAAW,KAAK,0BAA0B,IAAI;CAExD,OAAO;EAAE;EAAY;CAAM;AAC7B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { i as Config } from "./config-jZAB2Zg8.js";
|
|
2
|
+
|
|
3
|
+
//#region src/sandbox/extras.d.ts
|
|
4
|
+
/** A file to materialize before launching claude (absolute path + content). */
|
|
5
|
+
interface ExtraFile {
|
|
6
|
+
path: string;
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
/** Compiled box extras: the claude args to inject + the files they reference. */
|
|
10
|
+
interface BoxExtras {
|
|
11
|
+
/** Appended after `config.claudeArgs`, before any CLI args. */
|
|
12
|
+
claudeArgs: string[];
|
|
13
|
+
/** Files to write (under configDir, which the sandbox grants RW). */
|
|
14
|
+
files: ExtraFile[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Stable per-box slug used to name the materialized files. Prefers the config
|
|
18
|
+
* file's basename (so `-b is-mg` → `is-mg`, matching `init`'s box name), else
|
|
19
|
+
* falls back to the project dir's basename.
|
|
20
|
+
*/
|
|
21
|
+
declare function boxSlug(configFile: string | null | undefined, projectDir: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Compile `config.mcp` / `config.systemPrompt` into claude args.
|
|
24
|
+
*
|
|
25
|
+
* - MCP: written to `<configDir>/mcp/<slug>.json` and loaded with
|
|
26
|
+
* `--strict-mcp-config --mcp-config <file>` (global/plugin servers ignored).
|
|
27
|
+
* configDir is sandbox-RW, so the json is readable inside the box.
|
|
28
|
+
* - systemPrompt: appended inline via `--append-system-prompt` (no file —
|
|
29
|
+
* nothing to leave stale; `string[]` is joined with blank lines).
|
|
30
|
+
*/
|
|
31
|
+
declare function buildBoxExtras(config: Config, slug: string): BoxExtras;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { buildBoxExtras as i, ExtraFile as n, boxSlug as r, BoxExtras as t };
|
|
34
|
+
//# sourceMappingURL=extras-B_dzBrCF.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AppConfig } from "./config-
|
|
1
|
+
import { n as AppConfig } from "./config-jZAB2Zg8.js";
|
|
2
2
|
|
|
3
3
|
//#region src/init/ghostty.d.ts
|
|
4
4
|
/** Inputs for {@link buildGhosttyConfig} (all paths already absolute). */
|
|
@@ -8,9 +8,18 @@ interface GhosttyConfigOptions {
|
|
|
8
8
|
boxName: string;
|
|
9
9
|
/** Absolute project dir to `cd` into. null → don't `cd` (run in launch cwd). */
|
|
10
10
|
projectDir: string | null;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Absolute `CLABOX_CONFIGS_DIR` so `-b` resolves the box from any cwd, or
|
|
13
|
+
* null to omit it — when null, `-b` finds the box via the runtime default
|
|
14
|
+
* (`~/.config/clabox/configs`) at launch time.
|
|
15
|
+
*/
|
|
16
|
+
configsDir: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* The `clabox` command baked into the launcher. Default is a bare `clabox`
|
|
19
|
+
* resolved from PATH at launch time by the `zsh -lic` login shell (survives
|
|
20
|
+
* package-manager moves, e.g. bun → npm/homebrew); pass an absolute path only
|
|
21
|
+
* to pin a specific binary.
|
|
22
|
+
*/
|
|
14
23
|
claboxBin: string;
|
|
15
24
|
/** Absolute path to a base Ghostty config, emitted as a leading `config-file`. */
|
|
16
25
|
baseGhosttyConfig?: string | null;
|
|
@@ -31,4 +40,4 @@ declare function appBundlePath(appsDir: string, app: AppConfig): string;
|
|
|
31
40
|
declare function bundleId(boxName: string, app: AppConfig): string;
|
|
32
41
|
//#endregion
|
|
33
42
|
export { buildLauncherSource as a, buildGhosttyConfig as i, appBundlePath as n, bundleId as o, buildCommand as r, GhosttyConfigOptions as t };
|
|
34
|
-
//# sourceMappingURL=ghostty-
|
|
43
|
+
//# sourceMappingURL=ghostty-DFwFh4aL.d.ts.map
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
//#region src/init/ghostty.ts
|
|
3
3
|
/** The `command = zsh -lic '…'` line that boots clabox for the box. */
|
|
4
4
|
function buildCommand(opts) {
|
|
5
|
-
return `command = zsh -lic '${`${opts.projectDir ? `cd ${opts.projectDir} && ` : ""}CLABOX_CONFIGS_DIR=${opts.configsDir} ${opts.claboxBin} -b ${opts.boxName}; exec zsh`}'`;
|
|
5
|
+
return `command = zsh -lic '${`${opts.projectDir ? `cd ${opts.projectDir} && ` : ""}${opts.configsDir ? `CLABOX_CONFIGS_DIR=${opts.configsDir} ` : ""}${opts.claboxBin} -b ${opts.boxName}; exec zsh`}'`;
|
|
6
6
|
}
|
|
7
7
|
/** Build the Ghostty config text for an app box. */
|
|
8
8
|
function buildGhosttyConfig(opts) {
|
|
@@ -71,4 +71,4 @@ function bundleId(boxName, app) {
|
|
|
71
71
|
//#endregion
|
|
72
72
|
export { bundleId as a, buildLauncherSource as i, buildCommand as n, buildGhosttyConfig as r, appBundlePath as t };
|
|
73
73
|
|
|
74
|
-
//# sourceMappingURL=ghostty-
|
|
74
|
+
//# sourceMappingURL=ghostty-Dw4QLyl-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ghostty-Dw4QLyl-.js","names":[],"sources":["../src/init/ghostty.ts"],"sourcesContent":["// Pure builders for the Ghostty-app artifacts emitted by `clabox init`.\n//\n// For a box that opts in via `app` (see AppConfig), `init` generates a Ghostty\n// config whose `command` launches `clabox -b <box>`, then clones Ghostty.app\n// with a tiny C launcher that bakes in `--config-file=<that config>`. Everything\n// here is text-only (no I/O) so it can be unit-tested without macOS; the actual\n// build lives in init/app.ts.\n\nimport path from 'node:path';\nimport type { AppConfig } from '../utils/config.js';\n\n/** Inputs for {@link buildGhosttyConfig} (all paths already absolute). */\nexport interface GhosttyConfigOptions {\n app: AppConfig;\n /** The `-b` box name this config launches. */\n boxName: string;\n /** Absolute project dir to `cd` into. null → don't `cd` (run in launch cwd). */\n projectDir: string | null;\n /**\n * Absolute `CLABOX_CONFIGS_DIR` so `-b` resolves the box from any cwd, or\n * null to omit it — when null, `-b` finds the box via the runtime default\n * (`~/.config/clabox/configs`) at launch time.\n */\n configsDir: string | null;\n /**\n * The `clabox` command baked into the launcher. Default is a bare `clabox`\n * resolved from PATH at launch time by the `zsh -lic` login shell (survives\n * package-manager moves, e.g. bun → npm/homebrew); pass an absolute path only\n * to pin a specific binary.\n */\n claboxBin: string;\n /** Absolute path to a base Ghostty config, emitted as a leading `config-file`. */\n baseGhosttyConfig?: string | null;\n}\n\n/** The `command = zsh -lic '…'` line that boots clabox for the box. */\nexport function buildCommand(opts: GhosttyConfigOptions): string {\n const cd = opts.projectDir ? `cd ${opts.projectDir} && ` : '';\n const env = opts.configsDir ? `CLABOX_CONFIGS_DIR=${opts.configsDir} ` : '';\n const inner = `${cd}${env}${opts.claboxBin} -b ${opts.boxName}; exec zsh`;\n // Login + interactive zsh so the GUI-launched app inherits the user's PATH\n // (/etc/zprofile→path_helper for Homebrew, ~/.zshrc for fnm/nvm/volta). A bare\n // `bash -c` gets only launchd's minimal PATH and can't find `node` (clabox's\n // shebang is `#!/usr/bin/env node`).\n return `command = zsh -lic '${inner}'`;\n}\n\n/** Build the Ghostty config text for an app box. */\nexport function buildGhosttyConfig(opts: GhosttyConfigOptions): string {\n const { app } = opts;\n const lines: string[] = [\n '# Generated by `clabox init` — do not edit; rerun it after changing the box config.',\n ];\n if (opts.baseGhosttyConfig) lines.push(`config-file = ${opts.baseGhosttyConfig}`);\n lines.push('', `title = \"${app.title ?? app.name}\"`);\n if (app.macosIcon) lines.push(`macos-icon = ${app.macosIcon}`);\n if (app.ghostty && Object.keys(app.ghostty).length > 0) {\n lines.push('');\n for (const [key, value] of Object.entries(app.ghostty)) lines.push(`${key} = ${value}`);\n }\n lines.push('', buildCommand(opts));\n return `${lines.join('\\n')}\\n`;\n}\n\n/** Escape a string for embedding as a C double-quoted literal. */\nfunction cEscape(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Build the C launcher source. It finds itself, locates `ghostty.real` next to\n * it, and re-execs it with `--config-file=<configPath>` prepended — so the clone\n * always boots with its own config regardless of how it's launched.\n */\nexport function buildLauncherSource(configPath: string): string {\n return `// Generated by clabox init. Launches ghostty.real with a baked config.\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <libgen.h>\n#include <mach-o/dyld.h>\n\nstatic const char *CONFIG_PATH = \"${cEscape(configPath)}\";\n\nint main(int argc, char *argv[]) {\n char path[4096];\n uint32_t size = sizeof(path);\n _NSGetExecutablePath(path, &size);\n\n char *dir = dirname(path);\n char real_path[4096];\n snprintf(real_path, sizeof(real_path), \"%s/ghostty.real\", dir);\n\n char config_arg[4096];\n snprintf(config_arg, sizeof(config_arg), \"--config-file=%s\", CONFIG_PATH);\n\n char **new_argv = malloc(sizeof(char *) * (argc + 2));\n new_argv[0] = real_path;\n new_argv[1] = config_arg;\n for (int i = 1; i < argc; i++) new_argv[i + 1] = argv[i];\n new_argv[argc + 1] = NULL;\n\n execv(real_path, new_argv);\n return 1;\n}\n`;\n}\n\n/** Absolute path to the built `.app` bundle. */\nexport function appBundlePath(appsDir: string, app: AppConfig): string {\n return path.join(appsDir, `${app.name}.app`);\n}\n\n/** Bundle identifier for the clone (explicit, or derived from the box name). */\nexport function bundleId(boxName: string, app: AppConfig): string {\n return app.bundleId ?? `com.ghostty.custom.${boxName.replace(/-/g, '.')}`;\n}\n"],"mappings":";;;AAoCA,SAAgB,aAAa,MAAoC;CAQ/D,OAAO,uBAAuB,GAPnB,KAAK,aAAa,MAAM,KAAK,WAAW,QAAQ,KAC/C,KAAK,aAAa,sBAAsB,KAAK,WAAW,KAAK,KAC7C,KAAK,UAAU,MAAM,KAAK,QAAQ,YAK1B;AACtC;;AAGA,SAAgB,mBAAmB,MAAoC;CACrE,MAAM,EAAE,QAAQ;CAChB,MAAM,QAAkB,CACtB,qFACF;CACA,IAAI,KAAK,mBAAmB,MAAM,KAAK,iBAAiB,KAAK,mBAAmB;CAChF,MAAM,KAAK,IAAI,YAAY,IAAI,SAAS,IAAI,KAAK,EAAE;CACnD,IAAI,IAAI,WAAW,MAAM,KAAK,gBAAgB,IAAI,WAAW;CAC7D,IAAI,IAAI,WAAW,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC,SAAS,GAAG;EACtD,MAAM,KAAK,EAAE;EACb,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,OAAO,GAAG,MAAM,KAAK,GAAG,IAAI,KAAK,OAAO;CACxF;CACA,MAAM,KAAK,IAAI,aAAa,IAAI,CAAC;CACjC,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;;AAGA,SAAS,QAAQ,GAAmB;CAClC,OAAO,EAAE,QAAQ,OAAO,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAK;AACrD;;;;;;AAOA,SAAgB,oBAAoB,YAA4B;CAC9D,OAAO;;;;;;;oCAO2B,QAAQ,UAAU,EAAE;;;;;;;;;;;;;;;;;;;;;;;;AAwBxD;;AAGA,SAAgB,cAAc,SAAiB,KAAwB;CACrE,OAAO,KAAK,KAAK,SAAS,GAAG,IAAI,KAAK,KAAK;AAC7C;;AAGA,SAAgB,SAAS,SAAiB,KAAwB;CAChE,OAAO,IAAI,YAAY,sBAAsB,QAAQ,QAAQ,MAAM,GAAG;AACxE"}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { a as buildIndex, i as buildAliasFiles, n as InitFile, o as buildWrapper, r as aliasName, t as AliasPaths } from "./aliases-
|
|
2
|
-
import { a as HOME, c as
|
|
3
|
-
import { i as canBuildApps, n as BuildAppResult, r as buildApp, t as BuildAppOptions } from "./app-
|
|
4
|
-
import { a as buildLauncherSource, i as buildGhosttyConfig, n as appBundlePath, o as bundleId, r as buildCommand, t as GhosttyConfigOptions } from "./ghostty-
|
|
5
|
-
import { n as buildRaycastCommand, r as raycastIcon, t as RaycastCommandOptions } from "./raycast-
|
|
6
|
-
import { a as runInit, i as discoverProfiles, n as InitOptions, r as InitResult, t as BuiltApp } from "./scaffold-
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
|
|
1
|
+
import { a as buildIndex, i as buildAliasFiles, n as InitFile, o as buildWrapper, r as aliasName, t as AliasPaths } from "./aliases-BNdrOn9E.js";
|
|
2
|
+
import { a as HOME, c as PathRules, d as expandHome, f as findConfigFile, g as resolveBox, h as mergeConfig, i as Config, l as configsDir, m as loadConfig, n as AppConfig, o as LoadedConfig, p as listBoxes, r as BotConfig, s as McpServer, t as AppBuilderConfig, u as defaultConfig } from "./config-jZAB2Zg8.js";
|
|
3
|
+
import { i as canBuildApps, n as BuildAppResult, r as buildApp, t as BuildAppOptions } from "./app-B1djcEnN.js";
|
|
4
|
+
import { a as buildLauncherSource, i as buildGhosttyConfig, n as appBundlePath, o as bundleId, r as buildCommand, t as GhosttyConfigOptions } from "./ghostty-DFwFh4aL.js";
|
|
5
|
+
import { n as buildRaycastCommand, r as raycastIcon, t as RaycastCommandOptions } from "./raycast-fc5U1x7E.js";
|
|
6
|
+
import { a as runInit, i as discoverProfiles, n as InitOptions, r as InitResult, t as BuiltApp } from "./scaffold-CbsKxlL0.js";
|
|
7
|
+
import { i as buildBoxExtras, n as ExtraFile, r as boxSlug, t as BoxExtras } from "./extras-B_dzBrCF.js";
|
|
8
|
+
import { a as ipcName, c as regex, i as globalName, l as subpath, n as buildProfile, o as literal, r as detectPackagePaths, s as reEscape, t as ProfileContext } from "./profile-7CiMUPu9.js";
|
|
9
|
+
import { a as resolveProjectDir, i as profilePath, o as runClaude, r as generateProfile, t as RunOptions } from "./run-BWGDNJiY.js";
|
|
10
|
+
export { type AliasPaths, type AppBuilderConfig, type AppConfig, type BotConfig, type BoxExtras, type BuildAppOptions, type BuildAppResult, type BuiltApp, type Config, type ExtraFile, type GhosttyConfigOptions, HOME, type InitFile, type InitOptions, type InitResult, type LoadedConfig, type McpServer, type PathRules, type ProfileContext, type RaycastCommandOptions, type RunOptions, aliasName, appBundlePath, boxSlug, buildAliasFiles, buildApp, buildBoxExtras, buildCommand, buildGhosttyConfig, buildIndex, buildLauncherSource, buildProfile, buildRaycastCommand, buildWrapper, bundleId, canBuildApps, configsDir, defaultConfig, detectPackagePaths, discoverProfiles, expandHome, findConfigFile, generateProfile, globalName, ipcName, listBoxes, literal, loadConfig, mergeConfig, profilePath, raycastIcon, reEscape, regex, resolveBox, resolveProjectDir, runClaude, runInit, subpath };
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { a as findConfigFile, c as mergeConfig, i as expandHome, l as resolveBox, n as configsDir, o as listBoxes, r as defaultConfig, s as loadConfig, t as HOME } from "./config-BQ44iVWT.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
2
|
+
import { n as buildBoxExtras, t as boxSlug } from "./extras-B2ss2Cgh.js";
|
|
3
|
+
import { i as buildWrapper, n as buildAliasFiles, r as buildIndex, t as aliasName } from "./aliases-KGjds7dK.js";
|
|
4
|
+
import { a as bundleId, i as buildLauncherSource, n as buildCommand, r as buildGhosttyConfig, t as appBundlePath } from "./ghostty-Dw4QLyl-.js";
|
|
5
|
+
import { n as canBuildApps, t as buildApp } from "./app-ChnKbgkD.js";
|
|
5
6
|
import { n as raycastIcon, t as buildRaycastCommand } from "./raycast-BCdO2Se1.js";
|
|
6
|
-
import { n as runInit, t as discoverProfiles } from "./scaffold-
|
|
7
|
+
import { n as runInit, t as discoverProfiles } from "./scaffold-C1tqCL_8.js";
|
|
7
8
|
import { a as literal, c as subpath, i as ipcName, n as detectPackagePaths, o as reEscape, r as globalName, s as regex, t as buildProfile } from "./profile-DM6NAgb-.js";
|
|
8
|
-
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath } from "./run-
|
|
9
|
-
export { HOME, aliasName, appBundlePath, buildAliasFiles, buildApp, buildCommand, buildGhosttyConfig, buildIndex, buildLauncherSource, buildProfile, buildRaycastCommand, buildWrapper, bundleId, canBuildApps, configsDir, defaultConfig, detectPackagePaths, discoverProfiles, expandHome, findConfigFile, generateProfile, globalName, ipcName, listBoxes, literal, loadConfig, mergeConfig, profilePath, raycastIcon, reEscape, regex, resolveBox, resolveProjectDir, runClaude, runInit, subpath };
|
|
9
|
+
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath } from "./run-yUsOaKFx.js";
|
|
10
|
+
export { HOME, aliasName, appBundlePath, boxSlug, buildAliasFiles, buildApp, buildBoxExtras, buildCommand, buildGhosttyConfig, buildIndex, buildLauncherSource, buildProfile, buildRaycastCommand, buildWrapper, bundleId, canBuildApps, configsDir, defaultConfig, detectPackagePaths, discoverProfiles, expandHome, findConfigFile, generateProfile, globalName, ipcName, listBoxes, literal, loadConfig, mergeConfig, profilePath, raycastIcon, reEscape, regex, resolveBox, resolveProjectDir, runClaude, runInit, subpath };
|
package/lib/init/aliases.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as buildIndex, i as buildAliasFiles, n as InitFile, o as buildWrapper, r as aliasName, t as AliasPaths } from "../aliases-
|
|
1
|
+
import { a as buildIndex, i as buildAliasFiles, n as InitFile, o as buildWrapper, r as aliasName, t as AliasPaths } from "../aliases-BNdrOn9E.js";
|
|
2
2
|
export { AliasPaths, InitFile, aliasName, buildAliasFiles, buildIndex, buildWrapper };
|
package/lib/init/aliases.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as buildWrapper, n as buildAliasFiles, r as buildIndex, t as aliasName } from "../aliases-
|
|
1
|
+
import { i as buildWrapper, n as buildAliasFiles, r as buildIndex, t as aliasName } from "../aliases-KGjds7dK.js";
|
|
2
2
|
export { aliasName, buildAliasFiles, buildIndex, buildWrapper };
|
package/lib/init/app.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as installIcon, i as canBuildApps, n as BuildAppResult, r as buildApp, t as BuildAppOptions } from "../app-
|
|
1
|
+
import { a as installIcon, i as canBuildApps, n as BuildAppResult, r as buildApp, t as BuildAppOptions } from "../app-B1djcEnN.js";
|
|
2
2
|
export { BuildAppOptions, BuildAppResult, buildApp, canBuildApps, installIcon };
|
package/lib/init/app.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as canBuildApps, r as installIcon, t as buildApp } from "../app-
|
|
1
|
+
import { n as canBuildApps, r as installIcon, t as buildApp } from "../app-ChnKbgkD.js";
|
|
2
2
|
export { buildApp, canBuildApps, installIcon };
|
package/lib/init/ghostty.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as buildLauncherSource, i as buildGhosttyConfig, n as appBundlePath, o as bundleId, r as buildCommand, t as GhosttyConfigOptions } from "../ghostty-
|
|
1
|
+
import { a as buildLauncherSource, i as buildGhosttyConfig, n as appBundlePath, o as bundleId, r as buildCommand, t as GhosttyConfigOptions } from "../ghostty-DFwFh4aL.js";
|
|
2
2
|
export { GhosttyConfigOptions, appBundlePath, buildCommand, buildGhosttyConfig, buildLauncherSource, bundleId };
|
package/lib/init/ghostty.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as bundleId, i as buildLauncherSource, n as buildCommand, r as buildGhosttyConfig, t as appBundlePath } from "../ghostty-
|
|
1
|
+
import { a as bundleId, i as buildLauncherSource, n as buildCommand, r as buildGhosttyConfig, t as appBundlePath } from "../ghostty-Dw4QLyl-.js";
|
|
2
2
|
export { appBundlePath, buildCommand, buildGhosttyConfig, buildLauncherSource, bundleId };
|
package/lib/init/raycast.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as buildRaycastCommand, r as raycastIcon, t as RaycastCommandOptions } from "../raycast-
|
|
1
|
+
import { n as buildRaycastCommand, r as raycastIcon, t as RaycastCommandOptions } from "../raycast-fc5U1x7E.js";
|
|
2
2
|
export { RaycastCommandOptions, buildRaycastCommand, raycastIcon };
|
package/lib/init/scaffold.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as runInit, i as discoverProfiles, n as InitOptions, r as InitResult, t as BuiltApp } from "../scaffold-
|
|
1
|
+
import { a as runInit, i as discoverProfiles, n as InitOptions, r as InitResult, t as BuiltApp } from "../scaffold-CbsKxlL0.js";
|
|
2
2
|
export { BuiltApp, InitOptions, InitResult, discoverProfiles, runInit };
|
package/lib/init/scaffold.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as runInit, t as discoverProfiles } from "../scaffold-
|
|
1
|
+
import { n as runInit, t as discoverProfiles } from "../scaffold-C1tqCL_8.js";
|
|
2
2
|
export { discoverProfiles, runInit };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as Config } from "./config-
|
|
1
|
+
import { i as Config } from "./config-jZAB2Zg8.js";
|
|
2
2
|
|
|
3
3
|
//#region src/sandbox/profile.d.ts
|
|
4
4
|
declare const subpath: (p: string) => string;
|
|
@@ -26,4 +26,4 @@ declare function buildProfile(config: Config, {
|
|
|
26
26
|
}: ProfileContext): string;
|
|
27
27
|
//#endregion
|
|
28
28
|
export { ipcName as a, regex as c, globalName as i, subpath as l, buildProfile as n, literal as o, detectPackagePaths as r, reEscape as s, ProfileContext as t };
|
|
29
|
-
//# sourceMappingURL=profile-
|
|
29
|
+
//# sourceMappingURL=profile-7CiMUPu9.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as AppConfig } from "./config-
|
|
1
|
+
import { n as AppConfig } from "./config-jZAB2Zg8.js";
|
|
2
2
|
|
|
3
3
|
//#region src/init/raycast.d.ts
|
|
4
4
|
/** Inputs for {@link buildRaycastCommand}. */
|
|
@@ -19,4 +19,4 @@ declare function buildRaycastCommand({
|
|
|
19
19
|
}: RaycastCommandOptions): string;
|
|
20
20
|
//#endregion
|
|
21
21
|
export { buildRaycastCommand as n, raycastIcon as r, RaycastCommandOptions as t };
|
|
22
|
-
//# sourceMappingURL=raycast-
|
|
22
|
+
//# sourceMappingURL=raycast-fc5U1x7E.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { i as Config } from "./config-
|
|
1
|
+
import { i as Config } from "./config-jZAB2Zg8.js";
|
|
2
|
+
import { n as ExtraFile } from "./extras-B_dzBrCF.js";
|
|
2
3
|
|
|
3
4
|
//#region src/sandbox/run.d.ts
|
|
4
5
|
/** Deterministic per-project profile path under TMPDIR. */
|
|
@@ -12,6 +13,13 @@ declare function resolveProjectDir(config: Config): string;
|
|
|
12
13
|
declare function generateProfile(config: Config, projectDir?: string): string;
|
|
13
14
|
/** Build the `env KEY=VALUE …` argument list forced onto the sandboxed claude. */
|
|
14
15
|
declare function buildEnvArgs(config: Config): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Write the per-box extra files (mkdir -p their dirs) `0600`. They live under
|
|
18
|
+
* configDir and can carry secrets (e.g. an MCP auth token in a URL/header), so
|
|
19
|
+
* they're kept out of argv (vs. an inline `--mcp-config '<json>'`) AND off the
|
|
20
|
+
* world-readable bit on disk. Returns the paths.
|
|
21
|
+
*/
|
|
22
|
+
declare function writeExtraFiles(files: ExtraFile[]): string[];
|
|
15
23
|
/** Options accepted by {@link runClaude}. */
|
|
16
24
|
interface RunOptions {
|
|
17
25
|
configFile?: string | null;
|
|
@@ -21,5 +29,5 @@ declare function runClaude(config: Config, claudeArgs: string[], {
|
|
|
21
29
|
configFile
|
|
22
30
|
}?: RunOptions): number;
|
|
23
31
|
//#endregion
|
|
24
|
-
export { resolveProjectDir as a, profilePath as i, buildEnvArgs as n, runClaude as o, generateProfile as r, RunOptions as t };
|
|
25
|
-
//# sourceMappingURL=run-
|
|
32
|
+
export { resolveProjectDir as a, profilePath as i, buildEnvArgs as n, runClaude as o, generateProfile as r, writeExtraFiles as s, RunOptions as t };
|
|
33
|
+
//# sourceMappingURL=run-BWGDNJiY.d.ts.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { i as expandHome, t as HOME } from "./config-BQ44iVWT.js";
|
|
2
|
+
import { n as buildBoxExtras, t as boxSlug } from "./extras-B2ss2Cgh.js";
|
|
2
3
|
import { n as detectPackagePaths, t as buildProfile } from "./profile-DM6NAgb-.js";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { execFileSync, spawnSync } from "node:child_process";
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
6
7
|
import crypto from "node:crypto";
|
|
7
8
|
//#region src/sandbox/run.ts
|
|
8
9
|
const TMPDIR = (process.env.TMPDIR || "/tmp").replace(/\/$/, "");
|
|
@@ -69,16 +70,33 @@ function buildEnvArgs(config) {
|
|
|
69
70
|
for (const [key, value] of Object.entries(config.env ?? {})) args.push(`${key}=${value}`);
|
|
70
71
|
return args;
|
|
71
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Write the per-box extra files (mkdir -p their dirs) `0600`. They live under
|
|
75
|
+
* configDir and can carry secrets (e.g. an MCP auth token in a URL/header), so
|
|
76
|
+
* they're kept out of argv (vs. an inline `--mcp-config '<json>'`) AND off the
|
|
77
|
+
* world-readable bit on disk. Returns the paths.
|
|
78
|
+
*/
|
|
79
|
+
function writeExtraFiles(files) {
|
|
80
|
+
for (const f of files) {
|
|
81
|
+
fs.mkdirSync(path.dirname(f.path), { recursive: true });
|
|
82
|
+
fs.writeFileSync(f.path, f.content, { mode: 384 });
|
|
83
|
+
fs.chmodSync(f.path, 384);
|
|
84
|
+
}
|
|
85
|
+
return files.map((f) => f.path);
|
|
86
|
+
}
|
|
72
87
|
/** Generate the profile and exec claude under sandbox-exec. Returns exit code. */
|
|
73
88
|
function runClaude(config, claudeArgs, { configFile } = {}) {
|
|
74
89
|
const projectDir = resolveProjectDir(config);
|
|
75
90
|
const claudeBin = resolveClaudeBin(config);
|
|
76
91
|
const profileFile = generateProfile(config, projectDir);
|
|
92
|
+
const extras = buildBoxExtras(config, boxSlug(configFile, projectDir));
|
|
93
|
+
const extraFiles = writeExtraFiles(extras.files);
|
|
77
94
|
if (process.env.CLABOX_DEBUG) {
|
|
78
95
|
console.error(`→ Running Claude Code sandboxed in: ${projectDir}`);
|
|
79
96
|
console.error(`→ Profile: ${profileFile}`);
|
|
80
97
|
console.error(`→ Config: ${expandHome(config.configDir)}`);
|
|
81
98
|
if (configFile) console.error(`→ Config file: ${configFile}`);
|
|
99
|
+
for (const f of extraFiles) console.error(`→ MCP: ${f}`);
|
|
82
100
|
}
|
|
83
101
|
const title = projectDir.startsWith(HOME) ? `~${projectDir.slice(HOME.length)}` : projectDir;
|
|
84
102
|
process.stdout.write(`\x1b]0;${title}\x07`);
|
|
@@ -92,6 +110,7 @@ function runClaude(config, claudeArgs, { configFile } = {}) {
|
|
|
92
110
|
...envArgs,
|
|
93
111
|
claudeBin,
|
|
94
112
|
...defaultArgs,
|
|
113
|
+
...extras.claudeArgs,
|
|
95
114
|
...claudeArgs
|
|
96
115
|
];
|
|
97
116
|
const res = spawnSync("/bin/sh", [
|
|
@@ -108,6 +127,6 @@ function runClaude(config, claudeArgs, { configFile } = {}) {
|
|
|
108
127
|
return res.status ?? 0;
|
|
109
128
|
}
|
|
110
129
|
//#endregion
|
|
111
|
-
export { runClaude as a, resolveProjectDir as i, generateProfile as n, profilePath as r, buildEnvArgs as t };
|
|
130
|
+
export { runClaude as a, resolveProjectDir as i, generateProfile as n, writeExtraFiles as o, profilePath as r, buildEnvArgs as t };
|
|
112
131
|
|
|
113
|
-
//# sourceMappingURL=run-
|
|
132
|
+
//# sourceMappingURL=run-yUsOaKFx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-yUsOaKFx.js","names":[],"sources":["../src/sandbox/run.ts"],"sourcesContent":["// Profile materialization + launching `claude` under sandbox-exec.\n\nimport { execFileSync, spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { type Config, expandHome, HOME } from '../utils/config.js';\nimport { boxSlug, buildBoxExtras, type ExtraFile } from './extras.js';\nimport { buildProfile, detectPackagePaths } from './profile.js';\n\nconst TMPDIR = (process.env.TMPDIR || '/tmp').replace(/\\/$/, '');\n\n/** Deterministic per-project profile path under TMPDIR. */\nexport function profilePath(projectDir: string = process.cwd()): string {\n const hash = crypto.createHash('sha256').update(projectDir).digest('hex').slice(0, 8);\n return path.join(TMPDIR, `clabox-${path.basename(projectDir)}-${hash}.sb`);\n}\n\n/**\n * Effective project dir: `config.cwd` (with `~` expanded, resolved to an\n * absolute path so SBPL `subpath` rules stay valid) if set, else the shell CWD.\n */\nexport function resolveProjectDir(config: Config): string {\n return config.cwd ? path.resolve(expandHome(config.cwd)) : process.cwd();\n}\n\nfunction which(bin: string): string | null {\n try {\n return (\n execFileSync('command', ['-v', bin], { shell: '/bin/sh', encoding: 'utf8' }).trim() || null\n );\n } catch {\n return null;\n }\n}\n\nfunction requireSandboxExec(): void {\n if (!which('sandbox-exec')) {\n throw new Error('sandbox-exec not found. This tool requires macOS with sandbox-exec.');\n }\n}\n\nfunction resolveClaudeBin(config: Config): string {\n const candidate = config.claudeBin || which('claude') || path.join(HOME, '.local/bin/claude');\n if (!candidate || !fs.existsSync(candidate)) {\n throw new Error(`claude not found at '${candidate}'`);\n }\n return candidate;\n}\n\n/** Generate the profile file for the current project, return its path. */\nexport function generateProfile(\n config: Config,\n projectDir: string = resolveProjectDir(config),\n): string {\n requireSandboxExec();\n const file = profilePath(projectDir);\n const text = buildProfile(config, { projectDir, detectedPaths: detectPackagePaths() });\n fs.writeFileSync(file, text);\n return file;\n}\n\n/** Build the `env KEY=VALUE …` argument list forced onto the sandboxed claude. */\nexport function buildEnvArgs(config: Config): string[] {\n const sshDir = expandHome(config.bot.sshDir);\n const botKey = path.join(sshDir, 'id_ed25519');\n const botCfg = path.join(sshDir, 'config');\n const args = [\n `PATH=${path.join(HOME, '.local/bin')}:${process.env.PATH || ''}`,\n `CLAUDE_CONFIG_DIR=${expandHome(config.configDir)}`,\n `GIT_AUTHOR_NAME=${config.bot.name}`,\n `GIT_AUTHOR_EMAIL=${config.bot.email}`,\n `GIT_COMMITTER_NAME=${config.bot.name}`,\n `GIT_COMMITTER_EMAIL=${config.bot.email}`,\n 'GIT_CONFIG_COUNT=2',\n 'GIT_CONFIG_KEY_0=commit.gpgsign',\n 'GIT_CONFIG_VALUE_0=false',\n 'GIT_CONFIG_KEY_1=tag.gpgsign',\n 'GIT_CONFIG_VALUE_1=false',\n ];\n // Pin git ssh to the bot key only when it actually exists, so the sandbox\n // stays usable without a dedicated bot key configured.\n if (fs.existsSync(botKey)) {\n args.push(\n `GIT_SSH_COMMAND=ssh -F ${botCfg} -i ${botKey} -o IdentitiesOnly=yes -o IdentityAgent=none`,\n );\n }\n // User-declared extras go last so they win over the built-in vars above\n // (duplicate keys: `env` keeps the last assignment).\n for (const [key, value] of Object.entries(config.env ?? {})) {\n args.push(`${key}=${value}`);\n }\n return args;\n}\n\n/**\n * Write the per-box extra files (mkdir -p their dirs) `0600`. They live under\n * configDir and can carry secrets (e.g. an MCP auth token in a URL/header), so\n * they're kept out of argv (vs. an inline `--mcp-config '<json>'`) AND off the\n * world-readable bit on disk. Returns the paths.\n */\nexport function writeExtraFiles(files: ExtraFile[]): string[] {\n for (const f of files) {\n fs.mkdirSync(path.dirname(f.path), { recursive: true });\n fs.writeFileSync(f.path, f.content, { mode: 0o600 });\n fs.chmodSync(f.path, 0o600); // enforce even if the file pre-existed\n }\n return files.map((f) => f.path);\n}\n\n/** Options accepted by {@link runClaude}. */\nexport interface RunOptions {\n configFile?: string | null;\n}\n\n/** Generate the profile and exec claude under sandbox-exec. Returns exit code. */\nexport function runClaude(\n config: Config,\n claudeArgs: string[],\n { configFile }: RunOptions = {},\n): number {\n const projectDir = resolveProjectDir(config);\n const claudeBin = resolveClaudeBin(config);\n const profileFile = generateProfile(config, projectDir);\n\n // Compile the box's declarative mcp / systemPrompt into claude args, and\n // materialize the files they reference (under configDir, which is sandbox-RW).\n const extras = buildBoxExtras(config, boxSlug(configFile, projectDir));\n const extraFiles = writeExtraFiles(extras.files);\n\n if (process.env.CLABOX_DEBUG) {\n console.error(`→ Running Claude Code sandboxed in: ${projectDir}`);\n console.error(`→ Profile: ${profileFile}`);\n console.error(`→ Config: ${expandHome(config.configDir)}`);\n if (configFile) console.error(`→ Config file: ${configFile}`);\n for (const f of extraFiles) console.error(`→ MCP: ${f}`);\n }\n\n // Terminal title = cwd (with ~ for $HOME), matching the bash version.\n const title = projectDir.startsWith(HOME) ? `~${projectDir.slice(HOME.length)}` : projectDir;\n process.stdout.write(`\\x1b]0;${title}\\x07`);\n\n const envArgs = buildEnvArgs(config);\n const defaultArgs = Array.isArray(config.claudeArgs) ? config.claudeArgs : [];\n const inner = [\n 'sandbox-exec',\n '-f',\n profileFile,\n 'env',\n ...envArgs,\n claudeBin,\n ...defaultArgs,\n ...extras.claudeArgs,\n ...claudeArgs,\n ];\n\n // `ulimit` is a shell builtin; run the whole thing under sh so we can set it.\n // `exec \"$@\"` keeps argv intact without re-quoting (args start after $0=sh).\n const ulimit = config.ulimitProcs > 0 ? `ulimit -u ${config.ulimitProcs} 2>/dev/null; ` : '';\n const res = spawnSync('/bin/sh', ['-c', `${ulimit}exec \"$@\"`, 'sh', ...inner], {\n cwd: projectDir,\n stdio: 'inherit',\n });\n if (res.error) throw res.error;\n if (res.signal) return 1;\n return res.status ?? 0;\n}\n"],"mappings":"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"}
|
package/lib/sandbox/profile.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ipcName, c as regex, i as globalName, l as subpath, n as buildProfile, o as literal, r as detectPackagePaths, s as reEscape, t as ProfileContext } from "../profile-
|
|
1
|
+
import { a as ipcName, c as regex, i as globalName, l as subpath, n as buildProfile, o as literal, r as detectPackagePaths, s as reEscape, t as ProfileContext } from "../profile-7CiMUPu9.js";
|
|
2
2
|
export { ProfileContext, buildProfile, detectPackagePaths, globalName, ipcName, literal, reEscape, regex, subpath };
|
package/lib/sandbox/run.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as resolveProjectDir, i as profilePath, n as buildEnvArgs, o as runClaude, r as generateProfile, t as RunOptions } from "../run-
|
|
2
|
-
export { RunOptions, buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude };
|
|
1
|
+
import { a as resolveProjectDir, i as profilePath, n as buildEnvArgs, o as runClaude, r as generateProfile, s as writeExtraFiles, t as RunOptions } from "../run-BWGDNJiY.js";
|
|
2
|
+
export { RunOptions, buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude, writeExtraFiles };
|
package/lib/sandbox/run.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath, t as buildEnvArgs } from "../run-
|
|
2
|
-
export { buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude };
|
|
1
|
+
import { a as runClaude, i as resolveProjectDir, n as generateProfile, o as writeExtraFiles, r as profilePath, t as buildEnvArgs } from "../run-yUsOaKFx.js";
|
|
2
|
+
export { buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude, writeExtraFiles };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { i as expandHome, l as resolveBox, o as listBoxes, s as loadConfig } from "./config-BQ44iVWT.js";
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { n as buildBoxExtras } from "./extras-B2ss2Cgh.js";
|
|
3
|
+
import { n as buildAliasFiles } from "./aliases-KGjds7dK.js";
|
|
4
|
+
import { r as buildGhosttyConfig, t as appBundlePath } from "./ghostty-Dw4QLyl-.js";
|
|
5
|
+
import { n as canBuildApps, t as buildApp } from "./app-ChnKbgkD.js";
|
|
5
6
|
import { t as buildRaycastCommand } from "./raycast-BCdO2Se1.js";
|
|
6
7
|
import path from "node:path";
|
|
7
|
-
import { execFileSync } from "node:child_process";
|
|
8
8
|
import fs from "node:fs";
|
|
9
9
|
//#region src/init/scaffold.ts
|
|
10
10
|
/**
|
|
@@ -28,17 +28,47 @@ function pruneByExt(dir, ext) {
|
|
|
28
28
|
if (!fs.existsSync(dir)) return;
|
|
29
29
|
for (const f of fs.readdirSync(dir)) if (f.endsWith(ext)) fs.rmSync(path.join(dir, f), { force: true });
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* The `clabox` command baked into the generated Ghostty `command`. Default is a
|
|
33
|
+
* bare `clabox` resolved from PATH at launch time by the `zsh -lic` login shell
|
|
34
|
+
* — this survives package-manager moves (e.g. bun → npm/homebrew) that change
|
|
35
|
+
* the binary's absolute path. Only an explicit `appBuilder.claboxBin` pins an
|
|
36
|
+
* absolute path.
|
|
37
|
+
*/
|
|
32
38
|
function resolveClaboxBin(configured) {
|
|
33
|
-
|
|
39
|
+
return configured ? expandHome(configured) : "clabox";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The `CLABOX_CONFIGS_DIR` value to bake into generated commands, or null to
|
|
43
|
+
* omit it. Omit when `<dir>` resolves (realpath, symlinks included) to the
|
|
44
|
+
* runtime default `~/.config/clabox/configs` — then `-b` finds the box via that
|
|
45
|
+
* default at launch time, so no path needs baking (and it stays correct if the
|
|
46
|
+
* dir later moves behind the same symlink). Otherwise bake the absolute path so
|
|
47
|
+
* `-b` resolves the box regardless of the launcher's cwd.
|
|
48
|
+
*/
|
|
49
|
+
function bakeConfigsDir(dir) {
|
|
34
50
|
try {
|
|
35
|
-
|
|
36
|
-
shell: "/bin/sh",
|
|
37
|
-
encoding: "utf8"
|
|
38
|
-
}).trim();
|
|
39
|
-
if (found) return found;
|
|
51
|
+
if (fs.realpathSync(dir) === fs.realpathSync(expandHome("~/.config/clabox/configs"))) return null;
|
|
40
52
|
} catch {}
|
|
41
|
-
return
|
|
53
|
+
return dir;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compile each box's declarative `mcp` (→ `<configDir>/mcp/<box>.json`) so the
|
|
57
|
+
* files exist ahead of the first run (the same files `run` writes; slug = box
|
|
58
|
+
* name). Best-effort per box: a config that fails to load becomes a warning.
|
|
59
|
+
*/
|
|
60
|
+
async function materializeExtras(configsDir, profiles, result) {
|
|
61
|
+
for (const name of profiles) try {
|
|
62
|
+
const { config } = await loadConfig(resolveBox(name, configsDir));
|
|
63
|
+
for (const f of buildBoxExtras(config, name).files) {
|
|
64
|
+
fs.mkdirSync(path.dirname(f.path), { recursive: true });
|
|
65
|
+
fs.writeFileSync(f.path, f.content, { mode: 384 });
|
|
66
|
+
fs.chmodSync(f.path, 384);
|
|
67
|
+
result.extraFiles.push(f.path);
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
result.warnings.push(`${name}: extras not materialized — ${e.message}`);
|
|
71
|
+
}
|
|
42
72
|
}
|
|
43
73
|
/** Generate the Ghostty configs and build the apps for every `app` box. */
|
|
44
74
|
async function buildAppArtifacts(base, configsDir, profiles, only, result) {
|
|
@@ -71,7 +101,7 @@ async function buildAppArtifacts(base, configsDir, profiles, only, result) {
|
|
|
71
101
|
app,
|
|
72
102
|
boxName: name,
|
|
73
103
|
projectDir,
|
|
74
|
-
configsDir,
|
|
104
|
+
configsDir: bakeConfigsDir(configsDir),
|
|
75
105
|
claboxBin: resolveClaboxBin(config.appBuilder.claboxBin),
|
|
76
106
|
baseGhosttyConfig: baseGhostty
|
|
77
107
|
}));
|
|
@@ -115,7 +145,7 @@ async function runInit({ baseDir, buildApps = true, only = null } = {}) {
|
|
|
115
145
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
116
146
|
pruneGenerated(scriptsDir);
|
|
117
147
|
const files = buildAliasFiles(profiles, {
|
|
118
|
-
configsDir,
|
|
148
|
+
configsDir: bakeConfigsDir(configsDir),
|
|
119
149
|
scriptsDir
|
|
120
150
|
});
|
|
121
151
|
for (const f of files) {
|
|
@@ -130,12 +160,14 @@ async function runInit({ baseDir, buildApps = true, only = null } = {}) {
|
|
|
130
160
|
apps: [],
|
|
131
161
|
ghosttyConfigs: [],
|
|
132
162
|
raycastCommands: [],
|
|
163
|
+
extraFiles: [],
|
|
133
164
|
warnings: []
|
|
134
165
|
};
|
|
166
|
+
await materializeExtras(configsDir, profiles, result);
|
|
135
167
|
if (buildApps) await buildAppArtifacts(base, configsDir, profiles, only, result);
|
|
136
168
|
return result;
|
|
137
169
|
}
|
|
138
170
|
//#endregion
|
|
139
171
|
export { runInit as n, discoverProfiles as t };
|
|
140
172
|
|
|
141
|
-
//# sourceMappingURL=scaffold-
|
|
173
|
+
//# sourceMappingURL=scaffold-C1tqCL_8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-C1tqCL_8.js","names":[],"sources":["../src/init/scaffold.ts"],"sourcesContent":["// I/O for `clabox init`: discover the box configs in `<base>/configs/`,\n// (re)write the shell aliases into `<base>/scripts/`, and — for boxes that opt\n// in via `app` — generate a Ghostty config in `<base>/ghostty/` and build a\n// standalone `<appsDir>/<name>.app` (see init/app.ts).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { buildBoxExtras } from '../sandbox/extras.js';\nimport { type Config, expandHome, listBoxes, loadConfig, resolveBox } from '../utils/config.js';\nimport { buildAliasFiles } from './aliases.js';\nimport { buildApp, canBuildApps } from './app.js';\nimport { appBundlePath, buildGhosttyConfig } from './ghostty.js';\nimport { buildRaycastCommand } from './raycast.js';\n\n/**\n * Box names (sorted, de-duplicated) discovered in `<configsDir>` via the same\n * rules as `-b`: both `<name>.mjs` and `<name>.config.mjs`, `_`-prefixed\n * shared partials (e.g. `_presets.mjs`) skipped.\n */\nexport function discoverProfiles(configsDir: string): string[] {\n if (!fs.existsSync(configsDir)) {\n throw new Error(`clabox init: configs dir not found: ${configsDir}`);\n }\n const names = listBoxes(configsDir);\n if (names.length === 0) {\n throw new Error(`clabox init: no box configs (*.mjs) in ${configsDir}`);\n }\n return names;\n}\n\n/** Remove previously generated artifacts (`index.sh`, `clabox-*.sh`). */\nfunction pruneGenerated(scriptsDir: string): void {\n if (!fs.existsSync(scriptsDir)) return;\n for (const f of fs.readdirSync(scriptsDir)) {\n if (f === 'index.sh' || (f.startsWith('clabox-') && f.endsWith('.sh'))) {\n fs.rmSync(path.join(scriptsDir, f), { force: true });\n }\n }\n}\n\n/** Remove files in `dir` whose name ends with `ext` (a no-op if dir is absent). */\nfunction pruneByExt(dir: string, ext: string): void {\n if (!fs.existsSync(dir)) return;\n for (const f of fs.readdirSync(dir)) {\n if (f.endsWith(ext)) fs.rmSync(path.join(dir, f), { force: true });\n }\n}\n\n/**\n * The `clabox` command baked into the generated Ghostty `command`. Default is a\n * bare `clabox` resolved from PATH at launch time by the `zsh -lic` login shell\n * — this survives package-manager moves (e.g. bun → npm/homebrew) that change\n * the binary's absolute path. Only an explicit `appBuilder.claboxBin` pins an\n * absolute path.\n */\nfunction resolveClaboxBin(configured: string | null): string {\n return configured ? expandHome(configured) : 'clabox';\n}\n\n/**\n * The `CLABOX_CONFIGS_DIR` value to bake into generated commands, or null to\n * omit it. Omit when `<dir>` resolves (realpath, symlinks included) to the\n * runtime default `~/.config/clabox/configs` — then `-b` finds the box via that\n * default at launch time, so no path needs baking (and it stays correct if the\n * dir later moves behind the same symlink). Otherwise bake the absolute path so\n * `-b` resolves the box regardless of the launcher's cwd.\n */\nfunction bakeConfigsDir(dir: string): string | null {\n try {\n // The launched process has no CLABOX_CONFIGS_DIR set (we omit it), so its\n // runtime default is always ~/.config/clabox/configs — compare against that.\n if (fs.realpathSync(dir) === fs.realpathSync(expandHome('~/.config/clabox/configs'))) {\n return null;\n }\n } catch {\n // default dir missing/unresolvable → bake the explicit path.\n }\n return dir;\n}\n\n/** Options for {@link runInit}. */\nexport interface InitOptions {\n /** Base dir holding `configs/` and `scripts/`. Default: `<cwd>/__`. */\n baseDir?: string;\n /** Build the Ghostty apps for `app` boxes. Default: true. */\n buildApps?: boolean;\n /** Limit app building to a single box (by box name or app display name). */\n only?: string | null;\n}\n\n/** A standalone Ghostty app built by `clabox init`. */\nexport interface BuiltApp {\n box: string;\n appPath: string;\n signed: 'identity' | 'adhoc';\n}\n\n/** Result of {@link runInit}. */\nexport interface InitResult {\n profiles: string[];\n scriptsDir: string;\n indexFile: string;\n written: string[];\n /** Apps successfully built. */\n apps: BuiltApp[];\n /** Generated Ghostty config files. */\n ghosttyConfigs: string[];\n /** Generated Raycast command scripts. */\n raycastCommands: string[];\n /** Compiled per-box MCP json files (from `config.mcp`). */\n extraFiles: string[];\n /** Non-fatal issues (e.g. app build skipped/failed). */\n warnings: string[];\n}\n\n/**\n * Compile each box's declarative `mcp` (→ `<configDir>/mcp/<box>.json`) so the\n * files exist ahead of the first run (the same files `run` writes; slug = box\n * name). Best-effort per box: a config that fails to load becomes a warning.\n */\nasync function materializeExtras(\n configsDir: string,\n profiles: string[],\n result: InitResult,\n): Promise<void> {\n for (const name of profiles) {\n try {\n const { config } = await loadConfig(resolveBox(name, configsDir));\n for (const f of buildBoxExtras(config, name).files) {\n fs.mkdirSync(path.dirname(f.path), { recursive: true });\n // 0600 — the MCP json can carry an auth token (see run.ts#writeExtraFiles).\n fs.writeFileSync(f.path, f.content, { mode: 0o600 });\n fs.chmodSync(f.path, 0o600);\n result.extraFiles.push(f.path);\n }\n } catch (e) {\n result.warnings.push(`${name}: extras not materialized — ${(e as Error).message}`);\n }\n }\n}\n\n/** Generate the Ghostty configs and build the apps for every `app` box. */\nasync function buildAppArtifacts(\n base: string,\n configsDir: string,\n profiles: string[],\n only: string | null,\n result: InitResult,\n): Promise<void> {\n // Load each box config; keep the ones that opt into an app (and match `only`).\n const appBoxes: { name: string; config: Config }[] = [];\n for (const name of profiles) {\n const { config } = await loadConfig(resolveBox(name, configsDir));\n if (!config.app) continue;\n if (only && name !== only && config.app.name !== only) continue;\n appBoxes.push({ name, config });\n }\n if (appBoxes.length === 0) return;\n\n const ghosttyDir = path.join(base, 'ghostty');\n const raycastDir = path.join(base, 'raycast');\n fs.mkdirSync(ghosttyDir, { recursive: true });\n fs.mkdirSync(raycastDir, { recursive: true });\n // Only prune on a full run — with `only` we'd orphan other apps' artifacts.\n if (!only) {\n pruneByExt(ghosttyDir, '.config');\n pruneByExt(raycastDir, '.sh');\n }\n\n for (const { name, config } of appBoxes) {\n const app = config.app;\n if (!app) continue; // narrowed above; keeps the type checker happy\n const configPath = path.join(ghosttyDir, `${name}.config`);\n const projectDir = config.cwd ? path.resolve(expandHome(config.cwd)) : null;\n const baseGhostty = config.appBuilder.baseGhosttyConfig\n ? expandHome(config.appBuilder.baseGhosttyConfig)\n : null;\n fs.writeFileSync(\n configPath,\n buildGhosttyConfig({\n app,\n boxName: name,\n projectDir,\n configsDir: bakeConfigsDir(configsDir),\n claboxBin: resolveClaboxBin(config.appBuilder.claboxBin),\n baseGhosttyConfig: baseGhostty,\n }),\n );\n result.ghosttyConfigs.push(configPath);\n\n // Raycast command that opens the (to be) built bundle.\n const appPath = appBundlePath(expandHome(config.appBuilder.appsDir), app);\n const raycastPath = path.join(raycastDir, `${name}.sh`);\n fs.writeFileSync(raycastPath, buildRaycastCommand({ app, appPath }));\n fs.chmodSync(raycastPath, 0o755);\n result.raycastCommands.push(raycastPath);\n\n const check = canBuildApps(config.appBuilder);\n if (!check.ok) {\n result.warnings.push(`${name}: app not built (${check.reason})`);\n continue;\n }\n try {\n const built = buildApp({ boxName: name, app, builder: config.appBuilder, configPath });\n result.apps.push({ box: name, appPath: built.appPath, signed: built.signed });\n } catch (e) {\n result.warnings.push(`${name}: app build failed — ${(e as Error).message}`);\n }\n }\n}\n\n/** Scan the configs dir, (re)write the alias scripts, and build `app` apps. */\nexport async function runInit({\n baseDir,\n buildApps = true,\n only = null,\n}: InitOptions = {}): Promise<InitResult> {\n const base = path.resolve(baseDir ?? path.join(process.cwd(), '__'));\n const configsDir = path.join(base, 'configs');\n const scriptsDir = path.join(base, 'scripts');\n const profiles = discoverProfiles(configsDir);\n\n fs.mkdirSync(scriptsDir, { recursive: true });\n pruneGenerated(scriptsDir);\n\n const files = buildAliasFiles(profiles, { configsDir: bakeConfigsDir(configsDir), scriptsDir });\n for (const f of files) {\n fs.writeFileSync(f.path, f.content);\n if (f.executable) fs.chmodSync(f.path, 0o755);\n }\n\n const result: InitResult = {\n profiles,\n scriptsDir,\n indexFile: path.join(scriptsDir, 'index.sh'),\n written: files.map((f) => f.path),\n apps: [],\n ghosttyConfigs: [],\n raycastCommands: [],\n extraFiles: [],\n warnings: [],\n };\n\n await materializeExtras(configsDir, profiles, result);\n\n if (buildApps) {\n await buildAppArtifacts(base, configsDir, profiles, only, result);\n }\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBA,SAAgB,iBAAiB,YAA8B;CAC7D,IAAI,CAAC,GAAG,WAAW,UAAU,GAC3B,MAAM,IAAI,MAAM,uCAAuC,YAAY;CAErE,MAAM,QAAQ,UAAU,UAAU;CAClC,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,0CAA0C,YAAY;CAExE,OAAO;AACT;;AAGA,SAAS,eAAe,YAA0B;CAChD,IAAI,CAAC,GAAG,WAAW,UAAU,GAAG;CAChC,KAAK,MAAM,KAAK,GAAG,YAAY,UAAU,GACvC,IAAI,MAAM,cAAe,EAAE,WAAW,SAAS,KAAK,EAAE,SAAS,KAAK,GAClE,GAAG,OAAO,KAAK,KAAK,YAAY,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AAGzD;;AAGA,SAAS,WAAW,KAAa,KAAmB;CAClD,IAAI,CAAC,GAAG,WAAW,GAAG,GAAG;CACzB,KAAK,MAAM,KAAK,GAAG,YAAY,GAAG,GAChC,IAAI,EAAE,SAAS,GAAG,GAAG,GAAG,OAAO,KAAK,KAAK,KAAK,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AAErE;;;;;;;;AASA,SAAS,iBAAiB,YAAmC;CAC3D,OAAO,aAAa,WAAW,UAAU,IAAI;AAC/C;;;;;;;;;AAUA,SAAS,eAAe,KAA4B;CAClD,IAAI;EAGF,IAAI,GAAG,aAAa,GAAG,MAAM,GAAG,aAAa,WAAW,0BAA0B,CAAC,GACjF,OAAO;CAEX,QAAQ,CAER;CACA,OAAO;AACT;;;;;;AA0CA,eAAe,kBACb,YACA,UACA,QACe;CACf,KAAK,MAAM,QAAQ,UACjB,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,UAAU,CAAC;EAChE,KAAK,MAAM,KAAK,eAAe,QAAQ,IAAI,CAAC,CAAC,OAAO;GAClD,GAAG,UAAU,KAAK,QAAQ,EAAE,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;GAEtD,GAAG,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,IAAM,CAAC;GACnD,GAAG,UAAU,EAAE,MAAM,GAAK;GAC1B,OAAO,WAAW,KAAK,EAAE,IAAI;EAC/B;CACF,SAAS,GAAG;EACV,OAAO,SAAS,KAAK,GAAG,KAAK,8BAA+B,EAAY,SAAS;CACnF;AAEJ;;AAGA,eAAe,kBACb,MACA,YACA,UACA,MACA,QACe;CAEf,MAAM,WAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,UAAU,CAAC;EAChE,IAAI,CAAC,OAAO,KAAK;EACjB,IAAI,QAAQ,SAAS,QAAQ,OAAO,IAAI,SAAS,MAAM;EACvD,SAAS,KAAK;GAAE;GAAM;EAAO,CAAC;CAChC;CACA,IAAI,SAAS,WAAW,GAAG;CAE3B,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAC5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAE5C,IAAI,CAAC,MAAM;EACT,WAAW,YAAY,SAAS;EAChC,WAAW,YAAY,KAAK;CAC9B;CAEA,KAAK,MAAM,EAAE,MAAM,YAAY,UAAU;EACvC,MAAM,MAAM,OAAO;EACnB,IAAI,CAAC,KAAK;EACV,MAAM,aAAa,KAAK,KAAK,YAAY,GAAG,KAAK,QAAQ;EACzD,MAAM,aAAa,OAAO,MAAM,KAAK,QAAQ,WAAW,OAAO,GAAG,CAAC,IAAI;EACvE,MAAM,cAAc,OAAO,WAAW,oBAClC,WAAW,OAAO,WAAW,iBAAiB,IAC9C;EACJ,GAAG,cACD,YACA,mBAAmB;GACjB;GACA,SAAS;GACT;GACA,YAAY,eAAe,UAAU;GACrC,WAAW,iBAAiB,OAAO,WAAW,SAAS;GACvD,mBAAmB;EACrB,CAAC,CACH;EACA,OAAO,eAAe,KAAK,UAAU;EAGrC,MAAM,UAAU,cAAc,WAAW,OAAO,WAAW,OAAO,GAAG,GAAG;EACxE,MAAM,cAAc,KAAK,KAAK,YAAY,GAAG,KAAK,IAAI;EACtD,GAAG,cAAc,aAAa,oBAAoB;GAAE;GAAK;EAAQ,CAAC,CAAC;EACnE,GAAG,UAAU,aAAa,GAAK;EAC/B,OAAO,gBAAgB,KAAK,WAAW;EAEvC,MAAM,QAAQ,aAAa,OAAO,UAAU;EAC5C,IAAI,CAAC,MAAM,IAAI;GACb,OAAO,SAAS,KAAK,GAAG,KAAK,mBAAmB,MAAM,OAAO,EAAE;GAC/D;EACF;EACA,IAAI;GACF,MAAM,QAAQ,SAAS;IAAE,SAAS;IAAM;IAAK,SAAS,OAAO;IAAY;GAAW,CAAC;GACrF,OAAO,KAAK,KAAK;IAAE,KAAK;IAAM,SAAS,MAAM;IAAS,QAAQ,MAAM;GAAO,CAAC;EAC9E,SAAS,GAAG;GACV,OAAO,SAAS,KAAK,GAAG,KAAK,uBAAwB,EAAY,SAAS;EAC5E;CACF;AACF;;AAGA,eAAsB,QAAQ,EAC5B,SACA,YAAY,MACZ,OAAO,SACQ,CAAC,GAAwB;CACxC,MAAM,OAAO,KAAK,QAAQ,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,IAAI,CAAC;CACnE,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,WAAW,iBAAiB,UAAU;CAE5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAC5C,eAAe,UAAU;CAEzB,MAAM,QAAQ,gBAAgB,UAAU;EAAE,YAAY,eAAe,UAAU;EAAG;CAAW,CAAC;CAC9F,KAAK,MAAM,KAAK,OAAO;EACrB,GAAG,cAAc,EAAE,MAAM,EAAE,OAAO;EAClC,IAAI,EAAE,YAAY,GAAG,UAAU,EAAE,MAAM,GAAK;CAC9C;CAEA,MAAM,SAAqB;EACzB;EACA;EACA,WAAW,KAAK,KAAK,YAAY,UAAU;EAC3C,SAAS,MAAM,KAAK,MAAM,EAAE,IAAI;EAChC,MAAM,CAAC;EACP,gBAAgB,CAAC;EACjB,iBAAiB,CAAC;EAClB,YAAY,CAAC;EACb,UAAU,CAAC;CACb;CAEA,MAAM,kBAAkB,YAAY,UAAU,MAAM;CAEpD,IAAI,WACF,MAAM,kBAAkB,MAAM,YAAY,UAAU,MAAM,MAAM;CAElE,OAAO;AACT"}
|
|
@@ -32,6 +32,8 @@ interface InitResult {
|
|
|
32
32
|
ghosttyConfigs: string[];
|
|
33
33
|
/** Generated Raycast command scripts. */
|
|
34
34
|
raycastCommands: string[];
|
|
35
|
+
/** Compiled per-box MCP json files (from `config.mcp`). */
|
|
36
|
+
extraFiles: string[];
|
|
35
37
|
/** Non-fatal issues (e.g. app build skipped/failed). */
|
|
36
38
|
warnings: string[];
|
|
37
39
|
}
|
|
@@ -43,4 +45,4 @@ declare function runInit({
|
|
|
43
45
|
}?: InitOptions): Promise<InitResult>;
|
|
44
46
|
//#endregion
|
|
45
47
|
export { runInit as a, discoverProfiles as i, InitOptions as n, InitResult as r, BuiltApp as t };
|
|
46
|
-
//# sourceMappingURL=scaffold-
|
|
48
|
+
//# sourceMappingURL=scaffold-CbsKxlL0.d.ts.map
|
package/lib/utils/config.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as HOME, c as
|
|
2
|
-
export { AppBuilderConfig, AppConfig, BotConfig, Config, HOME, LoadedConfig, PathRules, configsDir, defaultConfig, expandHome, findConfigFile, listBoxes, loadConfig, mergeConfig, resolveBox };
|
|
1
|
+
import { a as HOME, c as PathRules, d as expandHome, f as findConfigFile, g as resolveBox, h as mergeConfig, i as Config, l as configsDir, m as loadConfig, n as AppConfig, o as LoadedConfig, p as listBoxes, r as BotConfig, s as McpServer, t as AppBuilderConfig, u as defaultConfig } from "../config-jZAB2Zg8.js";
|
|
2
|
+
export { AppBuilderConfig, AppConfig, BotConfig, Config, HOME, LoadedConfig, McpServer, PathRules, configsDir, defaultConfig, expandHome, findConfigFile, listBoxes, loadConfig, mergeConfig, resolveBox };
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aliases-DKGcMHHe.js","names":[],"sources":["../src/init/aliases.ts"],"sourcesContent":["// Pure builder for the shell aliases emitted by `clabox init`.\n//\n// For each box config (`<name>.mjs` / `<name>.config.mjs`) it produces one\n// command `clabox-<name>` that runs that box. Whether it runs yolo or asks for\n// confirmation is decided by the box's own preset (its `claudeArgs`), not here.\n// Plus an `index.sh` defining them all as shell functions to `source`.\n\nimport path from 'node:path';\n\n/** A file to be written by `clabox init`. */\nexport interface InitFile {\n /** Absolute path to write. */\n path: string;\n /** File contents. */\n content: string;\n /** Whether to `chmod +x` after writing. */\n executable: boolean;\n}\n\n/** Absolute locations for the generated artifacts. */\nexport interface AliasPaths {\n /** Dir holding the box config files. */\n configsDir: string;\n /** Dir to emit `index.sh` and the per-box wrappers into. */\n scriptsDir: string;\n}\n\n/** The `clabox-<name>` command name for a box. */\nexport function aliasName(profile: string): string {\n return `clabox-${profile}`;\n}\n\n/** Build the source-able `index.sh` defining one function per box. */\nexport function buildIndex(profiles: string[], { configsDir, scriptsDir }: AliasPaths): string {\n const indexPath = path.join(scriptsDir, 'index.sh');\n const usage = profiles.map((p) => `# ${aliasName(p)}`);\n const defs = profiles.map((p) => `${aliasName(p)}() { _clabox_run ${p} \"$@\"; }`);\n return `${[\n '#!/usr/bin/env bash',\n '# Generated by `clabox init` — do not edit; rerun it after changing the configs dir.',\n '#',\n '# Source this from ~/.zshrc or ~/.bashrc:',\n `# source ${indexPath}`,\n '#',\n \"# Commands (run from any cwd). yolo vs. safe is set by each box's preset:\",\n ...usage,\n '',\n '_clabox_run() {',\n // Point `-b` at this dir so resolveBox picks the right .mjs/.config.mjs file.\n ` CLABOX_CONFIGS_DIR=\"${configsDir}\" clabox -b \"$1\" \"\\${@:2}\"`,\n '}',\n '',\n ...defs,\n ].join('\\n')}\\n`;\n}\n\n/** Build a standalone wrapper script that sources `index.sh` and calls one fn. */\nexport function buildWrapper(fnName: string, indexPath: string): string {\n return `${[\n '#!/usr/bin/env bash',\n '# Generated by `clabox init`.',\n 'set -euo pipefail',\n `source \"${indexPath}\"`,\n `${fnName} \"$@\"`,\n ].join('\\n')}\\n`;\n}\n\n/** All files `clabox init` writes: the `index.sh` plus a wrapper per box. */\nexport function buildAliasFiles(profiles: string[], paths: AliasPaths): InitFile[] {\n const indexPath = path.join(paths.scriptsDir, 'index.sh');\n const files: InitFile[] = [\n { path: indexPath, content: buildIndex(profiles, paths), executable: true },\n ];\n for (const p of profiles) {\n const fn = aliasName(p);\n files.push({\n path: path.join(paths.scriptsDir, `${fn}.sh`),\n content: buildWrapper(fn, indexPath),\n executable: true,\n });\n }\n return files;\n}\n"],"mappings":";;;AA4BA,SAAgB,UAAU,SAAyB;CACjD,OAAO,UAAU;AACnB;;AAGA,SAAgB,WAAW,UAAoB,EAAE,YAAY,cAAkC;CAC7F,MAAM,YAAY,KAAK,KAAK,YAAY,UAAU;CAClD,MAAM,QAAQ,SAAS,KAAK,MAAM,OAAO,UAAU,CAAC,GAAG;CACvD,MAAM,OAAO,SAAS,KAAK,MAAM,GAAG,UAAU,CAAC,EAAE,mBAAmB,EAAE,SAAS;CAC/E,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;EACA,GAAG;EACH;EACA;EAEA,yBAAyB,WAAW;EACpC;EACA;EACA,GAAG;CACL,CAAC,CAAC,KAAK,IAAI,EAAE;AACf;;AAGA,SAAgB,aAAa,QAAgB,WAA2B;CACtE,OAAO,GAAG;EACR;EACA;EACA;EACA,WAAW,UAAU;EACrB,GAAG,OAAO;CACZ,CAAC,CAAC,KAAK,IAAI,EAAE;AACf;;AAGA,SAAgB,gBAAgB,UAAoB,OAA+B;CACjF,MAAM,YAAY,KAAK,KAAK,MAAM,YAAY,UAAU;CACxD,MAAM,QAAoB,CACxB;EAAE,MAAM;EAAW,SAAS,WAAW,UAAU,KAAK;EAAG,YAAY;CAAK,CAC5E;CACA,KAAK,MAAM,KAAK,UAAU;EACxB,MAAM,KAAK,UAAU,CAAC;EACtB,MAAM,KAAK;GACT,MAAM,KAAK,KAAK,MAAM,YAAY,GAAG,GAAG,IAAI;GAC5C,SAAS,aAAa,IAAI,SAAS;GACnC,YAAY;EACd,CAAC;CACH;CACA,OAAO;AACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ghostty-DcMEZ6Ey.js","names":[],"sources":["../src/init/ghostty.ts"],"sourcesContent":["// Pure builders for the Ghostty-app artifacts emitted by `clabox init`.\n//\n// For a box that opts in via `app` (see AppConfig), `init` generates a Ghostty\n// config whose `command` launches `clabox -b <box>`, then clones Ghostty.app\n// with a tiny C launcher that bakes in `--config-file=<that config>`. Everything\n// here is text-only (no I/O) so it can be unit-tested without macOS; the actual\n// build lives in init/app.ts.\n\nimport path from 'node:path';\nimport type { AppConfig } from '../utils/config.js';\n\n/** Inputs for {@link buildGhosttyConfig} (all paths already absolute). */\nexport interface GhosttyConfigOptions {\n app: AppConfig;\n /** The `-b` box name this config launches. */\n boxName: string;\n /** Absolute project dir to `cd` into. null → don't `cd` (run in launch cwd). */\n projectDir: string | null;\n /** Absolute `CLABOX_CONFIGS_DIR` so `-b` resolves the box from any cwd. */\n configsDir: string;\n /** Absolute path to the `clabox` binary. */\n claboxBin: string;\n /** Absolute path to a base Ghostty config, emitted as a leading `config-file`. */\n baseGhosttyConfig?: string | null;\n}\n\n/** The `command = zsh -lic '…'` line that boots clabox for the box. */\nexport function buildCommand(opts: GhosttyConfigOptions): string {\n const cd = opts.projectDir ? `cd ${opts.projectDir} && ` : '';\n const inner = `${cd}CLABOX_CONFIGS_DIR=${opts.configsDir} ${opts.claboxBin} -b ${opts.boxName}; exec zsh`;\n // Login + interactive zsh so the GUI-launched app inherits the user's PATH\n // (/etc/zprofile→path_helper for Homebrew, ~/.zshrc for fnm/nvm/volta). A bare\n // `bash -c` gets only launchd's minimal PATH and can't find `node` (clabox's\n // shebang is `#!/usr/bin/env node`).\n return `command = zsh -lic '${inner}'`;\n}\n\n/** Build the Ghostty config text for an app box. */\nexport function buildGhosttyConfig(opts: GhosttyConfigOptions): string {\n const { app } = opts;\n const lines: string[] = [\n '# Generated by `clabox init` — do not edit; rerun it after changing the box config.',\n ];\n if (opts.baseGhosttyConfig) lines.push(`config-file = ${opts.baseGhosttyConfig}`);\n lines.push('', `title = \"${app.title ?? app.name}\"`);\n if (app.macosIcon) lines.push(`macos-icon = ${app.macosIcon}`);\n if (app.ghostty && Object.keys(app.ghostty).length > 0) {\n lines.push('');\n for (const [key, value] of Object.entries(app.ghostty)) lines.push(`${key} = ${value}`);\n }\n lines.push('', buildCommand(opts));\n return `${lines.join('\\n')}\\n`;\n}\n\n/** Escape a string for embedding as a C double-quoted literal. */\nfunction cEscape(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Build the C launcher source. It finds itself, locates `ghostty.real` next to\n * it, and re-execs it with `--config-file=<configPath>` prepended — so the clone\n * always boots with its own config regardless of how it's launched.\n */\nexport function buildLauncherSource(configPath: string): string {\n return `// Generated by clabox init. Launches ghostty.real with a baked config.\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <libgen.h>\n#include <mach-o/dyld.h>\n\nstatic const char *CONFIG_PATH = \"${cEscape(configPath)}\";\n\nint main(int argc, char *argv[]) {\n char path[4096];\n uint32_t size = sizeof(path);\n _NSGetExecutablePath(path, &size);\n\n char *dir = dirname(path);\n char real_path[4096];\n snprintf(real_path, sizeof(real_path), \"%s/ghostty.real\", dir);\n\n char config_arg[4096];\n snprintf(config_arg, sizeof(config_arg), \"--config-file=%s\", CONFIG_PATH);\n\n char **new_argv = malloc(sizeof(char *) * (argc + 2));\n new_argv[0] = real_path;\n new_argv[1] = config_arg;\n for (int i = 1; i < argc; i++) new_argv[i + 1] = argv[i];\n new_argv[argc + 1] = NULL;\n\n execv(real_path, new_argv);\n return 1;\n}\n`;\n}\n\n/** Absolute path to the built `.app` bundle. */\nexport function appBundlePath(appsDir: string, app: AppConfig): string {\n return path.join(appsDir, `${app.name}.app`);\n}\n\n/** Bundle identifier for the clone (explicit, or derived from the box name). */\nexport function bundleId(boxName: string, app: AppConfig): string {\n return app.bundleId ?? `com.ghostty.custom.${boxName.replace(/-/g, '.')}`;\n}\n"],"mappings":";;;AA2BA,SAAgB,aAAa,MAAoC;CAO/D,OAAO,uBAAuB,GANnB,KAAK,aAAa,MAAM,KAAK,WAAW,QAAQ,GACvC,qBAAqB,KAAK,WAAW,GAAG,KAAK,UAAU,MAAM,KAAK,QAAQ,YAK1D;AACtC;;AAGA,SAAgB,mBAAmB,MAAoC;CACrE,MAAM,EAAE,QAAQ;CAChB,MAAM,QAAkB,CACtB,qFACF;CACA,IAAI,KAAK,mBAAmB,MAAM,KAAK,iBAAiB,KAAK,mBAAmB;CAChF,MAAM,KAAK,IAAI,YAAY,IAAI,SAAS,IAAI,KAAK,EAAE;CACnD,IAAI,IAAI,WAAW,MAAM,KAAK,gBAAgB,IAAI,WAAW;CAC7D,IAAI,IAAI,WAAW,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC,SAAS,GAAG;EACtD,MAAM,KAAK,EAAE;EACb,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,OAAO,GAAG,MAAM,KAAK,GAAG,IAAI,KAAK,OAAO;CACxF;CACA,MAAM,KAAK,IAAI,aAAa,IAAI,CAAC;CACjC,OAAO,GAAG,MAAM,KAAK,IAAI,EAAE;AAC7B;;AAGA,SAAS,QAAQ,GAAmB;CAClC,OAAO,EAAE,QAAQ,OAAO,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAK;AACrD;;;;;;AAOA,SAAgB,oBAAoB,YAA4B;CAC9D,OAAO;;;;;;;oCAO2B,QAAQ,UAAU,EAAE;;;;;;;;;;;;;;;;;;;;;;;;AAwBxD;;AAGA,SAAgB,cAAc,SAAiB,KAAwB;CACrE,OAAO,KAAK,KAAK,SAAS,GAAG,IAAI,KAAK,KAAK;AAC7C;;AAGA,SAAgB,SAAS,SAAiB,KAAwB;CAChE,OAAO,IAAI,YAAY,sBAAsB,QAAQ,QAAQ,MAAM,GAAG;AACxE"}
|
package/lib/run-CNehSQ-S.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-CNehSQ-S.js","names":[],"sources":["../src/sandbox/run.ts"],"sourcesContent":["// Profile materialization + launching `claude` under sandbox-exec.\n\nimport { execFileSync, spawnSync } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { type Config, expandHome, HOME } from '../utils/config.js';\nimport { buildProfile, detectPackagePaths } from './profile.js';\n\nconst TMPDIR = (process.env.TMPDIR || '/tmp').replace(/\\/$/, '');\n\n/** Deterministic per-project profile path under TMPDIR. */\nexport function profilePath(projectDir: string = process.cwd()): string {\n const hash = crypto.createHash('sha256').update(projectDir).digest('hex').slice(0, 8);\n return path.join(TMPDIR, `clabox-${path.basename(projectDir)}-${hash}.sb`);\n}\n\n/**\n * Effective project dir: `config.cwd` (with `~` expanded, resolved to an\n * absolute path so SBPL `subpath` rules stay valid) if set, else the shell CWD.\n */\nexport function resolveProjectDir(config: Config): string {\n return config.cwd ? path.resolve(expandHome(config.cwd)) : process.cwd();\n}\n\nfunction which(bin: string): string | null {\n try {\n return (\n execFileSync('command', ['-v', bin], { shell: '/bin/sh', encoding: 'utf8' }).trim() || null\n );\n } catch {\n return null;\n }\n}\n\nfunction requireSandboxExec(): void {\n if (!which('sandbox-exec')) {\n throw new Error('sandbox-exec not found. This tool requires macOS with sandbox-exec.');\n }\n}\n\nfunction resolveClaudeBin(config: Config): string {\n const candidate = config.claudeBin || which('claude') || path.join(HOME, '.local/bin/claude');\n if (!candidate || !fs.existsSync(candidate)) {\n throw new Error(`claude not found at '${candidate}'`);\n }\n return candidate;\n}\n\n/** Generate the profile file for the current project, return its path. */\nexport function generateProfile(\n config: Config,\n projectDir: string = resolveProjectDir(config),\n): string {\n requireSandboxExec();\n const file = profilePath(projectDir);\n const text = buildProfile(config, { projectDir, detectedPaths: detectPackagePaths() });\n fs.writeFileSync(file, text);\n return file;\n}\n\n/** Build the `env KEY=VALUE …` argument list forced onto the sandboxed claude. */\nexport function buildEnvArgs(config: Config): string[] {\n const sshDir = expandHome(config.bot.sshDir);\n const botKey = path.join(sshDir, 'id_ed25519');\n const botCfg = path.join(sshDir, 'config');\n const args = [\n `PATH=${path.join(HOME, '.local/bin')}:${process.env.PATH || ''}`,\n `CLAUDE_CONFIG_DIR=${expandHome(config.configDir)}`,\n `GIT_AUTHOR_NAME=${config.bot.name}`,\n `GIT_AUTHOR_EMAIL=${config.bot.email}`,\n `GIT_COMMITTER_NAME=${config.bot.name}`,\n `GIT_COMMITTER_EMAIL=${config.bot.email}`,\n 'GIT_CONFIG_COUNT=2',\n 'GIT_CONFIG_KEY_0=commit.gpgsign',\n 'GIT_CONFIG_VALUE_0=false',\n 'GIT_CONFIG_KEY_1=tag.gpgsign',\n 'GIT_CONFIG_VALUE_1=false',\n ];\n // Pin git ssh to the bot key only when it actually exists, so the sandbox\n // stays usable without a dedicated bot key configured.\n if (fs.existsSync(botKey)) {\n args.push(\n `GIT_SSH_COMMAND=ssh -F ${botCfg} -i ${botKey} -o IdentitiesOnly=yes -o IdentityAgent=none`,\n );\n }\n // User-declared extras go last so they win over the built-in vars above\n // (duplicate keys: `env` keeps the last assignment).\n for (const [key, value] of Object.entries(config.env ?? {})) {\n args.push(`${key}=${value}`);\n }\n return args;\n}\n\n/** Options accepted by {@link runClaude}. */\nexport interface RunOptions {\n configFile?: string | null;\n}\n\n/** Generate the profile and exec claude under sandbox-exec. Returns exit code. */\nexport function runClaude(\n config: Config,\n claudeArgs: string[],\n { configFile }: RunOptions = {},\n): number {\n const projectDir = resolveProjectDir(config);\n const claudeBin = resolveClaudeBin(config);\n const profileFile = generateProfile(config, projectDir);\n\n if (process.env.CLABOX_DEBUG) {\n console.error(`→ Running Claude Code sandboxed in: ${projectDir}`);\n console.error(`→ Profile: ${profileFile}`);\n console.error(`→ Config: ${expandHome(config.configDir)}`);\n if (configFile) console.error(`→ Config file: ${configFile}`);\n }\n\n // Terminal title = cwd (with ~ for $HOME), matching the bash version.\n const title = projectDir.startsWith(HOME) ? `~${projectDir.slice(HOME.length)}` : projectDir;\n process.stdout.write(`\\x1b]0;${title}\\x07`);\n\n const envArgs = buildEnvArgs(config);\n const defaultArgs = Array.isArray(config.claudeArgs) ? config.claudeArgs : [];\n const inner = [\n 'sandbox-exec',\n '-f',\n profileFile,\n 'env',\n ...envArgs,\n claudeBin,\n ...defaultArgs,\n ...claudeArgs,\n ];\n\n // `ulimit` is a shell builtin; run the whole thing under sh so we can set it.\n // `exec \"$@\"` keeps argv intact without re-quoting (args start after $0=sh).\n const ulimit = config.ulimitProcs > 0 ? `ulimit -u ${config.ulimitProcs} 2>/dev/null; ` : '';\n const res = spawnSync('/bin/sh', ['-c', `${ulimit}exec \"$@\"`, 'sh', ...inner], {\n cwd: projectDir,\n stdio: 'inherit',\n });\n if (res.error) throw res.error;\n if (res.signal) return 1;\n return res.status ?? 0;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold-B7pUVGoC.js","names":[],"sources":["../src/init/scaffold.ts"],"sourcesContent":["// I/O for `clabox init`: discover the box configs in `<base>/configs/`,\n// (re)write the shell aliases into `<base>/scripts/`, and — for boxes that opt\n// in via `app` — generate a Ghostty config in `<base>/ghostty/` and build a\n// standalone `<appsDir>/<name>.app` (see init/app.ts).\n\nimport { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { type Config, expandHome, listBoxes, loadConfig, resolveBox } from '../utils/config.js';\nimport { buildAliasFiles } from './aliases.js';\nimport { buildApp, canBuildApps } from './app.js';\nimport { appBundlePath, buildGhosttyConfig } from './ghostty.js';\nimport { buildRaycastCommand } from './raycast.js';\n\n/**\n * Box names (sorted, de-duplicated) discovered in `<configsDir>` via the same\n * rules as `-b`: both `<name>.mjs` and `<name>.config.mjs`, `_`-prefixed\n * shared partials (e.g. `_presets.mjs`) skipped.\n */\nexport function discoverProfiles(configsDir: string): string[] {\n if (!fs.existsSync(configsDir)) {\n throw new Error(`clabox init: configs dir not found: ${configsDir}`);\n }\n const names = listBoxes(configsDir);\n if (names.length === 0) {\n throw new Error(`clabox init: no box configs (*.mjs) in ${configsDir}`);\n }\n return names;\n}\n\n/** Remove previously generated artifacts (`index.sh`, `clabox-*.sh`). */\nfunction pruneGenerated(scriptsDir: string): void {\n if (!fs.existsSync(scriptsDir)) return;\n for (const f of fs.readdirSync(scriptsDir)) {\n if (f === 'index.sh' || (f.startsWith('clabox-') && f.endsWith('.sh'))) {\n fs.rmSync(path.join(scriptsDir, f), { force: true });\n }\n }\n}\n\n/** Remove files in `dir` whose name ends with `ext` (a no-op if dir is absent). */\nfunction pruneByExt(dir: string, ext: string): void {\n if (!fs.existsSync(dir)) return;\n for (const f of fs.readdirSync(dir)) {\n if (f.endsWith(ext)) fs.rmSync(path.join(dir, f), { force: true });\n }\n}\n\n/** Locate the `clabox` binary to bake into the generated Ghostty `command`. */\nfunction resolveClaboxBin(configured: string | null): string {\n if (configured) return expandHome(configured);\n try {\n const found = execFileSync('command', ['-v', 'clabox'], {\n shell: '/bin/sh',\n encoding: 'utf8',\n }).trim();\n if (found) return found;\n } catch {\n // fall through\n }\n return 'clabox';\n}\n\n/** Options for {@link runInit}. */\nexport interface InitOptions {\n /** Base dir holding `configs/` and `scripts/`. Default: `<cwd>/__`. */\n baseDir?: string;\n /** Build the Ghostty apps for `app` boxes. Default: true. */\n buildApps?: boolean;\n /** Limit app building to a single box (by box name or app display name). */\n only?: string | null;\n}\n\n/** A standalone Ghostty app built by `clabox init`. */\nexport interface BuiltApp {\n box: string;\n appPath: string;\n signed: 'identity' | 'adhoc';\n}\n\n/** Result of {@link runInit}. */\nexport interface InitResult {\n profiles: string[];\n scriptsDir: string;\n indexFile: string;\n written: string[];\n /** Apps successfully built. */\n apps: BuiltApp[];\n /** Generated Ghostty config files. */\n ghosttyConfigs: string[];\n /** Generated Raycast command scripts. */\n raycastCommands: string[];\n /** Non-fatal issues (e.g. app build skipped/failed). */\n warnings: string[];\n}\n\n/** Generate the Ghostty configs and build the apps for every `app` box. */\nasync function buildAppArtifacts(\n base: string,\n configsDir: string,\n profiles: string[],\n only: string | null,\n result: InitResult,\n): Promise<void> {\n // Load each box config; keep the ones that opt into an app (and match `only`).\n const appBoxes: { name: string; config: Config }[] = [];\n for (const name of profiles) {\n const { config } = await loadConfig(resolveBox(name, configsDir));\n if (!config.app) continue;\n if (only && name !== only && config.app.name !== only) continue;\n appBoxes.push({ name, config });\n }\n if (appBoxes.length === 0) return;\n\n const ghosttyDir = path.join(base, 'ghostty');\n const raycastDir = path.join(base, 'raycast');\n fs.mkdirSync(ghosttyDir, { recursive: true });\n fs.mkdirSync(raycastDir, { recursive: true });\n // Only prune on a full run — with `only` we'd orphan other apps' artifacts.\n if (!only) {\n pruneByExt(ghosttyDir, '.config');\n pruneByExt(raycastDir, '.sh');\n }\n\n for (const { name, config } of appBoxes) {\n const app = config.app;\n if (!app) continue; // narrowed above; keeps the type checker happy\n const configPath = path.join(ghosttyDir, `${name}.config`);\n const projectDir = config.cwd ? path.resolve(expandHome(config.cwd)) : null;\n const baseGhostty = config.appBuilder.baseGhosttyConfig\n ? expandHome(config.appBuilder.baseGhosttyConfig)\n : null;\n fs.writeFileSync(\n configPath,\n buildGhosttyConfig({\n app,\n boxName: name,\n projectDir,\n configsDir,\n claboxBin: resolveClaboxBin(config.appBuilder.claboxBin),\n baseGhosttyConfig: baseGhostty,\n }),\n );\n result.ghosttyConfigs.push(configPath);\n\n // Raycast command that opens the (to be) built bundle.\n const appPath = appBundlePath(expandHome(config.appBuilder.appsDir), app);\n const raycastPath = path.join(raycastDir, `${name}.sh`);\n fs.writeFileSync(raycastPath, buildRaycastCommand({ app, appPath }));\n fs.chmodSync(raycastPath, 0o755);\n result.raycastCommands.push(raycastPath);\n\n const check = canBuildApps(config.appBuilder);\n if (!check.ok) {\n result.warnings.push(`${name}: app not built (${check.reason})`);\n continue;\n }\n try {\n const built = buildApp({ boxName: name, app, builder: config.appBuilder, configPath });\n result.apps.push({ box: name, appPath: built.appPath, signed: built.signed });\n } catch (e) {\n result.warnings.push(`${name}: app build failed — ${(e as Error).message}`);\n }\n }\n}\n\n/** Scan the configs dir, (re)write the alias scripts, and build `app` apps. */\nexport async function runInit({\n baseDir,\n buildApps = true,\n only = null,\n}: InitOptions = {}): Promise<InitResult> {\n const base = path.resolve(baseDir ?? path.join(process.cwd(), '__'));\n const configsDir = path.join(base, 'configs');\n const scriptsDir = path.join(base, 'scripts');\n const profiles = discoverProfiles(configsDir);\n\n fs.mkdirSync(scriptsDir, { recursive: true });\n pruneGenerated(scriptsDir);\n\n const files = buildAliasFiles(profiles, { configsDir, scriptsDir });\n for (const f of files) {\n fs.writeFileSync(f.path, f.content);\n if (f.executable) fs.chmodSync(f.path, 0o755);\n }\n\n const result: InitResult = {\n profiles,\n scriptsDir,\n indexFile: path.join(scriptsDir, 'index.sh'),\n written: files.map((f) => f.path),\n apps: [],\n ghosttyConfigs: [],\n raycastCommands: [],\n warnings: [],\n };\n\n if (buildApps) {\n await buildAppArtifacts(base, configsDir, profiles, only, result);\n }\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBA,SAAgB,iBAAiB,YAA8B;CAC7D,IAAI,CAAC,GAAG,WAAW,UAAU,GAC3B,MAAM,IAAI,MAAM,uCAAuC,YAAY;CAErE,MAAM,QAAQ,UAAU,UAAU;CAClC,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,0CAA0C,YAAY;CAExE,OAAO;AACT;;AAGA,SAAS,eAAe,YAA0B;CAChD,IAAI,CAAC,GAAG,WAAW,UAAU,GAAG;CAChC,KAAK,MAAM,KAAK,GAAG,YAAY,UAAU,GACvC,IAAI,MAAM,cAAe,EAAE,WAAW,SAAS,KAAK,EAAE,SAAS,KAAK,GAClE,GAAG,OAAO,KAAK,KAAK,YAAY,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AAGzD;;AAGA,SAAS,WAAW,KAAa,KAAmB;CAClD,IAAI,CAAC,GAAG,WAAW,GAAG,GAAG;CACzB,KAAK,MAAM,KAAK,GAAG,YAAY,GAAG,GAChC,IAAI,EAAE,SAAS,GAAG,GAAG,GAAG,OAAO,KAAK,KAAK,KAAK,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC;AAErE;;AAGA,SAAS,iBAAiB,YAAmC;CAC3D,IAAI,YAAY,OAAO,WAAW,UAAU;CAC5C,IAAI;EACF,MAAM,QAAQ,aAAa,WAAW,CAAC,MAAM,QAAQ,GAAG;GACtD,OAAO;GACP,UAAU;EACZ,CAAC,CAAC,CAAC,KAAK;EACR,IAAI,OAAO,OAAO;CACpB,QAAQ,CAER;CACA,OAAO;AACT;;AAoCA,eAAe,kBACb,MACA,YACA,UACA,MACA,QACe;CAEf,MAAM,WAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,UAAU,CAAC;EAChE,IAAI,CAAC,OAAO,KAAK;EACjB,IAAI,QAAQ,SAAS,QAAQ,OAAO,IAAI,SAAS,MAAM;EACvD,SAAS,KAAK;GAAE;GAAM;EAAO,CAAC;CAChC;CACA,IAAI,SAAS,WAAW,GAAG;CAE3B,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAC5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAE5C,IAAI,CAAC,MAAM;EACT,WAAW,YAAY,SAAS;EAChC,WAAW,YAAY,KAAK;CAC9B;CAEA,KAAK,MAAM,EAAE,MAAM,YAAY,UAAU;EACvC,MAAM,MAAM,OAAO;EACnB,IAAI,CAAC,KAAK;EACV,MAAM,aAAa,KAAK,KAAK,YAAY,GAAG,KAAK,QAAQ;EACzD,MAAM,aAAa,OAAO,MAAM,KAAK,QAAQ,WAAW,OAAO,GAAG,CAAC,IAAI;EACvE,MAAM,cAAc,OAAO,WAAW,oBAClC,WAAW,OAAO,WAAW,iBAAiB,IAC9C;EACJ,GAAG,cACD,YACA,mBAAmB;GACjB;GACA,SAAS;GACT;GACA;GACA,WAAW,iBAAiB,OAAO,WAAW,SAAS;GACvD,mBAAmB;EACrB,CAAC,CACH;EACA,OAAO,eAAe,KAAK,UAAU;EAGrC,MAAM,UAAU,cAAc,WAAW,OAAO,WAAW,OAAO,GAAG,GAAG;EACxE,MAAM,cAAc,KAAK,KAAK,YAAY,GAAG,KAAK,IAAI;EACtD,GAAG,cAAc,aAAa,oBAAoB;GAAE;GAAK;EAAQ,CAAC,CAAC;EACnE,GAAG,UAAU,aAAa,GAAK;EAC/B,OAAO,gBAAgB,KAAK,WAAW;EAEvC,MAAM,QAAQ,aAAa,OAAO,UAAU;EAC5C,IAAI,CAAC,MAAM,IAAI;GACb,OAAO,SAAS,KAAK,GAAG,KAAK,mBAAmB,MAAM,OAAO,EAAE;GAC/D;EACF;EACA,IAAI;GACF,MAAM,QAAQ,SAAS;IAAE,SAAS;IAAM;IAAK,SAAS,OAAO;IAAY;GAAW,CAAC;GACrF,OAAO,KAAK,KAAK;IAAE,KAAK;IAAM,SAAS,MAAM;IAAS,QAAQ,MAAM;GAAO,CAAC;EAC9E,SAAS,GAAG;GACV,OAAO,SAAS,KAAK,GAAG,KAAK,uBAAwB,EAAY,SAAS;EAC5E;CACF;AACF;;AAGA,eAAsB,QAAQ,EAC5B,SACA,YAAY,MACZ,OAAO,SACQ,CAAC,GAAwB;CACxC,MAAM,OAAO,KAAK,QAAQ,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,IAAI,CAAC;CACnE,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,aAAa,KAAK,KAAK,MAAM,SAAS;CAC5C,MAAM,WAAW,iBAAiB,UAAU;CAE5C,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAC5C,eAAe,UAAU;CAEzB,MAAM,QAAQ,gBAAgB,UAAU;EAAE;EAAY;CAAW,CAAC;CAClE,KAAK,MAAM,KAAK,OAAO;EACrB,GAAG,cAAc,EAAE,MAAM,EAAE,OAAO;EAClC,IAAI,EAAE,YAAY,GAAG,UAAU,EAAE,MAAM,GAAK;CAC9C;CAEA,MAAM,SAAqB;EACzB;EACA;EACA,WAAW,KAAK,KAAK,YAAY,UAAU;EAC3C,SAAS,MAAM,KAAK,MAAM,EAAE,IAAI;EAChC,MAAM,CAAC;EACP,gBAAgB,CAAC;EACjB,iBAAiB,CAAC;EAClB,UAAU,CAAC;CACb;CAEA,IAAI,WACF,MAAM,kBAAkB,MAAM,YAAY,UAAU,MAAM,MAAM;CAElE,OAAO;AACT"}
|