@aamini/config 0.2.2 → 0.2.3

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.
@@ -228,8 +228,29 @@ function updateEnvFile(path, groups, replace = false) {
228
228
  function setDaemonPort(appPort) {
229
229
  const base = "pitchfork.toml";
230
230
  if (!existsSync(base)) return;
231
- const contents = readFileSync(base, "utf8").replace(/^port = \d+\n/m, "");
232
- writeFileSync("pitchfork.local.toml", `${contents.trimEnd()}\nport = ${appPort}\n`);
231
+ writeFileSync("pitchfork.local.toml", withDevPort(readFileSync(base, "utf8"), appPort));
232
+ }
233
+ const tableHeader = /^\s*\[([^\]]+)\]\s*(?:#.*)?$/;
234
+ function withDevPort(contents, appPort) {
235
+ const lines = [];
236
+ let table;
237
+ let placed = false;
238
+ for (const line of contents.trimEnd().split("\n")) {
239
+ const header = tableHeader.exec(line);
240
+ if (header) {
241
+ table = header[1]?.trim();
242
+ lines.push(line);
243
+ if (table === "daemons.dev") {
244
+ lines.push(`port = ${appPort}`);
245
+ placed = true;
246
+ }
247
+ continue;
248
+ }
249
+ if (table === "daemons.dev" && /^\s*port\s*=/.test(line)) continue;
250
+ lines.push(line);
251
+ }
252
+ if (!placed) lines.push("", "[daemons.dev]", `port = ${appPort}`);
253
+ return `${lines.join("\n")}\n`;
233
254
  }
234
255
  function readEnvFile(path) {
235
256
  if (!existsSync(path)) return {};
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/bootstrap/args.ts","../../src/bootstrap/gc.ts","../../src/bootstrap/readiness.ts","../../src/bootstrap/workspace.ts","../../src/bootstrap/bootstrap.ts","../../src/bootstrap/cli.ts"],"sourcesContent":["export interface BootstrapOptions {\n\tverbose: boolean\n}\n\nexport type ParsedArgs =\n\t| { kind: 'run'; options: BootstrapOptions }\n\t| { kind: 'help' }\n\t| { kind: 'error'; message: string }\n\nexport const USAGE = 'Usage: aamini bootstrap [--verbose]'\n\n/**\n * Parses the arguments after `aamini bootstrap`. `usageVerbose` is the\n * `usage_verbose` variable that `mise run` sets from the task's `usage` flag.\n */\nexport function parseBootstrapArgs(\n\targs: string[],\n\tusageVerbose?: string,\n): ParsedArgs {\n\tif (\n\t\targs.length > 1 ||\n\t\t(args[0] && !['--verbose', '--help'].includes(args[0]))\n\t) {\n\t\treturn { kind: 'error', message: USAGE }\n\t}\n\n\tif (args[0] === '--help') return { kind: 'help' }\n\n\treturn {\n\t\tkind: 'run',\n\t\toptions: { verbose: args[0] === '--verbose' || usageVerbose === 'true' },\n\t}\n}\n","import { statSync } from 'node:fs'\n\nimport { $ } from 'execa'\n\nfunction lines(value: string): string[] {\n\treturn value\n\t\t.split('\\n')\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean)\n}\n\nexport async function collectGarbage(): Promise<number> {\n\t// Compose directory labels describe the daemon host, not a remote client.\n\tconst host = process.env.DOCKER_HOST\n\n\tif (host && !host.startsWith('unix://')) {\n\t\tthrow new Error(\n\t\t\t`gc: refusing to run against a remote Docker daemon: ${host}`,\n\t\t)\n\t}\n\n\tconst { stdout } =\n\t\tawait $`docker ps -a --format ${'{{.Label \"com.docker.compose.project\"}}'}`\n\n\tconst projects = [...new Set(lines(stdout))].sort()\n\tlet removed = 0\n\tlet failed = 0\n\n\tfor (const project of projects) {\n\t\tconst result =\n\t\t\tawait $`docker ps -a --filter ${`label=com.docker.compose.project=${project}`} --format ${'{{.Label \"com.docker.compose.project.working_dir\"}}'}`\n\n\t\tconst directory = lines(result.stdout)[0]\n\n\t\tif (\n\t\t\t!directory ||\n\t\t\tstatSync(directory, { throwIfNoEntry: false })?.isDirectory()\n\t\t) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconsole.log(`Removing orphaned stack: ${project} (${directory})`)\n\n\t\t// Compose interpolates the current config even for down on a lost workspace.\n\t\tconst down = await $({\n\t\t\tenv: { DOCKER_SUBNET: '10.255.255.0/24' },\n\t\t\tstdio: 'inherit',\n\t\t\treject: false,\n\t\t})`docker compose -p ${project} down --remove-orphans`\n\n\t\tif (down.exitCode === 0) {\n\t\t\tremoved++\n\t\t} else {\n\t\t\tconsole.error(`Failed to remove: ${project}`)\n\t\t\tfailed++\n\t\t}\n\t}\n\n\t// Docker 29 exposes network labels as a map and omits working_dir.\n\tconst networks =\n\t\tawait $`docker network ls --filter label=com.docker.compose.project --format ${'{{.Name}}'}`\n\n\tfor (const network of lines(networks.stdout)) {\n\t\tconst label =\n\t\t\tawait $`docker network inspect ${network} --format ${'{{index .Labels \"com.docker.compose.project\"}}'}`\n\n\t\tconst project = label.stdout.trim()\n\n\t\tif (!project) continue\n\n\t\tconst containers =\n\t\t\tawait $`docker ps -a --filter ${`label=com.docker.compose.project=${project}`} --format ${'{{.ID}}'}`\n\n\t\tif (containers.stdout.trim()) continue\n\n\t\tconsole.log(`Removing orphaned network: ${network} (${project})`)\n\n\t\tconst result = await $({\n\t\t\tstdio: 'inherit',\n\t\t\treject: false,\n\t\t})`docker network rm ${network}`\n\n\t\tif (result.exitCode !== 0) {\n\t\t\tconsole.error(`Failed to remove: ${network}`)\n\t\t\tfailed++\n\t\t}\n\t}\n\n\tif (failed > 0) throw new Error(`gc: ${failed} removal(s) failed`)\n\n\treturn removed\n}\n","import { setTimeout } from 'node:timers/promises'\n\nimport { $ } from 'execa'\n\nexport async function verifyApp(\n\turl: string,\n\trestart: (() => Promise<void>) | undefined,\n\t{ attempts = 30, interval = 1_000 } = {},\n): Promise<void> {\n\tasync function poll(): Promise<boolean> {\n\t\tfor (let attempt = 0; attempt < attempts; attempt++) {\n\t\t\tconst result = await $({\n\t\t\t\treject: false,\n\t\t\t})`curl -skf --max-time 5 -o /dev/null ${url}`\n\n\t\t\tif (result.exitCode === 0) return true\n\n\t\t\tif (attempt + 1 < attempts) await setTimeout(interval)\n\t\t}\n\n\t\treturn false\n\t}\n\n\tif (await poll()) return\n\n\t// Pitchfork can report a stale daemon as started without a fresh ready probe.\n\tif (restart) {\n\t\tawait restart()\n\n\t\tif (await poll()) return\n\t}\n\n\tthrow new Error(`App did not answer at ${url}`)\n}\n","import { randomBytes } from 'node:crypto'\nimport { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs'\nimport { basename } from 'node:path'\n\nimport { execaSync } from 'execa'\n\nconst MASK_64 = (1n << 64n) - 1n\n\nfunction rotateLeft(value: bigint, bits: bigint): bigint {\n\treturn ((value << bits) | (value >> (64n - bits))) & MASK_64\n}\n\ntype SipState = [bigint, bigint, bigint, bigint]\n\nfunction sipRound(state: SipState): void {\n\tstate[0] = (state[0] + state[1]) & MASK_64\n\tstate[1] = rotateLeft(state[1], 13n) ^ state[0]\n\tstate[0] = rotateLeft(state[0], 32n)\n\tstate[2] = (state[2] + state[3]) & MASK_64\n\tstate[3] = rotateLeft(state[3], 16n) ^ state[2]\n\tstate[0] = (state[0] + state[3]) & MASK_64\n\tstate[3] = rotateLeft(state[3], 21n) ^ state[0]\n\tstate[2] = (state[2] + state[1]) & MASK_64\n\tstate[1] = rotateLeft(state[1], 17n) ^ state[2]\n\tstate[2] = rotateLeft(state[2], 32n)\n}\n\n// Worktrunk uses Rust's DefaultHasher (SipHash 1-3 with zero keys).\nexport function worktrunkHash(value: string): bigint {\n\tconst bytes = Buffer.concat([Buffer.from(value), Buffer.from([0xff])])\n\n\tconst state: SipState = [\n\t\t0x736f6d6570736575n,\n\t\t0x646f72616e646f6dn,\n\t\t0x6c7967656e657261n,\n\t\t0x7465646279746573n,\n\t]\n\n\tlet offset = 0\n\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst message = bytes.readBigUInt64LE(offset)\n\t\tstate[3] ^= message\n\t\tsipRound(state)\n\t\tstate[0] ^= message\n\t\toffset += 8\n\t}\n\n\tlet tail = (BigInt(bytes.length) << 56n) & MASK_64\n\n\tfor (let index = offset; index < bytes.length; index++) {\n\t\ttail |= BigInt(bytes.readUInt8(index)) << BigInt((index - offset) * 8)\n\t}\n\n\tstate[3] ^= tail\n\tsipRound(state)\n\tstate[0] ^= tail\n\tstate[2] ^= 0xffn\n\n\tfor (let round = 0; round < 3; round++) sipRound(state)\n\n\treturn state[0] ^ state[1] ^ state[2] ^ state[3]\n}\n\nexport function hashPort(value: string): number {\n\treturn 10_000 + Number(worktrunkHash(value) % 10_000n)\n}\n\nfunction shortHash(value: string): string {\n\tconst characters = '0123456789abcdefghijklmnopqrstuvwxyz'\n\tconst hash = worktrunkHash(value)\n\n\treturn [hash % 36n, (hash / 36n) % 36n, (hash / 1296n) % 36n]\n\t\t.map((index) => characters[Number(index)])\n\t\t.join('')\n}\n\nexport function sanitizeDatabaseName(value: string): string {\n\tlet result = value\n\t\t.replace(/[A-Z]/g, (character) => character.toLowerCase())\n\t\t.replace(/[^a-z0-9]+/g, '_')\n\t\t.replace(/^_+/, '')\n\n\tif (!result) result = 'workspace'\n\n\tif (!/^[a-z]/.test(result)) result = `w_${result}`\n\tresult = result.slice(0, 44)\n\n\tif (!result.endsWith('_')) result += '_'\n\n\treturn `${result}${shortHash(value)}`\n}\n\nfunction run(command: string, args: string[]): string {\n\treturn execaSync(command, args, {\n\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t}).stdout.trim()\n}\n\ninterface WorkspaceInfo {\n\tbranch: string\n\tworktree: string\n}\n\nfunction detectWorkspace(): WorkspaceInfo {\n\ttry {\n\t\tconst root = realpathSync(run('jj', ['workspace', 'root']))\n\n\t\tconst names = run('jj', [\n\t\t\t'workspace',\n\t\t\t'list',\n\t\t\t'-T',\n\t\t\t'self.name() ++ \"\\\\n\"',\n\t\t]).split('\\n')\n\n\t\tconst branch = names.find((name) => {\n\t\t\ttry {\n\t\t\t\treturn (\n\t\t\t\t\trealpathSync(run('jj', ['workspace', 'root', '--name', name])) ===\n\t\t\t\t\troot\n\t\t\t\t)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\n\t\tif (!branch) throw new Error('Could not identify the current jj workspace')\n\n\t\treturn { branch, worktree: basename(root) }\n\t} catch (error) {\n\t\tif (error instanceof Error && error.message.startsWith('Could not')) {\n\t\t\tthrow error\n\t\t}\n\t}\n\n\ttry {\n\t\tconst root = realpathSync(run('git', ['rev-parse', '--show-toplevel']))\n\t\tconst branch = run('git', ['branch', '--show-current']) || basename(root)\n\n\t\treturn { branch, worktree: basename(root) }\n\t} catch {\n\t\tthrow new Error('Run setup from inside a jj workspace or Git worktree')\n\t}\n}\n\nconst POSTGRES_USER = 'app_user'\n\nconst POSTGRES_PASSWORD = 'app_dev'\n\nconst AWS_ACCESS_KEY_ID = 'app_minio'\n\nconst AWS_SECRET_ACCESS_KEY = 'app_minio_secret'\n\nconst ENV_SPEC_HEADER = ['# ---', '# @defaultSensitive=false', '# ---', '']\n\nexport function updateEnvFile(\n\tpath: string,\n\tgroups: Record<string, string>[],\n\treplace = false,\n): void {\n\tconst updates: Record<string, string> = Object.assign({}, ...groups)\n\n\tconst lines =\n\t\t!replace && existsSync(path)\n\t\t\t? readFileSync(path, 'utf8').split(/\\r?\\n/)\n\t\t\t: []\n\n\tconst specIndex = lines.findIndex((line) =>\n\t\tline.includes('@defaultSensitive'),\n\t)\n\n\tif (specIndex === -1) {\n\t\tlines.unshift(...ENV_SPEC_HEADER)\n\t} else {\n\t\tif (lines[specIndex + 1]?.trim() !== '# ---') {\n\t\t\tlines.splice(specIndex + 1, 0, '# ---', '')\n\t\t}\n\n\t\tif (specIndex === 0 || lines[specIndex - 1]?.trim() !== '# ---') {\n\t\t\tlines.splice(specIndex, 0, '# ---')\n\t\t}\n\t}\n\n\tconst remaining = new Set(Object.keys(updates))\n\n\tfor (let index = 0; index < lines.length; index++) {\n\t\tconst key = lines[index]?.match(/^\\s*([^#=\\s]+)\\s*=/)?.[1]\n\n\t\tif (!key || !(key in updates)) continue\n\n\t\tlines[index] = `${key}=\"${updates[key]!}\"`\n\t\tremaining.delete(key)\n\t}\n\n\twhile (lines.at(-1) === '') lines.pop()\n\n\tfor (const group of groups) {\n\t\tconst pending = Object.keys(group).filter((key) => remaining.has(key))\n\n\t\tif (pending.length === 0) continue\n\n\t\tif (lines.length > 0) lines.push('')\n\n\t\tfor (const key of pending) lines.push(`${key}=\"${updates[key]!}\"`)\n\t}\n\n\twriteFileSync(path, `${lines.join('\\n')}\\n`)\n}\n\n// Vite/Nitro opens several sockets, so Pitchfork needs an explicit app port.\n// Workspace ports stay local to avoid conflicts across checkouts.\nfunction setDaemonPort(appPort: number): void {\n\tconst base = 'pitchfork.toml'\n\n\tif (!existsSync(base)) return\n\n\tconst contents = readFileSync(base, 'utf8').replace(/^port = \\d+\\n/m, '')\n\n\twriteFileSync(\n\t\t'pitchfork.local.toml',\n\t\t`${contents.trimEnd()}\\nport = ${appPort}\\n`,\n\t)\n}\n\nexport function readEnvFile(path: string): Record<string, string> {\n\tif (!existsSync(path)) return {}\n\n\treturn Object.fromEntries(\n\t\treadFileSync(path, 'utf8')\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.flatMap((line) => {\n\t\t\t\tconst match = line.match(\n\t\t\t\t\t/^\\s*([^#=\\s]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^#]*?))\\s*(?:#.*)?$/,\n\t\t\t\t)\n\n\t\t\t\treturn match\n\t\t\t\t\t? [[match[1]!, match[2] ?? match[3] ?? match[4] ?? '']]\n\t\t\t\t\t: []\n\t\t\t}),\n\t)\n}\n\nfunction defaultWorkspaceRoot(): string {\n\ttry {\n\t\treturn realpathSync(run('jj', ['workspace', 'root', '--name', 'default']))\n\t} catch {\n\t\t// not a jj repo — the current directory is the root\n\t\treturn realpathSync('.')\n\t}\n}\n\nfunction pitchfork(args: string[]): string {\n\treturn execaSync('pitchfork', args, {\n\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\ttimeout: 10_000,\n\t}).stdout.trim()\n}\n\nfunction pitchforkAvailable(): boolean {\n\ttry {\n\t\tpitchfork(['list'])\n\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// The proxy TLD (settings proxy.tld) decides where slugs live. A custom TLD\n// with a public suffix (e.g. lvh.example.com) makes slug URLs registrable as\n// OAuth redirect URIs; the default 'localhost' TLD is not registrable.\nfunction proxyTld(): string {\n\ttry {\n\t\treturn pitchfork(['settings', 'get', 'proxy.tld']) || 'localhost'\n\t} catch {\n\t\treturn 'localhost'\n\t}\n}\n\nexport function slugify(value: string): string {\n\tconst slug = value\n\t\t.toLowerCase()\n\t\t.replaceAll(/[^a-z0-9-]+/g, '-')\n\t\t.replaceAll(/-+/g, '-')\n\t\t.replace(/^-+|-+$/g, '')\n\n\tif (!slug) return 'workspace'\n\n\tif (slug.length <= 63) return slug\n\n\tconst suffix = worktrunkHash(value).toString(36).padStart(8, '0').slice(0, 8)\n\n\treturn `${slug.slice(0, 63 - suffix.length - 1).replace(/-+$/, '')}-${suffix}`\n}\n\n// Registers a stable https://<slug>.<tld> URL for the project. Only the\n// default workspace registers; other jj workspaces are reached via\n// https://<workspace>.<slug>.<tld> through `proxy.worktree` auto-discovery.\n// Best effort: pitchfork is a local convenience, never a bootstrap blocker.\n// `proxy trust` needs sudo, so it stays a one-time manual step.\nfunction registerProxySlug(mainRoot: string): string {\n\tconst slug = slugify(basename(mainRoot))\n\n\tif (realpathSync('.') === mainRoot) {\n\t\tregisterSlug(slug, mainRoot)\n\t}\n\n\treturn slug\n}\n\n// Worktree URLs must be single-level: the TLS edge serves a wildcard cert\n// covering only *.<tld>, so pitchfork's nested <workspace>.<slug>.<tld>\n// hostnames fail the TLS handshake behind it. Flattening to <slug>-<workspace>\n// keeps one label under the wildcard. Registered against this workspace's\n// directory so the proxy routes the slug to its own daemon.\nfunction registerWorktreeSlug(slug: string): void {\n\tregisterSlug(slug, realpathSync('.'))\n}\n\nfunction registerSlug(slug: string, dir: string): void {\n\ttry {\n\t\tpitchfork(['settings', 'set', 'proxy.enable', 'true', '--global'])\n\t\tpitchfork(['proxy', 'add', slug, '--daemon', 'dev', '--dir', dir])\n\t} catch {\n\t\t// pitchfork unavailable — skip registration\n\t}\n}\n\n// Ports are stable once assigned: only regenerate when the workspace file\n// is absent or belongs to another worktree, whose cloned copy must not keep\n// this workspace's ports. Re-running setup must not move this workspace's\n// existing database or registered OAuth redirect URIs out from under it.\n// The whole file is owned by setup and fully rewritten when foreign; human\n// values live in .env.development.local, where setup only adds a missing\n// BETTER_AUTH_SECRET.\nfunction existingPorts(worktree: string): Record<string, string> {\n\tconst entries = readEnvFile('.env.workspace.local')\n\n\tif (entries['WORKTREE_NAME'] !== worktree) return {}\n\n\treturn entries\n}\n\nexport function setupWorkspace() {\n\t// Never honor WORKTREE_NAME/WORKTREE_BRANCH from the environment:\n\t// nothing legitimate sets them (wt passes template vars, not env), and\n\t// inherited stale values from another workspace must not steer setup.\n\tconst { branch, worktree } = detectWorkspace()\n\tconst compose = sanitizeDatabaseName(worktree)\n\tconst existing = existingPorts(worktree)\n\n\tconst isForeign =\n\t\texistsSync('.env.workspace.local') &&\n\t\treadEnvFile('.env.workspace.local')['WORKTREE_NAME'] !== worktree\n\n\tconst database = existing['POSTGRES_DB'] ?? sanitizeDatabaseName(branch)\n\tconst mainRoot = defaultWorkspaceRoot()\n\t// Repo name in every hash input: branch names (notably the root\n\t// workspace) repeat across repos, which collided their derived ports.\n\tconst repo = basename(mainRoot)\n\tconst appPort = Number(existing['APP_PORT']) || hashPort(`${repo}:${branch}`)\n\n\tconst postgresPort =\n\t\tNumber(existing['POSTGRES_PORT']) || hashPort(`db-${repo}:${branch}`)\n\n\tconst minioPort =\n\t\tNumber(existing['MINIO_PORT']) || hashPort(`minio-${repo}:${branch}`)\n\n\tconst minioConsolePort =\n\t\tNumber(existing['MINIO_CONSOLE_PORT']) ||\n\t\thashPort(`minio-console-${repo}:${branch}`)\n\n\tconst tld = proxyTld()\n\tconst proxySlug = registerProxySlug(mainRoot)\n\tconst worktreeLabel = slugify(worktree)\n\tconst isRoot = realpathSync('.') === mainRoot\n\n\tconst slug =\n\t\tisRoot || worktreeLabel === proxySlug\n\t\t\t? proxySlug\n\t\t\t: slugify(`${proxySlug}-${worktreeLabel}`)\n\n\tif (!isRoot && slug !== proxySlug) {\n\t\tregisterWorktreeSlug(slug)\n\t}\n\n\tconst proxyHost = `${slug}.${tld}`\n\n\tconst proxyUp = pitchforkAvailable()\n\n\tupdateEnvFile(\n\t\t'.env.workspace.local',\n\t\t[\n\t\t\t{\n\t\t\t\tAPP_PORT: String(appPort),\n\t\t\t\tBASE_URL: proxyUp\n\t\t\t\t\t? `https://${proxyHost}`\n\t\t\t\t\t: `http://localhost:${appPort}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tPOSTGRES_PORT: String(postgresPort),\n\t\t\t\tPOSTGRES_DB: database,\n\t\t\t\tPOSTGRES_USER,\n\t\t\t\tPOSTGRES_PASSWORD,\n\t\t\t\tDATABASE_URL:\n\t\t\t\t\t'postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}',\n\t\t\t},\n\t\t\t{\n\t\t\t\tMINIO_PORT: String(minioPort),\n\t\t\t\tMINIO_CONSOLE_PORT: String(minioConsolePort),\n\t\t\t},\n\t\t\t{\n\t\t\t\tAWS_ENDPOINT_URL: 'http://localhost:${MINIO_PORT}',\n\t\t\t\tAWS_ACCESS_KEY_ID,\n\t\t\t\tAWS_SECRET_ACCESS_KEY,\n\t\t\t\tAWS_S3_BUCKET_NAME: 'app',\n\t\t\t},\n\t\t\t{\n\t\t\t\tWORKTREE_NAME: worktree,\n\t\t\t\tCOMPOSE_PROJECT_NAME: compose,\n\t\t\t},\n\t\t],\n\t\tisForeign,\n\t)\n\n\tsetDaemonPort(appPort)\n\n\tconst summary = [\n\t\t`app: http://localhost:${appPort}`,\n\t\t`postgres: localhost:${postgresPort}/${database}`,\n\t\t`minio: http://localhost:${minioPort}`,\n\t]\n\n\tif (proxyUp) {\n\t\tsummary.push(`proxy: https://${proxyHost}`)\n\t}\n\n\tif (seedDevLocalFile()) {\n\t\tsummary.push('Seeded a local BETTER_AUTH_SECRET.')\n\t}\n\n\treturn {\n\t\tbaseUrl: proxyUp ? `https://${proxyHost}` : `http://localhost:${appPort}`,\n\t\tsummary,\n\t}\n}\n\n// Adds a BETTER_AUTH_SECRET to .env.development.local unless a local file\n// already sets one.\nfunction seedDevLocalFile(): boolean {\n\tconst path = '.env.development.local'\n\n\tif (readEnvFile(path)['BETTER_AUTH_SECRET']) {\n\t\treturn false\n\t}\n\n\t// Drops an empty BETTER_AUTH_SECRET line: a quoted \"\" would hide the\n\t// .env.local value, and an unquoted one would duplicate the new value.\n\tconst existing = existsSync(path)\n\t\t? readFileSync(path, 'utf8')\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.filter((line) => !/^\\s*BETTER_AUTH_SECRET\\s*=/.test(line))\n\t\t\t\t.join('\\n')\n\t\t\t\t.trimEnd()\n\t\t: ''\n\n\tif (readEnvFile('.env.local')['BETTER_AUTH_SECRET']) {\n\t\tif (existsSync(path)) {\n\t\t\twriteFileSync(path, existing ? `${existing}\\n` : '')\n\t\t}\n\n\t\treturn false\n\t}\n\n\twriteFileSync(\n\t\tpath,\n\t\t[\n\t\t\texisting ||\n\t\t\t\t'# Local overrides; setup adds missing defaults and leaves existing values alone.',\n\t\t\t`BETTER_AUTH_SECRET=\"${randomBytes(32).toString('base64url')}\"`,\n\t\t\t'',\n\t\t].join('\\n'),\n\t)\n\n\treturn true\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\n\nimport { $ } from 'execa'\n\nimport type { BootstrapOptions } from './args.ts'\nimport { collectGarbage } from './gc.ts'\nimport { verifyApp } from './readiness.ts'\nimport { setupWorkspace } from './workspace.ts'\n\ntype Command = [string, ...string[]]\n\n/**\n * Configures the checkout in the current directory and starts the app. The\n * caller installs tools and packages and prints the banner first, because this\n * code ships in a package that a fresh clone does not have yet.\n */\nexport async function bootstrap({ verbose }: BootstrapOptions): Promise<void> {\n\t// Headless hosts lack a keyring/TPM; suppress Varlock backend probes.\n\tprocess.env._VARLOCK_FORCE_FILE_ENCRYPTION_FALLBACK = '1'\n\n\tconst gum = spawnSync('gum', ['--version'], { stdio: 'ignore' })\n\n\tif (gum.error || gum.status !== 0) {\n\t\tthrow new Error('gum is required; run mise install before bootstrap')\n\t}\n\n\tconst inherited = $({ stdio: 'inherit' })\n\n\tasync function complete(title: string): Promise<void> {\n\t\tawait inherited`gum style --foreground 82 ${` ✓ ${title}`}`\n\t}\n\n\tasync function step(title: string, command: Command): Promise<void> {\n\t\tif (verbose) {\n\t\t\tconsole.log(` ${title}`)\n\t\t\tawait inherited`${command}`\n\t\t} else {\n\t\t\tawait inherited`gum spin --show-error --title ${` ${title}...`} -- ${command}`\n\t\t}\n\n\t\tawait complete(title)\n\t}\n\n\tconst { baseUrl, summary } = setupWorkspace()\n\tawait complete('Configure workspace')\n\tconst removed = await collectGarbage()\n\tawait complete(`Remove orphaned compose stacks (${removed} removed)`)\n\n\t// Agent mode redacts secrets and rejects prompts in headless sessions.\n\tawait step('Validate env with varlock', [\n\t\t'vp',\n\t\t'exec',\n\t\t'varlock',\n\t\t'load',\n\t\t'--agent',\n\t\t'--format',\n\t\t'pretty',\n\t])\n\tawait step('Start Docker services', ['vp', 'run', 'compose:up'])\n\tawait step('Apply database migrations', ['vp', 'run', 'db:migrate'])\n\n\tconst pitchfork = await $({ reject: false })`pitchfork --version`\n\tconst hasDaemon = existsSync('pitchfork.toml') && pitchfork.exitCode === 0\n\n\tif (hasDaemon) await step('Start dev daemon', ['pitchfork', 'start', 'dev'])\n\n\tawait verifyApp(\n\t\tbaseUrl,\n\t\thasDaemon\n\t\t\t? async () => {\n\t\t\t\t\tawait inherited`gum style --foreground 220 ${' App not answering; force-restarting dev daemon'}`\n\t\t\t\t\tawait inherited`pitchfork restart dev --force`\n\t\t\t\t}\n\t\t\t: undefined,\n\t)\n\tawait complete('Verify app responds')\n\n\tconst heading =\n\t\tawait $`gum style --bold --foreground 82 ${'✓ Bootstrap complete'}`\n\n\tconst details = await Promise.all(\n\t\tsummary.map(async (line) => {\n\t\t\tconst result = await $`gum style --foreground 39 ${line}`\n\n\t\t\treturn result.stdout\n\t\t}),\n\t)\n\n\tawait inherited`gum style --border rounded --border-foreground 82 --padding ${'0 3'} --margin ${'1 0'} ${heading.stdout} ${details}`\n}\n","#!/usr/bin/env node\nimport { parseBootstrapArgs, USAGE } from './args.ts'\nimport { bootstrap } from './bootstrap.ts'\n\nconst [command, ...args] = process.argv.slice(2)\n\nif (command !== 'bootstrap') {\n\tconsole.error(USAGE)\n\tprocess.exitCode = 2\n} else {\n\tconst parsed = parseBootstrapArgs(args, process.env['usage_verbose'])\n\n\tif (parsed.kind === 'help') {\n\t\tconsole.log(USAGE)\n\t} else if (parsed.kind === 'error') {\n\t\tconsole.error(parsed.message)\n\t\tprocess.exitCode = 2\n\t} else {\n\t\ttry {\n\t\t\tawait bootstrap(parsed.options)\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t`✗ Bootstrap failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t)\n\t\t\tprocess.exitCode = 1\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/bootstrap/args.ts","../../src/bootstrap/gc.ts","../../src/bootstrap/readiness.ts","../../src/bootstrap/workspace.ts","../../src/bootstrap/bootstrap.ts","../../src/bootstrap/cli.ts"],"sourcesContent":["export interface BootstrapOptions {\n\tverbose: boolean\n}\n\nexport type ParsedArgs =\n\t| { kind: 'run'; options: BootstrapOptions }\n\t| { kind: 'help' }\n\t| { kind: 'error'; message: string }\n\nexport const USAGE = 'Usage: aamini bootstrap [--verbose]'\n\n/**\n * Parses the arguments after `aamini bootstrap`. `usageVerbose` is the\n * `usage_verbose` variable that `mise run` sets from the task's `usage` flag.\n */\nexport function parseBootstrapArgs(\n\targs: string[],\n\tusageVerbose?: string,\n): ParsedArgs {\n\tif (\n\t\targs.length > 1 ||\n\t\t(args[0] && !['--verbose', '--help'].includes(args[0]))\n\t) {\n\t\treturn { kind: 'error', message: USAGE }\n\t}\n\n\tif (args[0] === '--help') return { kind: 'help' }\n\n\treturn {\n\t\tkind: 'run',\n\t\toptions: { verbose: args[0] === '--verbose' || usageVerbose === 'true' },\n\t}\n}\n","import { statSync } from 'node:fs'\n\nimport { $ } from 'execa'\n\nfunction lines(value: string): string[] {\n\treturn value\n\t\t.split('\\n')\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean)\n}\n\nexport async function collectGarbage(): Promise<number> {\n\t// Compose directory labels describe the daemon host, not a remote client.\n\tconst host = process.env.DOCKER_HOST\n\n\tif (host && !host.startsWith('unix://')) {\n\t\tthrow new Error(\n\t\t\t`gc: refusing to run against a remote Docker daemon: ${host}`,\n\t\t)\n\t}\n\n\tconst { stdout } =\n\t\tawait $`docker ps -a --format ${'{{.Label \"com.docker.compose.project\"}}'}`\n\n\tconst projects = [...new Set(lines(stdout))].sort()\n\tlet removed = 0\n\tlet failed = 0\n\n\tfor (const project of projects) {\n\t\tconst result =\n\t\t\tawait $`docker ps -a --filter ${`label=com.docker.compose.project=${project}`} --format ${'{{.Label \"com.docker.compose.project.working_dir\"}}'}`\n\n\t\tconst directory = lines(result.stdout)[0]\n\n\t\tif (\n\t\t\t!directory ||\n\t\t\tstatSync(directory, { throwIfNoEntry: false })?.isDirectory()\n\t\t) {\n\t\t\tcontinue\n\t\t}\n\n\t\tconsole.log(`Removing orphaned stack: ${project} (${directory})`)\n\n\t\t// Compose interpolates the current config even for down on a lost workspace.\n\t\tconst down = await $({\n\t\t\tenv: { DOCKER_SUBNET: '10.255.255.0/24' },\n\t\t\tstdio: 'inherit',\n\t\t\treject: false,\n\t\t})`docker compose -p ${project} down --remove-orphans`\n\n\t\tif (down.exitCode === 0) {\n\t\t\tremoved++\n\t\t} else {\n\t\t\tconsole.error(`Failed to remove: ${project}`)\n\t\t\tfailed++\n\t\t}\n\t}\n\n\t// Docker 29 exposes network labels as a map and omits working_dir.\n\tconst networks =\n\t\tawait $`docker network ls --filter label=com.docker.compose.project --format ${'{{.Name}}'}`\n\n\tfor (const network of lines(networks.stdout)) {\n\t\tconst label =\n\t\t\tawait $`docker network inspect ${network} --format ${'{{index .Labels \"com.docker.compose.project\"}}'}`\n\n\t\tconst project = label.stdout.trim()\n\n\t\tif (!project) continue\n\n\t\tconst containers =\n\t\t\tawait $`docker ps -a --filter ${`label=com.docker.compose.project=${project}`} --format ${'{{.ID}}'}`\n\n\t\tif (containers.stdout.trim()) continue\n\n\t\tconsole.log(`Removing orphaned network: ${network} (${project})`)\n\n\t\tconst result = await $({\n\t\t\tstdio: 'inherit',\n\t\t\treject: false,\n\t\t})`docker network rm ${network}`\n\n\t\tif (result.exitCode !== 0) {\n\t\t\tconsole.error(`Failed to remove: ${network}`)\n\t\t\tfailed++\n\t\t}\n\t}\n\n\tif (failed > 0) throw new Error(`gc: ${failed} removal(s) failed`)\n\n\treturn removed\n}\n","import { setTimeout } from 'node:timers/promises'\n\nimport { $ } from 'execa'\n\nexport async function verifyApp(\n\turl: string,\n\trestart: (() => Promise<void>) | undefined,\n\t{ attempts = 30, interval = 1_000 } = {},\n): Promise<void> {\n\tasync function poll(): Promise<boolean> {\n\t\tfor (let attempt = 0; attempt < attempts; attempt++) {\n\t\t\tconst result = await $({\n\t\t\t\treject: false,\n\t\t\t})`curl -skf --max-time 5 -o /dev/null ${url}`\n\n\t\t\tif (result.exitCode === 0) return true\n\n\t\t\tif (attempt + 1 < attempts) await setTimeout(interval)\n\t\t}\n\n\t\treturn false\n\t}\n\n\tif (await poll()) return\n\n\t// Pitchfork can report a stale daemon as started without a fresh ready probe.\n\tif (restart) {\n\t\tawait restart()\n\n\t\tif (await poll()) return\n\t}\n\n\tthrow new Error(`App did not answer at ${url}`)\n}\n","import { randomBytes } from 'node:crypto'\nimport { existsSync, readFileSync, realpathSync, writeFileSync } from 'node:fs'\nimport { basename } from 'node:path'\n\nimport { execaSync } from 'execa'\n\nconst MASK_64 = (1n << 64n) - 1n\n\nfunction rotateLeft(value: bigint, bits: bigint): bigint {\n\treturn ((value << bits) | (value >> (64n - bits))) & MASK_64\n}\n\ntype SipState = [bigint, bigint, bigint, bigint]\n\nfunction sipRound(state: SipState): void {\n\tstate[0] = (state[0] + state[1]) & MASK_64\n\tstate[1] = rotateLeft(state[1], 13n) ^ state[0]\n\tstate[0] = rotateLeft(state[0], 32n)\n\tstate[2] = (state[2] + state[3]) & MASK_64\n\tstate[3] = rotateLeft(state[3], 16n) ^ state[2]\n\tstate[0] = (state[0] + state[3]) & MASK_64\n\tstate[3] = rotateLeft(state[3], 21n) ^ state[0]\n\tstate[2] = (state[2] + state[1]) & MASK_64\n\tstate[1] = rotateLeft(state[1], 17n) ^ state[2]\n\tstate[2] = rotateLeft(state[2], 32n)\n}\n\n// Worktrunk uses Rust's DefaultHasher (SipHash 1-3 with zero keys).\nexport function worktrunkHash(value: string): bigint {\n\tconst bytes = Buffer.concat([Buffer.from(value), Buffer.from([0xff])])\n\n\tconst state: SipState = [\n\t\t0x736f6d6570736575n,\n\t\t0x646f72616e646f6dn,\n\t\t0x6c7967656e657261n,\n\t\t0x7465646279746573n,\n\t]\n\n\tlet offset = 0\n\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst message = bytes.readBigUInt64LE(offset)\n\t\tstate[3] ^= message\n\t\tsipRound(state)\n\t\tstate[0] ^= message\n\t\toffset += 8\n\t}\n\n\tlet tail = (BigInt(bytes.length) << 56n) & MASK_64\n\n\tfor (let index = offset; index < bytes.length; index++) {\n\t\ttail |= BigInt(bytes.readUInt8(index)) << BigInt((index - offset) * 8)\n\t}\n\n\tstate[3] ^= tail\n\tsipRound(state)\n\tstate[0] ^= tail\n\tstate[2] ^= 0xffn\n\n\tfor (let round = 0; round < 3; round++) sipRound(state)\n\n\treturn state[0] ^ state[1] ^ state[2] ^ state[3]\n}\n\nexport function hashPort(value: string): number {\n\treturn 10_000 + Number(worktrunkHash(value) % 10_000n)\n}\n\nfunction shortHash(value: string): string {\n\tconst characters = '0123456789abcdefghijklmnopqrstuvwxyz'\n\tconst hash = worktrunkHash(value)\n\n\treturn [hash % 36n, (hash / 36n) % 36n, (hash / 1296n) % 36n]\n\t\t.map((index) => characters[Number(index)])\n\t\t.join('')\n}\n\nexport function sanitizeDatabaseName(value: string): string {\n\tlet result = value\n\t\t.replace(/[A-Z]/g, (character) => character.toLowerCase())\n\t\t.replace(/[^a-z0-9]+/g, '_')\n\t\t.replace(/^_+/, '')\n\n\tif (!result) result = 'workspace'\n\n\tif (!/^[a-z]/.test(result)) result = `w_${result}`\n\tresult = result.slice(0, 44)\n\n\tif (!result.endsWith('_')) result += '_'\n\n\treturn `${result}${shortHash(value)}`\n}\n\nfunction run(command: string, args: string[]): string {\n\treturn execaSync(command, args, {\n\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t}).stdout.trim()\n}\n\ninterface WorkspaceInfo {\n\tbranch: string\n\tworktree: string\n}\n\nfunction detectWorkspace(): WorkspaceInfo {\n\ttry {\n\t\tconst root = realpathSync(run('jj', ['workspace', 'root']))\n\n\t\tconst names = run('jj', [\n\t\t\t'workspace',\n\t\t\t'list',\n\t\t\t'-T',\n\t\t\t'self.name() ++ \"\\\\n\"',\n\t\t]).split('\\n')\n\n\t\tconst branch = names.find((name) => {\n\t\t\ttry {\n\t\t\t\treturn (\n\t\t\t\t\trealpathSync(run('jj', ['workspace', 'root', '--name', name])) ===\n\t\t\t\t\troot\n\t\t\t\t)\n\t\t\t} catch {\n\t\t\t\treturn false\n\t\t\t}\n\t\t})\n\n\t\tif (!branch) throw new Error('Could not identify the current jj workspace')\n\n\t\treturn { branch, worktree: basename(root) }\n\t} catch (error) {\n\t\tif (error instanceof Error && error.message.startsWith('Could not')) {\n\t\t\tthrow error\n\t\t}\n\t}\n\n\ttry {\n\t\tconst root = realpathSync(run('git', ['rev-parse', '--show-toplevel']))\n\t\tconst branch = run('git', ['branch', '--show-current']) || basename(root)\n\n\t\treturn { branch, worktree: basename(root) }\n\t} catch {\n\t\tthrow new Error('Run setup from inside a jj workspace or Git worktree')\n\t}\n}\n\nconst POSTGRES_USER = 'app_user'\n\nconst POSTGRES_PASSWORD = 'app_dev'\n\nconst AWS_ACCESS_KEY_ID = 'app_minio'\n\nconst AWS_SECRET_ACCESS_KEY = 'app_minio_secret'\n\nconst ENV_SPEC_HEADER = ['# ---', '# @defaultSensitive=false', '# ---', '']\n\nexport function updateEnvFile(\n\tpath: string,\n\tgroups: Record<string, string>[],\n\treplace = false,\n): void {\n\tconst updates: Record<string, string> = Object.assign({}, ...groups)\n\n\tconst lines =\n\t\t!replace && existsSync(path)\n\t\t\t? readFileSync(path, 'utf8').split(/\\r?\\n/)\n\t\t\t: []\n\n\tconst specIndex = lines.findIndex((line) =>\n\t\tline.includes('@defaultSensitive'),\n\t)\n\n\tif (specIndex === -1) {\n\t\tlines.unshift(...ENV_SPEC_HEADER)\n\t} else {\n\t\tif (lines[specIndex + 1]?.trim() !== '# ---') {\n\t\t\tlines.splice(specIndex + 1, 0, '# ---', '')\n\t\t}\n\n\t\tif (specIndex === 0 || lines[specIndex - 1]?.trim() !== '# ---') {\n\t\t\tlines.splice(specIndex, 0, '# ---')\n\t\t}\n\t}\n\n\tconst remaining = new Set(Object.keys(updates))\n\n\tfor (let index = 0; index < lines.length; index++) {\n\t\tconst key = lines[index]?.match(/^\\s*([^#=\\s]+)\\s*=/)?.[1]\n\n\t\tif (!key || !(key in updates)) continue\n\n\t\tlines[index] = `${key}=\"${updates[key]!}\"`\n\t\tremaining.delete(key)\n\t}\n\n\twhile (lines.at(-1) === '') lines.pop()\n\n\tfor (const group of groups) {\n\t\tconst pending = Object.keys(group).filter((key) => remaining.has(key))\n\n\t\tif (pending.length === 0) continue\n\n\t\tif (lines.length > 0) lines.push('')\n\n\t\tfor (const key of pending) lines.push(`${key}=\"${updates[key]!}\"`)\n\t}\n\n\twriteFileSync(path, `${lines.join('\\n')}\\n`)\n}\n\n// Vite/Nitro opens several sockets, so Pitchfork needs an explicit app port.\n// Workspace ports stay local to avoid conflicts across checkouts.\nfunction setDaemonPort(appPort: number): void {\n\tconst base = 'pitchfork.toml'\n\n\tif (!existsSync(base)) return\n\n\twriteFileSync(\n\t\t'pitchfork.local.toml',\n\t\twithDevPort(readFileSync(base, 'utf8'), appPort),\n\t)\n}\n\nconst tableHeader = /^\\s*\\[([^\\]]+)\\]\\s*(?:#.*)?$/\n\n// The port belongs to the dev daemon's table, wherever that table sits: other\n// daemons, such as one on a fixed port, keep theirs.\nfunction withDevPort(contents: string, appPort: number): string {\n\tconst lines: string[] = []\n\tlet table: string | undefined\n\tlet placed = false\n\n\tfor (const line of contents.trimEnd().split('\\n')) {\n\t\tconst header = tableHeader.exec(line)\n\n\t\tif (header) {\n\t\t\ttable = header[1]?.trim()\n\t\t\tlines.push(line)\n\n\t\t\tif (table === 'daemons.dev') {\n\t\t\t\tlines.push(`port = ${appPort}`)\n\t\t\t\tplaced = true\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\tif (table === 'daemons.dev' && /^\\s*port\\s*=/.test(line)) continue\n\t\tlines.push(line)\n\t}\n\n\tif (!placed) lines.push('', '[daemons.dev]', `port = ${appPort}`)\n\n\treturn `${lines.join('\\n')}\\n`\n}\n\nexport function readEnvFile(path: string): Record<string, string> {\n\tif (!existsSync(path)) return {}\n\n\treturn Object.fromEntries(\n\t\treadFileSync(path, 'utf8')\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.flatMap((line) => {\n\t\t\t\tconst match = line.match(\n\t\t\t\t\t/^\\s*([^#=\\s]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^#]*?))\\s*(?:#.*)?$/,\n\t\t\t\t)\n\n\t\t\t\treturn match\n\t\t\t\t\t? [[match[1]!, match[2] ?? match[3] ?? match[4] ?? '']]\n\t\t\t\t\t: []\n\t\t\t}),\n\t)\n}\n\nfunction defaultWorkspaceRoot(): string {\n\ttry {\n\t\treturn realpathSync(run('jj', ['workspace', 'root', '--name', 'default']))\n\t} catch {\n\t\t// not a jj repo — the current directory is the root\n\t\treturn realpathSync('.')\n\t}\n}\n\nfunction pitchfork(args: string[]): string {\n\treturn execaSync('pitchfork', args, {\n\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\ttimeout: 10_000,\n\t}).stdout.trim()\n}\n\nfunction pitchforkAvailable(): boolean {\n\ttry {\n\t\tpitchfork(['list'])\n\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n// The proxy TLD (settings proxy.tld) decides where slugs live. A custom TLD\n// with a public suffix (e.g. lvh.example.com) makes slug URLs registrable as\n// OAuth redirect URIs; the default 'localhost' TLD is not registrable.\nfunction proxyTld(): string {\n\ttry {\n\t\treturn pitchfork(['settings', 'get', 'proxy.tld']) || 'localhost'\n\t} catch {\n\t\treturn 'localhost'\n\t}\n}\n\nexport function slugify(value: string): string {\n\tconst slug = value\n\t\t.toLowerCase()\n\t\t.replaceAll(/[^a-z0-9-]+/g, '-')\n\t\t.replaceAll(/-+/g, '-')\n\t\t.replace(/^-+|-+$/g, '')\n\n\tif (!slug) return 'workspace'\n\n\tif (slug.length <= 63) return slug\n\n\tconst suffix = worktrunkHash(value).toString(36).padStart(8, '0').slice(0, 8)\n\n\treturn `${slug.slice(0, 63 - suffix.length - 1).replace(/-+$/, '')}-${suffix}`\n}\n\n// Registers a stable https://<slug>.<tld> URL for the project. Only the\n// default workspace registers; other jj workspaces are reached via\n// https://<workspace>.<slug>.<tld> through `proxy.worktree` auto-discovery.\n// Best effort: pitchfork is a local convenience, never a bootstrap blocker.\n// `proxy trust` needs sudo, so it stays a one-time manual step.\nfunction registerProxySlug(mainRoot: string): string {\n\tconst slug = slugify(basename(mainRoot))\n\n\tif (realpathSync('.') === mainRoot) {\n\t\tregisterSlug(slug, mainRoot)\n\t}\n\n\treturn slug\n}\n\n// Worktree URLs must be single-level: the TLS edge serves a wildcard cert\n// covering only *.<tld>, so pitchfork's nested <workspace>.<slug>.<tld>\n// hostnames fail the TLS handshake behind it. Flattening to <slug>-<workspace>\n// keeps one label under the wildcard. Registered against this workspace's\n// directory so the proxy routes the slug to its own daemon.\nfunction registerWorktreeSlug(slug: string): void {\n\tregisterSlug(slug, realpathSync('.'))\n}\n\nfunction registerSlug(slug: string, dir: string): void {\n\ttry {\n\t\tpitchfork(['settings', 'set', 'proxy.enable', 'true', '--global'])\n\t\tpitchfork(['proxy', 'add', slug, '--daemon', 'dev', '--dir', dir])\n\t} catch {\n\t\t// pitchfork unavailable — skip registration\n\t}\n}\n\n// Ports are stable once assigned: only regenerate when the workspace file\n// is absent or belongs to another worktree, whose cloned copy must not keep\n// this workspace's ports. Re-running setup must not move this workspace's\n// existing database or registered OAuth redirect URIs out from under it.\n// The whole file is owned by setup and fully rewritten when foreign; human\n// values live in .env.development.local, where setup only adds a missing\n// BETTER_AUTH_SECRET.\nfunction existingPorts(worktree: string): Record<string, string> {\n\tconst entries = readEnvFile('.env.workspace.local')\n\n\tif (entries['WORKTREE_NAME'] !== worktree) return {}\n\n\treturn entries\n}\n\nexport function setupWorkspace() {\n\t// Never honor WORKTREE_NAME/WORKTREE_BRANCH from the environment:\n\t// nothing legitimate sets them (wt passes template vars, not env), and\n\t// inherited stale values from another workspace must not steer setup.\n\tconst { branch, worktree } = detectWorkspace()\n\tconst compose = sanitizeDatabaseName(worktree)\n\tconst existing = existingPorts(worktree)\n\n\tconst isForeign =\n\t\texistsSync('.env.workspace.local') &&\n\t\treadEnvFile('.env.workspace.local')['WORKTREE_NAME'] !== worktree\n\n\tconst database = existing['POSTGRES_DB'] ?? sanitizeDatabaseName(branch)\n\tconst mainRoot = defaultWorkspaceRoot()\n\t// Repo name in every hash input: branch names (notably the root\n\t// workspace) repeat across repos, which collided their derived ports.\n\tconst repo = basename(mainRoot)\n\tconst appPort = Number(existing['APP_PORT']) || hashPort(`${repo}:${branch}`)\n\n\tconst postgresPort =\n\t\tNumber(existing['POSTGRES_PORT']) || hashPort(`db-${repo}:${branch}`)\n\n\tconst minioPort =\n\t\tNumber(existing['MINIO_PORT']) || hashPort(`minio-${repo}:${branch}`)\n\n\tconst minioConsolePort =\n\t\tNumber(existing['MINIO_CONSOLE_PORT']) ||\n\t\thashPort(`minio-console-${repo}:${branch}`)\n\n\tconst tld = proxyTld()\n\tconst proxySlug = registerProxySlug(mainRoot)\n\tconst worktreeLabel = slugify(worktree)\n\tconst isRoot = realpathSync('.') === mainRoot\n\n\tconst slug =\n\t\tisRoot || worktreeLabel === proxySlug\n\t\t\t? proxySlug\n\t\t\t: slugify(`${proxySlug}-${worktreeLabel}`)\n\n\tif (!isRoot && slug !== proxySlug) {\n\t\tregisterWorktreeSlug(slug)\n\t}\n\n\tconst proxyHost = `${slug}.${tld}`\n\n\tconst proxyUp = pitchforkAvailable()\n\n\tupdateEnvFile(\n\t\t'.env.workspace.local',\n\t\t[\n\t\t\t{\n\t\t\t\tAPP_PORT: String(appPort),\n\t\t\t\tBASE_URL: proxyUp\n\t\t\t\t\t? `https://${proxyHost}`\n\t\t\t\t\t: `http://localhost:${appPort}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tPOSTGRES_PORT: String(postgresPort),\n\t\t\t\tPOSTGRES_DB: database,\n\t\t\t\tPOSTGRES_USER,\n\t\t\t\tPOSTGRES_PASSWORD,\n\t\t\t\tDATABASE_URL:\n\t\t\t\t\t'postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}',\n\t\t\t},\n\t\t\t{\n\t\t\t\tMINIO_PORT: String(minioPort),\n\t\t\t\tMINIO_CONSOLE_PORT: String(minioConsolePort),\n\t\t\t},\n\t\t\t{\n\t\t\t\tAWS_ENDPOINT_URL: 'http://localhost:${MINIO_PORT}',\n\t\t\t\tAWS_ACCESS_KEY_ID,\n\t\t\t\tAWS_SECRET_ACCESS_KEY,\n\t\t\t\tAWS_S3_BUCKET_NAME: 'app',\n\t\t\t},\n\t\t\t{\n\t\t\t\tWORKTREE_NAME: worktree,\n\t\t\t\tCOMPOSE_PROJECT_NAME: compose,\n\t\t\t},\n\t\t],\n\t\tisForeign,\n\t)\n\n\tsetDaemonPort(appPort)\n\n\tconst summary = [\n\t\t`app: http://localhost:${appPort}`,\n\t\t`postgres: localhost:${postgresPort}/${database}`,\n\t\t`minio: http://localhost:${minioPort}`,\n\t]\n\n\tif (proxyUp) {\n\t\tsummary.push(`proxy: https://${proxyHost}`)\n\t}\n\n\tif (seedDevLocalFile()) {\n\t\tsummary.push('Seeded a local BETTER_AUTH_SECRET.')\n\t}\n\n\treturn {\n\t\tbaseUrl: proxyUp ? `https://${proxyHost}` : `http://localhost:${appPort}`,\n\t\tsummary,\n\t}\n}\n\n// Adds a BETTER_AUTH_SECRET to .env.development.local unless a local file\n// already sets one.\nfunction seedDevLocalFile(): boolean {\n\tconst path = '.env.development.local'\n\n\tif (readEnvFile(path)['BETTER_AUTH_SECRET']) {\n\t\treturn false\n\t}\n\n\t// Drops an empty BETTER_AUTH_SECRET line: a quoted \"\" would hide the\n\t// .env.local value, and an unquoted one would duplicate the new value.\n\tconst existing = existsSync(path)\n\t\t? readFileSync(path, 'utf8')\n\t\t\t\t.split(/\\r?\\n/)\n\t\t\t\t.filter((line) => !/^\\s*BETTER_AUTH_SECRET\\s*=/.test(line))\n\t\t\t\t.join('\\n')\n\t\t\t\t.trimEnd()\n\t\t: ''\n\n\tif (readEnvFile('.env.local')['BETTER_AUTH_SECRET']) {\n\t\tif (existsSync(path)) {\n\t\t\twriteFileSync(path, existing ? `${existing}\\n` : '')\n\t\t}\n\n\t\treturn false\n\t}\n\n\twriteFileSync(\n\t\tpath,\n\t\t[\n\t\t\texisting ||\n\t\t\t\t'# Local overrides; setup adds missing defaults and leaves existing values alone.',\n\t\t\t`BETTER_AUTH_SECRET=\"${randomBytes(32).toString('base64url')}\"`,\n\t\t\t'',\n\t\t].join('\\n'),\n\t)\n\n\treturn true\n}\n","import { spawnSync } from 'node:child_process'\nimport { existsSync } from 'node:fs'\n\nimport { $ } from 'execa'\n\nimport type { BootstrapOptions } from './args.ts'\nimport { collectGarbage } from './gc.ts'\nimport { verifyApp } from './readiness.ts'\nimport { setupWorkspace } from './workspace.ts'\n\ntype Command = [string, ...string[]]\n\n/**\n * Configures the checkout in the current directory and starts the app. The\n * caller installs tools and packages and prints the banner first, because this\n * code ships in a package that a fresh clone does not have yet.\n */\nexport async function bootstrap({ verbose }: BootstrapOptions): Promise<void> {\n\t// Headless hosts lack a keyring/TPM; suppress Varlock backend probes.\n\tprocess.env._VARLOCK_FORCE_FILE_ENCRYPTION_FALLBACK = '1'\n\n\tconst gum = spawnSync('gum', ['--version'], { stdio: 'ignore' })\n\n\tif (gum.error || gum.status !== 0) {\n\t\tthrow new Error('gum is required; run mise install before bootstrap')\n\t}\n\n\tconst inherited = $({ stdio: 'inherit' })\n\n\tasync function complete(title: string): Promise<void> {\n\t\tawait inherited`gum style --foreground 82 ${` ✓ ${title}`}`\n\t}\n\n\tasync function step(title: string, command: Command): Promise<void> {\n\t\tif (verbose) {\n\t\t\tconsole.log(` ${title}`)\n\t\t\tawait inherited`${command}`\n\t\t} else {\n\t\t\tawait inherited`gum spin --show-error --title ${` ${title}...`} -- ${command}`\n\t\t}\n\n\t\tawait complete(title)\n\t}\n\n\tconst { baseUrl, summary } = setupWorkspace()\n\tawait complete('Configure workspace')\n\tconst removed = await collectGarbage()\n\tawait complete(`Remove orphaned compose stacks (${removed} removed)`)\n\n\t// Agent mode redacts secrets and rejects prompts in headless sessions.\n\tawait step('Validate env with varlock', [\n\t\t'vp',\n\t\t'exec',\n\t\t'varlock',\n\t\t'load',\n\t\t'--agent',\n\t\t'--format',\n\t\t'pretty',\n\t])\n\tawait step('Start Docker services', ['vp', 'run', 'compose:up'])\n\tawait step('Apply database migrations', ['vp', 'run', 'db:migrate'])\n\n\tconst pitchfork = await $({ reject: false })`pitchfork --version`\n\tconst hasDaemon = existsSync('pitchfork.toml') && pitchfork.exitCode === 0\n\n\tif (hasDaemon) await step('Start dev daemon', ['pitchfork', 'start', 'dev'])\n\n\tawait verifyApp(\n\t\tbaseUrl,\n\t\thasDaemon\n\t\t\t? async () => {\n\t\t\t\t\tawait inherited`gum style --foreground 220 ${' App not answering; force-restarting dev daemon'}`\n\t\t\t\t\tawait inherited`pitchfork restart dev --force`\n\t\t\t\t}\n\t\t\t: undefined,\n\t)\n\tawait complete('Verify app responds')\n\n\tconst heading =\n\t\tawait $`gum style --bold --foreground 82 ${'✓ Bootstrap complete'}`\n\n\tconst details = await Promise.all(\n\t\tsummary.map(async (line) => {\n\t\t\tconst result = await $`gum style --foreground 39 ${line}`\n\n\t\t\treturn result.stdout\n\t\t}),\n\t)\n\n\tawait inherited`gum style --border rounded --border-foreground 82 --padding ${'0 3'} --margin ${'1 0'} ${heading.stdout} ${details}`\n}\n","#!/usr/bin/env node\nimport { parseBootstrapArgs, USAGE } from './args.ts'\nimport { bootstrap } from './bootstrap.ts'\n\nconst [command, ...args] = process.argv.slice(2)\n\nif (command !== 'bootstrap') {\n\tconsole.error(USAGE)\n\tprocess.exitCode = 2\n} else {\n\tconst parsed = parseBootstrapArgs(args, process.env['usage_verbose'])\n\n\tif (parsed.kind === 'help') {\n\t\tconsole.log(USAGE)\n\t} else if (parsed.kind === 'error') {\n\t\tconsole.error(parsed.message)\n\t\tprocess.exitCode = 2\n\t} else {\n\t\ttry {\n\t\t\tawait bootstrap(parsed.options)\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t`✗ Bootstrap failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t)\n\t\t\tprocess.exitCode = 1\n\t\t}\n\t}\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aamini/config",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Shared Vite+, lint, format, type, and test config for aria-amini apps",
5
5
  "license": "MIT",
6
6
  "type": "module",