scripts/config.mjs
scripts/config.mjsBrowse 13 files
1,451 tokens
5,521 bytes
Token encoding: o200k_base
Snapshot bfcf687
← Back to SKILL.md
1// Configuration for the sandbox-bench scripts.2//3// Everything machine- or team-specific lives in a config file OUTSIDE the4// repo (never commit team/project names into the repo):5// ~/.config/sandbox-bench/config.json6//7// Required fields (no defaults — ask the user once, then save):8// team Vercel team slug the sandboxes bill to9// project Vercel project the sandboxes attach to10// Optional fields:11// cacheDir where clones/artifacts/snapshot-ids live12// (default ~/.cache/sandbox-bench)13// reactRepo existing react checkout to use (default: auto-clone14// into <cacheDir>/react)15// nextRepo existing next.js checkout to use (default: auto-clone16// into <cacheDir>/next)17// reactRepoUrl clone/fetch URL (default https://github.com/react/react.git)18// nextRepoUrl clone/fetch URL (default https://github.com/vercel/next.js.git)19// vercelBin vercel CLI binary (default "vercel")20//21// CLI: node config.mjs show22// node config.mjs set team=<slug> project=<name> [key=value...]23import fs from 'node:fs'24import os from 'node:os'25import path from 'node:path'26import { execFileSync } from 'node:child_process'27 28const CONFIG_DIR = path.join(os.homedir(), '.config', 'sandbox-bench')29const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json')30 31export function loadConfig({ requireScope = true } = {}) {32 let cfg = {}33 if (fs.existsSync(CONFIG_FILE)) {34 cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))35 }36 // Env overrides for CI or one-off use.37 cfg.team = process.env.SANDBOX_BENCH_TEAM ?? cfg.team38 cfg.project = process.env.SANDBOX_BENCH_PROJECT ?? cfg.project39 cfg.cacheDir = expandHome(40 process.env.SANDBOX_BENCH_CACHE ?? cfg.cacheDir ?? '~/.cache/sandbox-bench'41 )42 cfg.reactRepo = expandHome(43 process.env.SANDBOX_BENCH_REACT_REPO ?? cfg.reactRepo ?? ''44 )45 cfg.nextRepo = expandHome(46 process.env.SANDBOX_BENCH_NEXT_REPO ?? cfg.nextRepo ?? ''47 )48 cfg.reactRepoUrl = cfg.reactRepoUrl ?? 'https://github.com/react/react.git'49 cfg.nextRepoUrl = cfg.nextRepoUrl ?? 'https://github.com/vercel/next.js.git'50 cfg.vercelBin = cfg.vercelBin ?? 'vercel'51 if (requireScope && (!cfg.team || !cfg.project)) {52 throw new Error(53 'sandbox-bench is not configured: team and project are required.\n' +54 'Ask the user which Vercel team + project the sandbox VMs should ' +55 'run under, then save them with:\n' +56 ` node ${path.relative(process.cwd(), new URL(import.meta.url).pathname)} ` +57 'set team=<team-slug> project=<project-name>\n' +58 '(Stored in ~/.config/sandbox-bench/config.json, not in the repo.)'59 )60 }61 return cfg62}63 64export function saveConfig(patch) {65 fs.mkdirSync(CONFIG_DIR, { recursive: true })66 const existing = fs.existsSync(CONFIG_FILE)67 ? JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))68 : {}69 const merged = { ...existing, ...patch }70 fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + '\n')71 return merged72}73 74function expandHome(p) {75 if (!p) return p76 return p.startsWith('~/') ? path.join(os.homedir(), p.slice(2)) : p77}78 79// Scope flags appended to every `vercel sandbox` call. Never widen scope80// beyond the configured team/project: the project may be shared.81export function sandboxScope(cfg) {82 return ['--team', cfg.team, '--project', cfg.project]83}84 85// Repos: use the configured checkout, else clone lazily into the cache.86function ensureRepo(cfg, checkout, url, name) {87 if (checkout && fs.existsSync(path.join(checkout, '.git'))) return checkout88 const dest = path.join(cfg.cacheDir, name)89 if (fs.existsSync(path.join(dest, '.git'))) return dest90 fs.mkdirSync(cfg.cacheDir, { recursive: true })91 console.error(`cloning ${url} into ${dest} (one-time)...`)92 execFileSync('git', ['clone', '--filter=blob:none', url, dest], {93 stdio: 'inherit',94 })95 return dest96}97export function ensureReactRepo(cfg) {98 return ensureRepo(cfg, cfg.reactRepo, cfg.reactRepoUrl, 'react')99}100export function ensureNextRepo(cfg) {101 return ensureRepo(cfg, cfg.nextRepo, cfg.nextRepoUrl, 'next')102}103 104// ------------------------------------------------------------------ CLI105// realpath both sides: /tmp vs /private/tmp (macOS) or any symlinked106// invocation path must not silently skip the CLI (a no-op "config set"107// would leave stale team/project in effect with exit 0).108function isMain() {109 if (!process.argv[1]) return false110 try {111 return (112 fs.realpathSync(process.argv[1]) ===113 fs.realpathSync(new URL(import.meta.url).pathname)114 )115 } catch {116 return false117 }118}119if (isMain()) {120 const [cmd, ...rest] = process.argv.slice(2)121 if (cmd === 'show') {122 let cfg123 try {124 cfg = loadConfig({ requireScope: false })125 } catch (e) {126 console.error(e.message)127 process.exit(1)128 }129 console.log(JSON.stringify(cfg, null, 2))130 if (!cfg.team || !cfg.project) {131 console.error('\nNOT CONFIGURED: team/project missing (see file header).')132 process.exit(2)133 }134 } else if (cmd === 'set') {135 const patch = {}136 for (const kv of rest) {137 const i = kv.indexOf('=')138 if (i < 0) {139 console.error(`bad argument "${kv}", want key=value`)140 process.exit(1)141 }142 patch[kv.slice(0, i)] = kv.slice(i + 1)143 }144 const merged = saveConfig(patch)145 console.log(`saved: ${JSON.stringify(merged, null, 2)}`)146 } else {147 console.error('usage: node config.mjs show | set key=value [key=value...]')148 process.exit(1)149 }150}151 Referenced from SKILL.md
SKILL.mdView in source ↗
Source excerpt starting at line 38.SKILL.mdView in source ↗381. `node scripts/config.mjs show` — if it reports NOT CONFIGURED, ask39 the user which Vercel **team** and **project** the sandbox VMs
Source excerpt starting at line 40.40 should run under (these are billed resources; never guess, never41 default), then `node scripts/config.mjs set team=<slug> project=<name>`.42 Config lives in `~/.config/sandbox-bench/config.json` — never commit