clabox 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/clabox.config.example.mjs +44 -0
- package/docs/guideline.md +43 -10
- package/lib/aliases-DKGcMHHe.js +60 -0
- package/lib/aliases-DKGcMHHe.js.map +1 -0
- package/lib/aliases-DXyz-ufw.d.ts +31 -0
- package/lib/app-CQmESEdh.js +254 -0
- package/lib/app-CQmESEdh.js.map +1 -0
- package/lib/app-DzQ5yZfD.d.ts +32 -0
- package/lib/cli.js +46 -9
- package/lib/cli.js.map +1 -1
- package/lib/{config-DXTNeUhH.js → config-BQ44iVWT.js} +56 -4
- package/lib/config-BQ44iVWT.js.map +1 -0
- package/lib/config-DQWueb4a.d.ts +134 -0
- package/lib/ghostty-By4zmOuk.d.ts +34 -0
- package/lib/ghostty-DcMEZ6Ey.js +74 -0
- package/lib/ghostty-DcMEZ6Ey.js.map +1 -0
- package/lib/index.d.ts +9 -4
- package/lib/index.js +9 -4
- package/lib/init/aliases.d.ts +2 -0
- package/lib/init/aliases.js +2 -0
- package/lib/init/app.d.ts +2 -0
- package/lib/init/app.js +2 -0
- package/lib/init/ghostty.d.ts +2 -0
- package/lib/init/ghostty.js +2 -0
- package/lib/init/raycast.d.ts +2 -0
- package/lib/init/raycast.js +2 -0
- package/lib/init/scaffold.d.ts +2 -0
- package/lib/init/scaffold.js +2 -0
- package/lib/{profile-Bw6L1MiV.d.ts → profile-BeM41NXc.d.ts} +2 -2
- package/lib/{profile-CxqsgezL.js → profile-DM6NAgb-.js} +9 -12
- package/lib/profile-DM6NAgb-.js.map +1 -0
- package/lib/raycast-BCdO2Se1.js +35 -0
- package/lib/raycast-BCdO2Se1.js.map +1 -0
- package/lib/raycast-DM7c559f.d.ts +22 -0
- package/lib/{run-Dyp_hW97.js → run-CNehSQ-S.js} +20 -11
- package/lib/run-CNehSQ-S.js.map +1 -0
- package/lib/{run-BfF3Cwg7.d.ts → run-Cx8cuTh5.d.ts} +10 -3
- package/lib/sandbox/profile.d.ts +1 -1
- package/lib/sandbox/profile.js +1 -1
- package/lib/sandbox/run.d.ts +2 -2
- package/lib/sandbox/run.js +2 -2
- package/lib/scaffold-B7pUVGoC.js +141 -0
- package/lib/scaffold-B7pUVGoC.js.map +1 -0
- package/lib/scaffold-ByIbYAeS.d.ts +46 -0
- package/lib/utils/config.d.ts +2 -2
- package/lib/utils/config.js +2 -2
- package/package.json +1 -1
- package/lib/config-CUyriGxm.d.ts +0 -67
- package/lib/config-DXTNeUhH.js.map +0 -1
- package/lib/profile-CxqsgezL.js.map +0 -1
- package/lib/run-Dyp_hW97.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import fs from "node:fs";
|
|
1
|
+
import { i as expandHome, t as HOME } from "./config-BQ44iVWT.js";
|
|
3
2
|
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
4
|
//#region src/sandbox/profile.ts
|
|
5
5
|
const q = (s) => `"${String(s).replace(/"/g, "\\\"")}"`;
|
|
6
6
|
const subpath = (p) => `(subpath ${q(p)})`;
|
|
@@ -77,20 +77,17 @@ function buildProfile(config, { projectDir, detectedPaths = detectPackagePaths()
|
|
|
77
77
|
add("User-level preference reads (RO)", allow("file-read*", subpath(path.join(HOME, "Library/Preferences"))));
|
|
78
78
|
add("Keychain access (for OAuth)", [allow("file-read* file-write*", subpath(path.join(HOME, "Library/Keychains"))), allow("mach-lookup", globalName("com.apple.SecurityServer"), globalName("com.apple.security.agent"), globalName("com.apple.securityd"), globalName("com.apple.secd"), globalName("com.apple.trustd"), globalName("com.apple.trustd.agent"), globalName("com.apple.CoreAuthentication.daemon"))].join("\n"));
|
|
79
79
|
add("git config (RO)", allow("file-read*", literal(path.join(HOME, ".gitconfig")), literal(path.join(HOME, ".gitignore_global")), subpath(path.join(HOME, ".config/git"))));
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
denyRules.push(...config.paths.deny.map((p) => subpath(expandHome(p))));
|
|
83
|
-
add("explicit sensitive DENY list", deny("file-read* file-write*", ...denyRules));
|
|
84
|
-
add("SSH: bot key only, deny other keys", [
|
|
85
|
-
allow("file-read*", literal(path.join(HOME, ".ssh")), literal(path.join(HOME, ".ssh/known_hosts")), literal(path.join(HOME, ".ssh/known_hosts2")), literal(path.join(HOME, ".ssh/config")), subpath(sshDir)),
|
|
86
|
-
allow("file-write*", literal(path.join(HOME, ".ssh/known_hosts")), literal(path.join(HOME, ".ssh/known_hosts2"))),
|
|
87
|
-
deny("file-read* file-write*", regex(`^${homeRe}/\\.ssh/id_`), regex(`^${homeRe}/\\.ssh/.*\\.pem$`), regex(`^${homeRe}/\\.ssh/.*\\.key$`))
|
|
88
|
-
].join("\n"));
|
|
80
|
+
add("soft privacy DENY list (overridable by explicit grants)", deny("file-read* file-write*", ...[...config.denyHome.map((d) => subpath(path.join(HOME, d))), ...config.paths.deny.map((p) => subpath(expandHome(p)))]));
|
|
81
|
+
add("SSH: bot key + known_hosts (personal keys hard-denied at the very end)", [allow("file-read*", literal(path.join(HOME, ".ssh")), literal(path.join(HOME, ".ssh/known_hosts")), literal(path.join(HOME, ".ssh/known_hosts2")), literal(path.join(HOME, ".ssh/config")), subpath(sshDir)), allow("file-write*", literal(path.join(HOME, ".ssh/known_hosts")), literal(path.join(HOME, ".ssh/known_hosts2")))].join("\n"));
|
|
89
82
|
add("claude hooks (RO + exec)", hooksDir && fs.existsSync(hooksDir) ? [allow("file-read* file-map-executable", subpath(hooksDir)), allow("process-exec", subpath(hooksDir))].join("\n") : ";; (no hooks dir; set config.hooksDir / CLABOX_HOOKS_DIR to enable)");
|
|
90
83
|
if (config.paths.readOnly.length) add("extra read-only paths", allow("file-read*", ...config.paths.readOnly.map((p) => subpath(expandHome(p)))));
|
|
91
84
|
if (config.paths.readWrite.length) add("extra read-write paths", allow("file-read* file-write*", ...config.paths.readWrite.map((p) => subpath(expandHome(p)))));
|
|
92
85
|
if (config.paths.exec.length) add("extra exec paths", allow("process-exec", ...config.paths.exec.map((p) => subpath(expandHome(p)))));
|
|
93
86
|
add("project workspace (RW)", [allow("file-read* file-write* file-map-executable", subpath(projectDir)), allow("process-exec", subpath(projectDir))].join("\n"));
|
|
87
|
+
const hardDeny = [];
|
|
88
|
+
if (config.denyDotConfigs.length) hardDeny.push(regex(`^${homeRe}/\\.(${config.denyDotConfigs.join("|")})($|/)`));
|
|
89
|
+
hardDeny.push(regex(`^${homeRe}/\\.ssh/id_`), regex(`^${homeRe}/\\.ssh/.*\\.pem$`), regex(`^${homeRe}/\\.ssh/.*\\.key$`));
|
|
90
|
+
add("hard secret DENY (always wins — credentials & private keys)", deny("file-read* file-write*", ...hardDeny));
|
|
94
91
|
if (config.network) add("networking", "(allow network*)");
|
|
95
92
|
sections.push("(allow process-fork)\n(allow lsopen)");
|
|
96
93
|
const text = `${sections.join("\n\n")}\n`;
|
|
@@ -100,4 +97,4 @@ function buildProfile(config, { projectDir, detectedPaths = detectPackagePaths()
|
|
|
100
97
|
//#endregion
|
|
101
98
|
export { literal as a, subpath as c, ipcName as i, detectPackagePaths as n, reEscape as o, globalName as r, regex as s, buildProfile as t };
|
|
102
99
|
|
|
103
|
-
//# sourceMappingURL=profile-
|
|
100
|
+
//# sourceMappingURL=profile-DM6NAgb-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-DM6NAgb-.js","names":[],"sources":["../src/sandbox/profile.ts"],"sourcesContent":["// Seatbelt (SBPL) profile generator.\n//\n// The old bash version baked the profile into a heredoc and patched it with\n// sed. Here the profile is assembled from small typed helpers, so the parts\n// you actually want to tweak live in config.ts as plain data.\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { type Config, expandHome, HOME } from '../utils/config.js';\n\n// ---- SBPL helpers ----------------------------------------------------------\n\n// Quote a literal string for SBPL. Only `\"` needs escaping; backslashes are\n// left as-is so regex patterns survive verbatim.\nconst q = (s: string): string => `\"${String(s).replace(/\"/g, '\\\\\"')}\"`;\n\nexport const subpath = (p: string): string => `(subpath ${q(p)})`;\nexport const literal = (p: string): string => `(literal ${q(p)})`;\nexport const regex = (p: string): string => `(regex ${q(p)})`;\nexport const globalName = (n: string): string => `(global-name ${q(n)})`;\nexport const ipcName = (n: string): string => `(ipc-posix-name ${q(n)})`;\n\n/** Escape a path for safe embedding inside an SBPL regex. */\nexport const reEscape = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nfunction block(op: string, rules: string[]): string {\n return [`(${op}`, ...rules.map((r) => ` ${r}`), ')'].join('\\n');\n}\nconst allow = (op: string, ...rules: string[]): string => block(`allow ${op}`, rules);\nconst deny = (op: string, ...rules: string[]): string => block(`deny ${op}`, rules);\n\n// ---- package-manager autodetection ----------------------------------------\n\n/** Detect installed package managers whose paths must be readable/executable. */\nexport function detectPackagePaths(): string[] {\n const paths: string[] = [];\n if (fs.existsSync('/opt/homebrew')) paths.push('/opt/homebrew');\n else if (fs.existsSync('/usr/local/Homebrew')) paths.push('/usr/local/Homebrew');\n const local = path.join(HOME, '.local');\n if (fs.existsSync(local)) paths.push(local);\n if (fs.existsSync('/nix/store')) paths.push('/nix/store');\n return paths;\n}\n\n/** Context needed to assemble a profile for a specific project. */\nexport interface ProfileContext {\n projectDir: string;\n detectedPaths?: string[];\n}\n\n/**\n * Build the full SBPL profile text.\n * @param config effective config (see config.ts)\n * @param ctx { projectDir, detectedPaths }\n */\nexport function buildProfile(\n config: Config,\n { projectDir, detectedPaths = detectPackagePaths() }: ProfileContext,\n): string {\n const configDir = expandHome(config.configDir);\n const sshDir = expandHome(config.bot.sshDir);\n const hooksDir = config.hooksDir ? expandHome(config.hooksDir) : null;\n const homeRe = reEscape(HOME);\n\n const sections: string[] = [];\n const add = (comment: string, body: string) => sections.push(`;; ---------- ${comment}\\n${body}`);\n\n sections.push(\n [\n ';; ------------------------------------------------------------------',\n ';; Claude Code macOS sandbox profile (autogenerated)',\n ';; ------------------------------------------------------------------',\n '(version 1)',\n '(deny default)',\n ].join('\\n'),\n );\n\n add('introspection & sysctl', '(allow file-read-metadata)\\n(allow sysctl-read)');\n\n add(\n 'basic dir traversal',\n [\n allow('file-read*', literal('/')),\n allow('file-read*', literal('/private')),\n allow('file-read-data', literal('/Users')),\n allow('file-read-data', literal(HOME)),\n ].join('\\n'),\n );\n\n add(\n 'system runtime (read-only)',\n allow(\n 'file-read* file-map-executable',\n subpath('/System'),\n subpath('/usr'),\n subpath('/bin'),\n subpath('/sbin'),\n subpath('/Library/Frameworks'),\n subpath('/private/etc'),\n subpath('/var/db/dyld'),\n ...detectedPaths.map(subpath),\n ),\n );\n\n add(\n 'Xcode / Command Line Tools (xcrun, git, etc.)',\n [\n allow(\n 'file-read* file-map-executable',\n subpath('/Library/Developer/CommandLineTools'),\n subpath('/Applications/Xcode.app'),\n ),\n allow(\n 'process-exec',\n subpath('/Library/Developer/CommandLineTools'),\n subpath('/Applications/Xcode.app'),\n ),\n ].join('\\n'),\n );\n\n // global npm/pipx/cargo bins (user-installed)\n const userPaths = detectedPaths.filter((p) => p.endsWith('/.local'));\n add(\n 'global npm/pipx/cargo bins',\n userPaths.length\n ? userPaths.map((p) => allow('file-read*', subpath(p))).join('\\n')\n : ';; No user package paths detected',\n );\n\n add(\n 'executable paths',\n allow(\n 'process-exec',\n subpath('/usr'),\n subpath('/System'),\n subpath('/bin'),\n subpath('/sbin'),\n literal('/usr/bin/env'),\n ...detectedPaths.map(subpath),\n ),\n );\n\n add(\n 'temp dirs',\n allow(\n 'file-read* file-write*',\n subpath('/tmp'),\n subpath('/private/tmp'),\n regex('^/private/var/folders/'),\n ),\n );\n\n add('Claude config & token files', allow('file-read* file-write*', subpath(configDir)));\n\n add(\n 'Claude auto-update (RO) -- suppress warnings',\n allow(\n 'file-read*',\n subpath(path.join(HOME, '.local/state/claude')),\n subpath(path.join(HOME, '.cache/claude')),\n ),\n );\n\n add(\n 'time-zone & prefs (RO)',\n allow('file-read*', subpath('/private/var/db/timezone'), subpath('/Library/Preferences')),\n );\n\n add(\n '/dev access (RO) + ioctl',\n [\n allow('file-read*', literal('/dev')),\n allow('file-read* file-write*', regex('^/dev/(tty.*|null|zero|dtracehelper)')),\n allow('file-ioctl', literal('/dev/dtracehelper'), regex('^/dev/tty.*')),\n ].join('\\n'),\n );\n\n add(\n 'mach-lookup services',\n allow(\n 'mach-lookup',\n globalName('com.apple.system.opendirectoryd.libinfo'),\n globalName('com.apple.SystemConfiguration.DNSConfiguration'),\n globalName('com.apple.coreservices.launchservicesd'),\n globalName('com.apple.CoreServices.coreservicesd'),\n globalName('com.apple.system.notification_center'),\n globalName('com.apple.logd'),\n globalName('com.apple.diagnosticd'),\n globalName('com.apple.lsd.mapdb'),\n globalName('com.apple.lsd.modifydb'),\n globalName('com.apple.coreservices.quarantine-resolver'),\n globalName('com.apple.pasteboard.pboard'),\n globalName('com.apple.pasteboard.1'),\n ),\n );\n\n add(\n 'Launch Services needed by /usr/bin/open',\n allow('mach-lookup', regex('^com\\\\.apple\\\\.lsd(\\\\..*)?$')),\n );\n\n add(\n 'Developer Tools (xcrun / libxcrun)',\n allow('mach-lookup', globalName('com.apple.dt.xcsecurity'), regex('^com\\\\.apple\\\\.dt\\\\..*$')),\n );\n\n add(\n 'Audio (afplay)',\n allow(\n 'mach-lookup',\n globalName('com.apple.audio.audiohald'),\n globalName('com.apple.audio.AudioComponentRegistrar'),\n ),\n );\n\n add(\n 'Notification Center shared-memory (RO)',\n allow('ipc-posix-shm-read-data', ipcName('apple.shm.notification_center')),\n );\n\n add(\n 'User-level preference reads (RO)',\n allow('file-read*', subpath(path.join(HOME, 'Library/Preferences'))),\n );\n\n // Keychain RW so Claude can persist refreshed OAuth tokens (else ~24h → 401).\n add(\n 'Keychain access (for OAuth)',\n [\n allow('file-read* file-write*', subpath(path.join(HOME, 'Library/Keychains'))),\n allow(\n 'mach-lookup',\n globalName('com.apple.SecurityServer'),\n globalName('com.apple.security.agent'),\n globalName('com.apple.securityd'),\n globalName('com.apple.secd'),\n globalName('com.apple.trustd'),\n globalName('com.apple.trustd.agent'),\n globalName('com.apple.CoreAuthentication.daemon'),\n ),\n ].join('\\n'),\n );\n\n add(\n 'git config (RO)',\n allow(\n 'file-read*',\n literal(path.join(HOME, '.gitconfig')),\n literal(path.join(HOME, '.gitignore_global')),\n subpath(path.join(HOME, '.config/git')),\n ),\n );\n\n // Soft privacy DENY list — placed BEFORE the extra readOnly/readWrite and the\n // project dir, so an explicit grant may override it (e.g. running on a project\n // that lives under ~/Documents). The hard secret deny below is what's binding.\n const softDeny = [\n ...config.denyHome.map((d) => subpath(path.join(HOME, d))),\n ...config.paths.deny.map((p) => subpath(expandHome(p))),\n ];\n add(\n 'soft privacy DENY list (overridable by explicit grants)',\n deny('file-read* file-write*', ...softDeny),\n );\n\n add(\n 'SSH: bot key + known_hosts (personal keys hard-denied at the very end)',\n [\n allow(\n 'file-read*',\n literal(path.join(HOME, '.ssh')),\n literal(path.join(HOME, '.ssh/known_hosts')),\n literal(path.join(HOME, '.ssh/known_hosts2')),\n literal(path.join(HOME, '.ssh/config')),\n subpath(sshDir),\n ),\n allow(\n 'file-write*',\n literal(path.join(HOME, '.ssh/known_hosts')),\n literal(path.join(HOME, '.ssh/known_hosts2')),\n ),\n ].join('\\n'),\n );\n\n add(\n 'claude hooks (RO + exec)',\n hooksDir && fs.existsSync(hooksDir)\n ? [\n allow('file-read* file-map-executable', subpath(hooksDir)),\n allow('process-exec', subpath(hooksDir)),\n ].join('\\n')\n : ';; (no hooks dir; set config.hooksDir / CLABOX_HOOKS_DIR to enable)',\n );\n\n // Extra user-supplied RO / RW / exec rules.\n if (config.paths.readOnly.length)\n add(\n 'extra read-only paths',\n allow('file-read*', ...config.paths.readOnly.map((p) => subpath(expandHome(p)))),\n );\n if (config.paths.readWrite.length)\n add(\n 'extra read-write paths',\n allow('file-read* file-write*', ...config.paths.readWrite.map((p) => subpath(expandHome(p)))),\n );\n if (config.paths.exec.length)\n add(\n 'extra exec paths',\n allow('process-exec', ...config.paths.exec.map((p) => subpath(expandHome(p)))),\n );\n\n add(\n 'project workspace (RW)',\n [\n allow('file-read* file-write* file-map-executable', subpath(projectDir)),\n allow('process-exec', subpath(projectDir)),\n ].join('\\n'),\n );\n\n // Hard secret DENY — emitted LAST of all file rules so it wins even over a\n // broad readOnly/readWrite or the project dir (SBPL = last matching rule\n // wins). This is the binding invariant: personal credentials and private keys\n // are never readable, however wide the grants above. Only the bot key subdir\n // (allowed earlier, and not matched by these patterns) stays readable.\n const hardDeny: string[] = [];\n if (config.denyDotConfigs.length) {\n hardDeny.push(regex(`^${homeRe}/\\\\.(${config.denyDotConfigs.join('|')})($|/)`));\n }\n hardDeny.push(\n regex(`^${homeRe}/\\\\.ssh/id_`),\n regex(`^${homeRe}/\\\\.ssh/.*\\\\.pem$`),\n regex(`^${homeRe}/\\\\.ssh/.*\\\\.key$`),\n );\n add(\n 'hard secret DENY (always wins — credentials & private keys)',\n deny('file-read* file-write*', ...hardDeny),\n );\n\n if (config.network) add('networking', '(allow network*)');\n\n sections.push('(allow process-fork)\\n(allow lsopen)');\n\n const text = `${sections.join('\\n\\n')}\\n`;\n\n // Sanity-check before anyone feeds it to sandbox-exec.\n if (!/^\\(version 1\\)/m.test(text)) {\n throw new Error('generated sandbox profile is missing \"(version 1)\"');\n }\n return text;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/init/raycast.ts
|
|
2
|
+
/** Single-quote a string for safe use in a POSIX shell. */
|
|
3
|
+
function shellQuote(s) {
|
|
4
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The Raycast command icon: explicit `app.emoji`, else the title's leading
|
|
8
|
+
* token when it isn't plain ASCII (i.e. an emoji), else the Ghostty 👻.
|
|
9
|
+
*/
|
|
10
|
+
function raycastIcon(app) {
|
|
11
|
+
if (app.emoji) return app.emoji;
|
|
12
|
+
const lead = (app.title ?? "").split(" ")[0];
|
|
13
|
+
if (lead && !/^[\x00-\x7F]*$/.test(lead)) return lead;
|
|
14
|
+
return "👻";
|
|
15
|
+
}
|
|
16
|
+
/** Build a Raycast script command that opens the box's built app. */
|
|
17
|
+
function buildRaycastCommand({ app, appPath }) {
|
|
18
|
+
return `${[
|
|
19
|
+
"#!/bin/bash",
|
|
20
|
+
"# Generated by `clabox init`.",
|
|
21
|
+
"",
|
|
22
|
+
"# @raycast.schemaVersion 1",
|
|
23
|
+
`# @raycast.title ${app.title ?? app.name}`,
|
|
24
|
+
"# @raycast.mode silent",
|
|
25
|
+
`# @raycast.icon ${raycastIcon(app)}`,
|
|
26
|
+
"# @raycast.packageName Ghostty",
|
|
27
|
+
`# @raycast.description Open ${app.name} with Claude Code in Ghostty (clabox)`,
|
|
28
|
+
"",
|
|
29
|
+
`open ${shellQuote(appPath)}`
|
|
30
|
+
].join("\n")}\n`;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { raycastIcon as n, buildRaycastCommand as t };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=raycast-BCdO2Se1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raycast-BCdO2Se1.js","names":[],"sources":["../src/init/raycast.ts"],"sourcesContent":["// Pure builder for the Raycast script commands emitted by `clabox init`.\n//\n// For each `app` box, `init` writes a Raycast script command (`@raycast.*`\n// metadata + `open <app>`) so the built bundle is launchable from Raycast —\n// modelled on the hand-written ghostty-*.sh examples. Text only; the fs I/O\n// lives in init/scaffold.ts.\n\nimport type { AppConfig } from '../utils/config.js';\n\n/** Inputs for {@link buildRaycastCommand}. */\nexport interface RaycastCommandOptions {\n app: AppConfig;\n /** Absolute path to the built `.app` to open. */\n appPath: string;\n}\n\n/** Single-quote a string for safe use in a POSIX shell. */\nfunction shellQuote(s: string): string {\n return `'${s.replace(/'/g, `'\\\\''`)}'`;\n}\n\n/**\n * The Raycast command icon: explicit `app.emoji`, else the title's leading\n * token when it isn't plain ASCII (i.e. an emoji), else the Ghostty 👻.\n */\nexport function raycastIcon(app: AppConfig): string {\n if (app.emoji) return app.emoji;\n const lead = (app.title ?? '').split(' ')[0];\n // biome-ignore lint/suspicious/noControlCharactersInRegex: ASCII-range guard\n if (lead && !/^[\\x00-\\x7F]*$/.test(lead)) return lead;\n return '👻';\n}\n\n/** Build a Raycast script command that opens the box's built app. */\nexport function buildRaycastCommand({ app, appPath }: RaycastCommandOptions): string {\n const title = app.title ?? app.name;\n return `${[\n '#!/bin/bash',\n '# Generated by `clabox init`.',\n '',\n '# @raycast.schemaVersion 1',\n `# @raycast.title ${title}`,\n '# @raycast.mode silent',\n `# @raycast.icon ${raycastIcon(app)}`,\n '# @raycast.packageName Ghostty',\n `# @raycast.description Open ${app.name} with Claude Code in Ghostty (clabox)`,\n '',\n `open ${shellQuote(appPath)}`,\n ].join('\\n')}\\n`;\n}\n"],"mappings":";;AAiBA,SAAS,WAAW,GAAmB;CACrC,OAAO,IAAI,EAAE,QAAQ,MAAM,OAAO,EAAE;AACtC;;;;;AAMA,SAAgB,YAAY,KAAwB;CAClD,IAAI,IAAI,OAAO,OAAO,IAAI;CAC1B,MAAM,QAAQ,IAAI,SAAS,GAAA,CAAI,MAAM,GAAG,CAAC,CAAC;CAE1C,IAAI,QAAQ,CAAC,iBAAiB,KAAK,IAAI,GAAG,OAAO;CACjD,OAAO;AACT;;AAGA,SAAgB,oBAAoB,EAAE,KAAK,WAA0C;CAEnF,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA,oBANY,IAAI,SAAS,IAAI;EAO7B;EACA,mBAAmB,YAAY,GAAG;EAClC;EACA,+BAA+B,IAAI,KAAK;EACxC;EACA,QAAQ,WAAW,OAAO;CAC5B,CAAC,CAAC,KAAK,IAAI,EAAE;AACf"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { n as AppConfig } from "./config-DQWueb4a.js";
|
|
2
|
+
|
|
3
|
+
//#region src/init/raycast.d.ts
|
|
4
|
+
/** Inputs for {@link buildRaycastCommand}. */
|
|
5
|
+
interface RaycastCommandOptions {
|
|
6
|
+
app: AppConfig;
|
|
7
|
+
/** Absolute path to the built `.app` to open. */
|
|
8
|
+
appPath: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The Raycast command icon: explicit `app.emoji`, else the title's leading
|
|
12
|
+
* token when it isn't plain ASCII (i.e. an emoji), else the Ghostty 👻.
|
|
13
|
+
*/
|
|
14
|
+
declare function raycastIcon(app: AppConfig): string;
|
|
15
|
+
/** Build a Raycast script command that opens the box's built app. */
|
|
16
|
+
declare function buildRaycastCommand({
|
|
17
|
+
app,
|
|
18
|
+
appPath
|
|
19
|
+
}: RaycastCommandOptions): string;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { buildRaycastCommand as n, raycastIcon as r, RaycastCommandOptions as t };
|
|
22
|
+
//# sourceMappingURL=raycast-DM7c559f.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as detectPackagePaths, t as buildProfile } from "./profile-
|
|
1
|
+
import { i as expandHome, t as HOME } from "./config-BQ44iVWT.js";
|
|
2
|
+
import { n as detectPackagePaths, t as buildProfile } from "./profile-DM6NAgb-.js";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
4
|
-
import crypto from "node:crypto";
|
|
5
5
|
import fs from "node:fs";
|
|
6
|
-
import
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
7
|
//#region src/sandbox/run.ts
|
|
8
8
|
const TMPDIR = (process.env.TMPDIR || "/tmp").replace(/\/$/, "");
|
|
9
9
|
/** Deterministic per-project profile path under TMPDIR. */
|
|
@@ -11,6 +11,13 @@ function profilePath(projectDir = process.cwd()) {
|
|
|
11
11
|
const hash = crypto.createHash("sha256").update(projectDir).digest("hex").slice(0, 8);
|
|
12
12
|
return path.join(TMPDIR, `clabox-${path.basename(projectDir)}-${hash}.sb`);
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Effective project dir: `config.cwd` (with `~` expanded, resolved to an
|
|
16
|
+
* absolute path so SBPL `subpath` rules stay valid) if set, else the shell CWD.
|
|
17
|
+
*/
|
|
18
|
+
function resolveProjectDir(config) {
|
|
19
|
+
return config.cwd ? path.resolve(expandHome(config.cwd)) : process.cwd();
|
|
20
|
+
}
|
|
14
21
|
function which(bin) {
|
|
15
22
|
try {
|
|
16
23
|
return execFileSync("command", ["-v", bin], {
|
|
@@ -30,7 +37,7 @@ function resolveClaudeBin(config) {
|
|
|
30
37
|
return candidate;
|
|
31
38
|
}
|
|
32
39
|
/** Generate the profile file for the current project, return its path. */
|
|
33
|
-
function generateProfile(config, projectDir =
|
|
40
|
+
function generateProfile(config, projectDir = resolveProjectDir(config)) {
|
|
34
41
|
requireSandboxExec();
|
|
35
42
|
const file = profilePath(projectDir);
|
|
36
43
|
const text = buildProfile(config, {
|
|
@@ -48,8 +55,6 @@ function buildEnvArgs(config) {
|
|
|
48
55
|
const args = [
|
|
49
56
|
`PATH=${path.join(HOME, ".local/bin")}:${process.env.PATH || ""}`,
|
|
50
57
|
`CLAUDE_CONFIG_DIR=${expandHome(config.configDir)}`,
|
|
51
|
-
"DISABLE_AUTOUPDATER=1",
|
|
52
|
-
"NPM_CONFIG_USERCONFIG=/dev/null",
|
|
53
58
|
`GIT_AUTHOR_NAME=${config.bot.name}`,
|
|
54
59
|
`GIT_AUTHOR_EMAIL=${config.bot.email}`,
|
|
55
60
|
`GIT_COMMITTER_NAME=${config.bot.name}`,
|
|
@@ -61,11 +66,12 @@ function buildEnvArgs(config) {
|
|
|
61
66
|
"GIT_CONFIG_VALUE_1=false"
|
|
62
67
|
];
|
|
63
68
|
if (fs.existsSync(botKey)) args.push(`GIT_SSH_COMMAND=ssh -F ${botCfg} -i ${botKey} -o IdentitiesOnly=yes -o IdentityAgent=none`);
|
|
69
|
+
for (const [key, value] of Object.entries(config.env ?? {})) args.push(`${key}=${value}`);
|
|
64
70
|
return args;
|
|
65
71
|
}
|
|
66
72
|
/** Generate the profile and exec claude under sandbox-exec. Returns exit code. */
|
|
67
73
|
function runClaude(config, claudeArgs, { configFile } = {}) {
|
|
68
|
-
const projectDir =
|
|
74
|
+
const projectDir = resolveProjectDir(config);
|
|
69
75
|
const claudeBin = resolveClaudeBin(config);
|
|
70
76
|
const profileFile = generateProfile(config, projectDir);
|
|
71
77
|
if (process.env.CLABOX_DEBUG) {
|
|
@@ -93,12 +99,15 @@ function runClaude(config, claudeArgs, { configFile } = {}) {
|
|
|
93
99
|
`${config.ulimitProcs > 0 ? `ulimit -u ${config.ulimitProcs} 2>/dev/null; ` : ""}exec "$@"`,
|
|
94
100
|
"sh",
|
|
95
101
|
...inner
|
|
96
|
-
], {
|
|
102
|
+
], {
|
|
103
|
+
cwd: projectDir,
|
|
104
|
+
stdio: "inherit"
|
|
105
|
+
});
|
|
97
106
|
if (res.error) throw res.error;
|
|
98
107
|
if (res.signal) return 1;
|
|
99
108
|
return res.status ?? 0;
|
|
100
109
|
}
|
|
101
110
|
//#endregion
|
|
102
|
-
export {
|
|
111
|
+
export { runClaude as a, resolveProjectDir as i, generateProfile as n, profilePath as r, buildEnvArgs as t };
|
|
103
112
|
|
|
104
|
-
//# sourceMappingURL=run-
|
|
113
|
+
//# sourceMappingURL=run-CNehSQ-S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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,10 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as Config } from "./config-DQWueb4a.js";
|
|
2
2
|
|
|
3
3
|
//#region src/sandbox/run.d.ts
|
|
4
4
|
/** Deterministic per-project profile path under TMPDIR. */
|
|
5
5
|
declare function profilePath(projectDir?: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Effective project dir: `config.cwd` (with `~` expanded, resolved to an
|
|
8
|
+
* absolute path so SBPL `subpath` rules stay valid) if set, else the shell CWD.
|
|
9
|
+
*/
|
|
10
|
+
declare function resolveProjectDir(config: Config): string;
|
|
6
11
|
/** Generate the profile file for the current project, return its path. */
|
|
7
12
|
declare function generateProfile(config: Config, projectDir?: string): string;
|
|
13
|
+
/** Build the `env KEY=VALUE …` argument list forced onto the sandboxed claude. */
|
|
14
|
+
declare function buildEnvArgs(config: Config): string[];
|
|
8
15
|
/** Options accepted by {@link runClaude}. */
|
|
9
16
|
interface RunOptions {
|
|
10
17
|
configFile?: string | null;
|
|
@@ -14,5 +21,5 @@ declare function runClaude(config: Config, claudeArgs: string[], {
|
|
|
14
21
|
configFile
|
|
15
22
|
}?: RunOptions): number;
|
|
16
23
|
//#endregion
|
|
17
|
-
export {
|
|
18
|
-
//# sourceMappingURL=run-
|
|
24
|
+
export { resolveProjectDir as a, profilePath as i, buildEnvArgs as n, runClaude as o, generateProfile as r, RunOptions as t };
|
|
25
|
+
//# sourceMappingURL=run-Cx8cuTh5.d.ts.map
|
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-BeM41NXc.js";
|
|
2
2
|
export { ProfileContext, buildProfile, detectPackagePaths, globalName, ipcName, literal, reEscape, regex, subpath };
|
package/lib/sandbox/profile.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
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-
|
|
1
|
+
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";
|
|
2
2
|
export { buildProfile, detectPackagePaths, globalName, ipcName, literal, reEscape, regex, subpath };
|
package/lib/sandbox/run.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
export { RunOptions, generateProfile, profilePath, runClaude };
|
|
1
|
+
import { a as resolveProjectDir, i as profilePath, n as buildEnvArgs, o as runClaude, r as generateProfile, t as RunOptions } from "../run-Cx8cuTh5.js";
|
|
2
|
+
export { RunOptions, buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude };
|
package/lib/sandbox/run.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
export { generateProfile, profilePath, runClaude };
|
|
1
|
+
import { a as runClaude, i as resolveProjectDir, n as generateProfile, r as profilePath, t as buildEnvArgs } from "../run-CNehSQ-S.js";
|
|
2
|
+
export { buildEnvArgs, generateProfile, profilePath, resolveProjectDir, runClaude };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { i as expandHome, l as resolveBox, o as listBoxes, s as loadConfig } from "./config-BQ44iVWT.js";
|
|
2
|
+
import { n as buildAliasFiles } from "./aliases-DKGcMHHe.js";
|
|
3
|
+
import { r as buildGhosttyConfig, t as appBundlePath } from "./ghostty-DcMEZ6Ey.js";
|
|
4
|
+
import { n as canBuildApps, t as buildApp } from "./app-CQmESEdh.js";
|
|
5
|
+
import { t as buildRaycastCommand } from "./raycast-BCdO2Se1.js";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
//#region src/init/scaffold.ts
|
|
10
|
+
/**
|
|
11
|
+
* Box names (sorted, de-duplicated) discovered in `<configsDir>` via the same
|
|
12
|
+
* rules as `-b`: both `<name>.mjs` and `<name>.config.mjs`, `_`-prefixed
|
|
13
|
+
* shared partials (e.g. `_presets.mjs`) skipped.
|
|
14
|
+
*/
|
|
15
|
+
function discoverProfiles(configsDir) {
|
|
16
|
+
if (!fs.existsSync(configsDir)) throw new Error(`clabox init: configs dir not found: ${configsDir}`);
|
|
17
|
+
const names = listBoxes(configsDir);
|
|
18
|
+
if (names.length === 0) throw new Error(`clabox init: no box configs (*.mjs) in ${configsDir}`);
|
|
19
|
+
return names;
|
|
20
|
+
}
|
|
21
|
+
/** Remove previously generated artifacts (`index.sh`, `clabox-*.sh`). */
|
|
22
|
+
function pruneGenerated(scriptsDir) {
|
|
23
|
+
if (!fs.existsSync(scriptsDir)) return;
|
|
24
|
+
for (const f of fs.readdirSync(scriptsDir)) if (f === "index.sh" || f.startsWith("clabox-") && f.endsWith(".sh")) fs.rmSync(path.join(scriptsDir, f), { force: true });
|
|
25
|
+
}
|
|
26
|
+
/** Remove files in `dir` whose name ends with `ext` (a no-op if dir is absent). */
|
|
27
|
+
function pruneByExt(dir, ext) {
|
|
28
|
+
if (!fs.existsSync(dir)) return;
|
|
29
|
+
for (const f of fs.readdirSync(dir)) if (f.endsWith(ext)) fs.rmSync(path.join(dir, f), { force: true });
|
|
30
|
+
}
|
|
31
|
+
/** Locate the `clabox` binary to bake into the generated Ghostty `command`. */
|
|
32
|
+
function resolveClaboxBin(configured) {
|
|
33
|
+
if (configured) return expandHome(configured);
|
|
34
|
+
try {
|
|
35
|
+
const found = execFileSync("command", ["-v", "clabox"], {
|
|
36
|
+
shell: "/bin/sh",
|
|
37
|
+
encoding: "utf8"
|
|
38
|
+
}).trim();
|
|
39
|
+
if (found) return found;
|
|
40
|
+
} catch {}
|
|
41
|
+
return "clabox";
|
|
42
|
+
}
|
|
43
|
+
/** Generate the Ghostty configs and build the apps for every `app` box. */
|
|
44
|
+
async function buildAppArtifacts(base, configsDir, profiles, only, result) {
|
|
45
|
+
const appBoxes = [];
|
|
46
|
+
for (const name of profiles) {
|
|
47
|
+
const { config } = await loadConfig(resolveBox(name, configsDir));
|
|
48
|
+
if (!config.app) continue;
|
|
49
|
+
if (only && name !== only && config.app.name !== only) continue;
|
|
50
|
+
appBoxes.push({
|
|
51
|
+
name,
|
|
52
|
+
config
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (appBoxes.length === 0) return;
|
|
56
|
+
const ghosttyDir = path.join(base, "ghostty");
|
|
57
|
+
const raycastDir = path.join(base, "raycast");
|
|
58
|
+
fs.mkdirSync(ghosttyDir, { recursive: true });
|
|
59
|
+
fs.mkdirSync(raycastDir, { recursive: true });
|
|
60
|
+
if (!only) {
|
|
61
|
+
pruneByExt(ghosttyDir, ".config");
|
|
62
|
+
pruneByExt(raycastDir, ".sh");
|
|
63
|
+
}
|
|
64
|
+
for (const { name, config } of appBoxes) {
|
|
65
|
+
const app = config.app;
|
|
66
|
+
if (!app) continue;
|
|
67
|
+
const configPath = path.join(ghosttyDir, `${name}.config`);
|
|
68
|
+
const projectDir = config.cwd ? path.resolve(expandHome(config.cwd)) : null;
|
|
69
|
+
const baseGhostty = config.appBuilder.baseGhosttyConfig ? expandHome(config.appBuilder.baseGhosttyConfig) : null;
|
|
70
|
+
fs.writeFileSync(configPath, buildGhosttyConfig({
|
|
71
|
+
app,
|
|
72
|
+
boxName: name,
|
|
73
|
+
projectDir,
|
|
74
|
+
configsDir,
|
|
75
|
+
claboxBin: resolveClaboxBin(config.appBuilder.claboxBin),
|
|
76
|
+
baseGhosttyConfig: baseGhostty
|
|
77
|
+
}));
|
|
78
|
+
result.ghosttyConfigs.push(configPath);
|
|
79
|
+
const appPath = appBundlePath(expandHome(config.appBuilder.appsDir), app);
|
|
80
|
+
const raycastPath = path.join(raycastDir, `${name}.sh`);
|
|
81
|
+
fs.writeFileSync(raycastPath, buildRaycastCommand({
|
|
82
|
+
app,
|
|
83
|
+
appPath
|
|
84
|
+
}));
|
|
85
|
+
fs.chmodSync(raycastPath, 493);
|
|
86
|
+
result.raycastCommands.push(raycastPath);
|
|
87
|
+
const check = canBuildApps(config.appBuilder);
|
|
88
|
+
if (!check.ok) {
|
|
89
|
+
result.warnings.push(`${name}: app not built (${check.reason})`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const built = buildApp({
|
|
94
|
+
boxName: name,
|
|
95
|
+
app,
|
|
96
|
+
builder: config.appBuilder,
|
|
97
|
+
configPath
|
|
98
|
+
});
|
|
99
|
+
result.apps.push({
|
|
100
|
+
box: name,
|
|
101
|
+
appPath: built.appPath,
|
|
102
|
+
signed: built.signed
|
|
103
|
+
});
|
|
104
|
+
} catch (e) {
|
|
105
|
+
result.warnings.push(`${name}: app build failed — ${e.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Scan the configs dir, (re)write the alias scripts, and build `app` apps. */
|
|
110
|
+
async function runInit({ baseDir, buildApps = true, only = null } = {}) {
|
|
111
|
+
const base = path.resolve(baseDir ?? path.join(process.cwd(), "__"));
|
|
112
|
+
const configsDir = path.join(base, "configs");
|
|
113
|
+
const scriptsDir = path.join(base, "scripts");
|
|
114
|
+
const profiles = discoverProfiles(configsDir);
|
|
115
|
+
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
116
|
+
pruneGenerated(scriptsDir);
|
|
117
|
+
const files = buildAliasFiles(profiles, {
|
|
118
|
+
configsDir,
|
|
119
|
+
scriptsDir
|
|
120
|
+
});
|
|
121
|
+
for (const f of files) {
|
|
122
|
+
fs.writeFileSync(f.path, f.content);
|
|
123
|
+
if (f.executable) fs.chmodSync(f.path, 493);
|
|
124
|
+
}
|
|
125
|
+
const result = {
|
|
126
|
+
profiles,
|
|
127
|
+
scriptsDir,
|
|
128
|
+
indexFile: path.join(scriptsDir, "index.sh"),
|
|
129
|
+
written: files.map((f) => f.path),
|
|
130
|
+
apps: [],
|
|
131
|
+
ghosttyConfigs: [],
|
|
132
|
+
raycastCommands: [],
|
|
133
|
+
warnings: []
|
|
134
|
+
};
|
|
135
|
+
if (buildApps) await buildAppArtifacts(base, configsDir, profiles, only, result);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { runInit as n, discoverProfiles as t };
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=scaffold-B7pUVGoC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/init/scaffold.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Box names (sorted, de-duplicated) discovered in `<configsDir>` via the same
|
|
4
|
+
* rules as `-b`: both `<name>.mjs` and `<name>.config.mjs`, `_`-prefixed
|
|
5
|
+
* shared partials (e.g. `_presets.mjs`) skipped.
|
|
6
|
+
*/
|
|
7
|
+
declare function discoverProfiles(configsDir: string): string[];
|
|
8
|
+
/** Options for {@link runInit}. */
|
|
9
|
+
interface InitOptions {
|
|
10
|
+
/** Base dir holding `configs/` and `scripts/`. Default: `<cwd>/__`. */
|
|
11
|
+
baseDir?: string;
|
|
12
|
+
/** Build the Ghostty apps for `app` boxes. Default: true. */
|
|
13
|
+
buildApps?: boolean;
|
|
14
|
+
/** Limit app building to a single box (by box name or app display name). */
|
|
15
|
+
only?: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** A standalone Ghostty app built by `clabox init`. */
|
|
18
|
+
interface BuiltApp {
|
|
19
|
+
box: string;
|
|
20
|
+
appPath: string;
|
|
21
|
+
signed: 'identity' | 'adhoc';
|
|
22
|
+
}
|
|
23
|
+
/** Result of {@link runInit}. */
|
|
24
|
+
interface InitResult {
|
|
25
|
+
profiles: string[];
|
|
26
|
+
scriptsDir: string;
|
|
27
|
+
indexFile: string;
|
|
28
|
+
written: string[];
|
|
29
|
+
/** Apps successfully built. */
|
|
30
|
+
apps: BuiltApp[];
|
|
31
|
+
/** Generated Ghostty config files. */
|
|
32
|
+
ghosttyConfigs: string[];
|
|
33
|
+
/** Generated Raycast command scripts. */
|
|
34
|
+
raycastCommands: string[];
|
|
35
|
+
/** Non-fatal issues (e.g. app build skipped/failed). */
|
|
36
|
+
warnings: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Scan the configs dir, (re)write the alias scripts, and build `app` apps. */
|
|
39
|
+
declare function runInit({
|
|
40
|
+
baseDir,
|
|
41
|
+
buildApps,
|
|
42
|
+
only
|
|
43
|
+
}?: InitOptions): Promise<InitResult>;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { runInit as a, discoverProfiles as i, InitOptions as n, InitResult as r, BuiltApp as t };
|
|
46
|
+
//# sourceMappingURL=scaffold-ByIbYAeS.d.ts.map
|
package/lib/utils/config.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { BotConfig, Config, HOME, LoadedConfig, PathRules, defaultConfig, expandHome, findConfigFile, loadConfig, mergeConfig };
|
|
1
|
+
import { a as HOME, c as configsDir, d as findConfigFile, f as listBoxes, h as resolveBox, i as Config, l as defaultConfig, m as mergeConfig, n as AppConfig, o as LoadedConfig, p as loadConfig, r as BotConfig, s as PathRules, t as AppBuilderConfig, u as expandHome } from "../config-DQWueb4a.js";
|
|
2
|
+
export { AppBuilderConfig, AppConfig, BotConfig, Config, HOME, LoadedConfig, PathRules, configsDir, defaultConfig, expandHome, findConfigFile, listBoxes, loadConfig, mergeConfig, resolveBox };
|
package/lib/utils/config.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { HOME, defaultConfig, expandHome, findConfigFile, loadConfig, mergeConfig };
|
|
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
|
+
export { HOME, configsDir, defaultConfig, expandHome, findConfigFile, listBoxes, loadConfig, mergeConfig, resolveBox };
|
package/package.json
CHANGED
package/lib/config-CUyriGxm.d.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
//#region src/utils/config.d.ts
|
|
2
|
-
declare const HOME: string;
|
|
3
|
-
/** Expand a leading `~` / `~/` to the user's home directory. */
|
|
4
|
-
declare function expandHome(p: string): string;
|
|
5
|
-
/** Dedicated git/ssh identity for commits made from inside the sandbox. */
|
|
6
|
-
interface BotConfig {
|
|
7
|
-
name: string;
|
|
8
|
-
email: string;
|
|
9
|
-
/** If `${sshDir}/id_ed25519` exists, git ssh is pinned to it. */
|
|
10
|
-
sshDir: string;
|
|
11
|
-
}
|
|
12
|
-
/** Extra rules layered on top of the built-in base profile. */
|
|
13
|
-
interface PathRules {
|
|
14
|
-
/** RW subpaths (beyond project dir + configDir + /tmp). */
|
|
15
|
-
readWrite: string[];
|
|
16
|
-
/** RO subpaths. */
|
|
17
|
-
readOnly: string[];
|
|
18
|
-
/** process-exec subpaths. */
|
|
19
|
-
exec: string[];
|
|
20
|
-
/** explicit deny subpaths (read + write). */
|
|
21
|
-
deny: string[];
|
|
22
|
-
}
|
|
23
|
-
/** Effective clabox configuration. */
|
|
24
|
-
interface Config {
|
|
25
|
-
/** Path to the `claude` binary. null → autodetect (PATH, then ~/.local/bin). */
|
|
26
|
-
claudeBin: string | null;
|
|
27
|
-
/** Claude config/profile directory — supports multiple accounts. */
|
|
28
|
-
configDir: string;
|
|
29
|
-
/** Extra args always passed to `claude`, before any args from the CLI. */
|
|
30
|
-
claudeArgs: string[];
|
|
31
|
-
bot: BotConfig;
|
|
32
|
-
/** Allow outbound network. `false` → no `(allow network*)` line. */
|
|
33
|
-
network: boolean;
|
|
34
|
-
/** Cap the process table inside the sandbox (fork-bomb guard). 0 → skip. */
|
|
35
|
-
ulimitProcs: number;
|
|
36
|
-
/** Extra directory granted read + execute inside the sandbox. null → disabled. */
|
|
37
|
-
hooksDir: string | null;
|
|
38
|
-
paths: PathRules;
|
|
39
|
-
/** Home subdirectories denied entirely (read + write). */
|
|
40
|
-
denyHome: string[];
|
|
41
|
-
/** Dotfile config dirs under $HOME denied entirely. */
|
|
42
|
-
denyDotConfigs: string[];
|
|
43
|
-
}
|
|
44
|
-
/** Built-in defaults. Everything here is meant to be overridable. */
|
|
45
|
-
declare const defaultConfig: Config;
|
|
46
|
-
/** Merge a (partial) override over a full config, returning a new config. */
|
|
47
|
-
declare function mergeConfig(base: Config, override: unknown): Config;
|
|
48
|
-
/**
|
|
49
|
-
* Locate a config file: explicit (CLI arg, then `CLABOX_CONFIG` env),
|
|
50
|
-
* then CWD, then ~/.config. The CLI arg wins over the env var.
|
|
51
|
-
*/
|
|
52
|
-
declare function findConfigFile(explicit?: string | null): string | null;
|
|
53
|
-
/** Result of {@link loadConfig}: the effective config and the file it came from. */
|
|
54
|
-
interface LoadedConfig {
|
|
55
|
-
config: Config;
|
|
56
|
-
configFile: string | null;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Build the effective config: defaults ⊕ env ⊕ config file.
|
|
60
|
-
*
|
|
61
|
-
* @param explicitConfig optional config-file path (e.g. from `--config`);
|
|
62
|
-
* takes precedence over `CLABOX_CONFIG` and the default lookup locations.
|
|
63
|
-
*/
|
|
64
|
-
declare function loadConfig(explicitConfig?: string | null): Promise<LoadedConfig>;
|
|
65
|
-
//#endregion
|
|
66
|
-
export { PathRules as a, findConfigFile as c, LoadedConfig as i, loadConfig as l, Config as n, defaultConfig as o, HOME as r, expandHome as s, BotConfig as t, mergeConfig as u };
|
|
67
|
-
//# sourceMappingURL=config-CUyriGxm.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-DXTNeUhH.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/** 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 /** 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 /** 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\n/** Built-in defaults. Everything here is meant to be overridable. */\nexport const defaultConfig: Config = {\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 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};\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/** 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":";;;;;AAeA,MAAa,OAAO,GAAG,QAAQ;;AAG/B,SAAgB,WAAW,GAAmB;CAC5C,IAAI,CAAC,GAAG,OAAO;CACf,IAAI,MAAM,KAAK,OAAO;CACtB,IAAI,EAAE,WAAW,IAAI,GAAG,OAAO,KAAK,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD,OAAO;AACT;AAEA,MAAM,MAAM,QAAQ;;AA6CpB,MAAa,gBAAwB;CACnC,WAAW,IAAI,qBAAqB;CACpC,WAAW,IAAI,qBAAqB;CACpC,YAAY,CAAC,cAAc,kCAAgC;CAC3D,KAAK;EACH,MAAM,IAAI,mBAAmB;EAC7B,OAAO,IAAI,oBAAoB;EAC/B,QAAQ,IAAI,sBAAsB;CACpC;CACA,SAAS;CACT,aAAa;CACb,UAAU,IAAI,oBAAoB;CAClC,OAAO;EACL,WAAW,CAAC;EACZ,UAAU,CAAC;EACX,MAAM,CAAC;EACP,MAAM,CAAC;CACT;CACA,UAAU;EAAC;EAAa;EAAW;EAAa;EAAY;EAAU;CAAO;CAE7E,gBAAgB;EAAC;EAAO;EAAS;EAAQ;EAAU;CAAQ;AAC7D;AAIA,SAAS,cAAc,GAAwB;CAC7C,OAAO,KAAK,QAAQ,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AAC/D;;AAGA,SAAS,UAAU,MAAa,UAAwB;CACtD,MAAM,MAAa,EAAE,GAAG,KAAK;CAC7B,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,GAAG;EACjD,IAAI,QAAQ,KAAA,GAAW;EACvB,MAAM,UAAU,KAAK;EACrB,IAAI,OAAO,cAAc,GAAG,KAAK,cAAc,OAAO,IAAI,UAAU,SAAS,GAAG,IAAI;CACtF;CACA,OAAO;AACT;;AAGA,SAAgB,YAAY,MAAc,UAA2B;CACnE,IAAI,CAAC,cAAc,QAAQ,GAAG,OAAO;CACrC,OAAO,UAAU,MAA0B,QAAQ;AACrD;;;;;AAMA,SAAgB,eAAe,UAAyC;CACtE,MAAM,SAAS,YAAY,IAAI;CAC/B,IAAI,QAAQ,OAAO,WAAW,MAAM;CAMpC,OAAO;EAJL,KAAK,KAAK,QAAQ,IAAI,GAAG,mBAAmB;EAC5C,KAAK,KAAK,QAAQ,IAAI,GAAG,kBAAkB;EAC3C,KAAK,KAAK,MAAM,WAAW,UAAU,YAAY;CAEnC,CAAC,CAAC,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,KAAK;AACrD;;;;;;;AAcA,eAAsB,WAAW,gBAAuD;CACtF,IAAI,MAAc;CAClB,MAAM,OAAO,eAAe,cAAc;CAC1C,IAAI,MAAM;EACR,MAAM,MAAM,MAAM,OAAO,cAAc,IAAI,CAAC,CAAC;EAC7C,MAAM,WAAW,IAAI,WAAW,IAAI,UAAU;EAE9C,MAAM,YAAY,eADD,OAAO,aAAa,aAAa,MAAM,SAAS,aAAa,IAAI,QACzC;CAC3C;CACA,OAAO;EAAE,QAAQ;EAAK,YAAY;CAAK;AACzC"}
|