@getmonoceros/workbench 1.6.3 → 1.6.5

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/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/help.ts","../src/inner-args.ts","../src/main.ts","../src/commands/add-apt-packages.ts","../src/modify/index.ts","../src/config/io.ts","../src/config/schema.ts","../src/config/paths.ts","../src/create/catalog.ts","../src/create/scaffold.ts","../src/util/ref.ts","../src/modify/yml.ts","../src/commands/add-feature.ts","../src/commands/add-from-url.ts","../src/commands/add-repo.ts","../src/commands/add-language.ts","../src/commands/add-service.ts","../src/commands/apply.ts","../src/apply/index.ts","../src/config/global.ts","../src/config/state.ts","../src/config/transform.ts","../src/util/format.ts","../src/devcontainer/compose.ts","../src/util/mask-secrets.ts","../src/devcontainer/cli.ts","../src/devcontainer/credentials.ts","../src/devcontainer/repo-reachability.ts","../src/devcontainer/docker-mode.ts","../src/devcontainer/identity.ts","../src/version.ts","../src/commands/_dispatch.ts","../src/commands/completion.ts","../src/commands/init.ts","../src/init/index.ts","../src/init/components.ts","../src/init/generator.ts","../src/init/manifest.ts","../src/commands/list-components.ts","../src/commands/logs.ts","../src/commands/remove-apt-packages.ts","../src/commands/remove-feature.ts","../src/commands/remove.ts","../src/remove/index.ts","../src/commands/restore.ts","../src/restore/index.ts","../src/commands/remove-from-url.ts","../src/commands/remove-language.ts","../src/commands/remove-repo.ts","../src/commands/remove-service.ts","../src/commands/run.ts","../src/devcontainer/shell.ts","../src/devcontainer/run.ts","../src/commands/shell.ts","../src/commands/start.ts","../src/commands/status.ts","../src/commands/stop.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runMain } from 'citty';\nimport { maybeRenderHelp } from './help.js';\nimport { consumeInnerArgsFromProcessArgv } from './inner-args.js';\nimport { main } from './main.js';\n\n// Pull everything after `--` out of argv before citty starts parsing.\n// Otherwise citty's eager --help/--version handling shadows the inner\n// command (e.g. `monoceros run -- foo --help` would show monoceros run's\n// own help, not foo's).\nconsumeInnerArgsFromProcessArgv();\n\nasync function entry(): Promise<void> {\n // We render `--help` ourselves so the USAGE line shows positional\n // arguments *before* `[OPTIONS]`, matching the\n // `monoceros <command> <containername> [<args> …]` convention. Citty's\n // built-in renderer puts `[OPTIONS]` first which is the opposite. When\n // help was rendered, exit before handing off to citty so its own help\n // doesn't double up.\n if (await maybeRenderHelp(process.argv.slice(2), main)) {\n return;\n }\n await runMain(main);\n}\n\nentry().catch((err: unknown) => {\n // runMain handles its own errors; this catch is a safety net for the\n // help path.\n console.error(\n err instanceof Error ? (err.stack ?? err.message) : String(err),\n );\n process.exit(1);\n});\n","import type { CommandDef } from 'citty';\n\n/**\n * Custom help renderer. Citty's built-in `renderUsage` has two\n * issues for Monoceros:\n *\n * 1. It puts `[OPTIONS]` *before* the positional arguments in the\n * USAGE line. We want positionals first, matching the\n * `monoceros <command> <containername> [<args> …]` shape\n * documented in konzept.md and docs/commands/README.md.\n * 2. It lists all subcommands in a flat pipe-separated USAGE line\n * (`monoceros init|shell|run|…`) and a flat COMMANDS block.\n * With 20+ commands that becomes unreadable.\n *\n * This module checks for `--help` / `-h` in argv before citty gets a\n * chance to print its own help. When triggered, it resolves the\n * matching subcommand and prints our own block: positional-first\n * USAGE, COMMANDS grouped by `meta.group`, and descriptions wrapped\n * to terminal width.\n */\n\ninterface ResolvedArg {\n name: string;\n type: 'positional' | 'string' | 'boolean' | 'number' | 'enum';\n required?: boolean;\n description?: string;\n default?: unknown;\n alias?: string | string[];\n valueHint?: string;\n}\n\nconst ANSI_BOLD = '\\x1b[1m';\nconst ANSI_UNDERLINE = '\\x1b[4m';\nconst ANSI_CYAN = '\\x1b[36m';\nconst ANSI_GREY = '\\x1b[90m';\nconst ANSI_RESET = '\\x1b[0m';\n\nfunction isTty(): boolean {\n return process.stdout.isTTY ?? false;\n}\n\nfunction color(text: string, ...codes: string[]): string {\n if (!isTty()) return text;\n return codes.join('') + text + ANSI_RESET;\n}\n\nconst bold = (s: string) => color(s, ANSI_BOLD);\nconst underline = (s: string) => color(s, ANSI_UNDERLINE);\nconst cyan = (s: string) => color(s, ANSI_CYAN);\nconst grey = (s: string) => color(s, ANSI_GREY);\n\n/**\n * Ordered list of command-group keys with a human-readable label.\n * Anything a command file tags via `meta.group` lands in the\n * matching bucket; anything ungrouped falls through to \"Other\".\n * The render order follows this array.\n */\nconst GROUPS: ReadonlyArray<{ key: string; label: string }> = [\n { key: 'lifecycle', label: 'Container lifecycle' },\n { key: 'run', label: 'Run + inspect' },\n { key: 'edit', label: 'Edit container yml' },\n { key: 'discovery', label: 'Discovery' },\n { key: 'tooling', label: 'Tooling' },\n];\n\nfunction resolveArgs(\n argsDef: Record<string, unknown> | undefined,\n): ResolvedArg[] {\n if (!argsDef) return [];\n const out: ResolvedArg[] = [];\n for (const [name, defRaw] of Object.entries(argsDef)) {\n const def = (defRaw ?? {}) as Partial<ResolvedArg>;\n out.push({\n name,\n type: (def.type as ResolvedArg['type']) ?? 'string',\n required: def.required,\n description: def.description,\n default: def.default,\n alias: def.alias,\n valueHint: def.valueHint,\n });\n }\n return out;\n}\n\nfunction renderValueHint(arg: ResolvedArg): string {\n if (arg.type === 'boolean') return '';\n const hint = arg.valueHint ?? arg.name;\n return `=<${hint}>`;\n}\n\nfunction renderArgDescription(arg: ResolvedArg, isRequired: boolean): string {\n const parts: string[] = [];\n if (arg.description) parts.push(arg.description);\n if (isRequired) parts.push(grey('(Required)'));\n if (arg.default !== undefined && arg.type !== 'boolean') {\n parts.push(grey(`(Default: ${JSON.stringify(arg.default)})`));\n }\n return parts.join(' ');\n}\n\n// Strip ANSI escape sequences so column-padding measurements use\n// the visible width instead of the raw character count.\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;]*m/g;\n\nfunction visibleLen(s: string): number {\n return s.replace(ANSI_RE, '').length;\n}\n\nfunction terminalWidth(): number {\n return process.stdout.columns && process.stdout.columns > 40\n ? process.stdout.columns\n : 100;\n}\n\n/**\n * Wrap `text` (which may contain ANSI codes) to fit `width` columns,\n * with `continuationIndent` prepended to every wrapped line after\n * the first. Word-aware: breaks at spaces, falls back to hard breaks\n * only for individual tokens longer than `width`.\n */\nfunction wrapText(\n text: string,\n width: number,\n continuationIndent: string,\n): string {\n if (visibleLen(text) <= width) return text;\n // `width` is the budget for the actual text on each line — it does\n // not include the continuation indent (caller already accounted for\n // it when computing width). Continuation-line indent gets prefixed\n // at join time, so every line gets the same text-column budget.\n const words = text.split(/(\\s+)/);\n const lines: string[] = [];\n let current = '';\n for (const w of words) {\n if (visibleLen(current) + visibleLen(w) <= width) {\n current += w;\n continue;\n }\n if (current.length > 0) lines.push(current.replace(/\\s+$/, ''));\n current = w.replace(/^\\s+/, '');\n }\n if (current.length > 0) lines.push(current.replace(/\\s+$/, ''));\n return lines.map((l, i) => (i === 0 ? l : continuationIndent + l)).join('\\n');\n}\n\n/**\n * Render a left-aligned label column next to wrapped descriptions.\n * Column gutter is four spaces. Description wraps within the\n * remaining terminal width.\n */\nfunction alignTable(rows: Array<[string, string]>, indent: string): string {\n if (rows.length === 0) return '';\n const labelWidth = Math.max(...rows.map((r) => visibleLen(r[0])));\n const gutter = ' ';\n const descWidth =\n terminalWidth() - indent.length - labelWidth - gutter.length;\n const continuationIndent = ' '.repeat(\n indent.length + labelWidth + gutter.length,\n );\n return rows\n .map(([left, right]) => {\n const pad = ' '.repeat(labelWidth - visibleLen(left));\n const wrapped = wrapText(right, descWidth, continuationIndent);\n return `${indent}${left}${pad}${gutter}${wrapped}`;\n })\n .join('\\n');\n}\n\ninterface SubCommandEntry {\n name: string;\n description: string;\n group: string;\n}\n\nfunction collectSubCommands(cmd: CommandDef): SubCommandEntry[] {\n const subs = (cmd.subCommands ?? {}) as Record<string, CommandDef>;\n const out: SubCommandEntry[] = [];\n for (const [name, sub] of Object.entries(subs)) {\n const meta = (sub?.meta ?? {}) as {\n hidden?: boolean;\n description?: string;\n group?: string;\n };\n if (meta.hidden) continue;\n out.push({\n name,\n description: meta.description ?? '',\n group: meta.group ?? 'other',\n });\n }\n return out;\n}\n\nfunction renderCommandsBlock(entries: SubCommandEntry[]): string[] {\n if (entries.length === 0) return [];\n const lines: string[] = [];\n lines.push(underline(bold('COMMANDS')));\n\n // Group entries while preserving GROUPS' declared order. Anything\n // tagged with an unknown group (or no group) falls into \"Other\"\n // and renders last.\n const byGroup = new Map<string, SubCommandEntry[]>();\n for (const entry of entries) {\n const arr = byGroup.get(entry.group) ?? [];\n arr.push(entry);\n byGroup.set(entry.group, arr);\n }\n\n const renderSection = (label: string, items: SubCommandEntry[]) => {\n if (items.length === 0) return;\n lines.push('');\n // Group label is left-aligned, underlined, in grey — distinct\n // from the section headers above (which are bold+underlined+white)\n // through colour + weight. Blank line after gives the items room\n // to breathe; cyan command labels do the visual separation from\n // the heading without needing an indent, so commands go flush\n // left and the descriptions get the full terminal width.\n lines.push(underline(grey(label)));\n lines.push('');\n const rows: Array<[string, string]> = items.map((e) => [\n cyan(e.name),\n e.description,\n ]);\n lines.push(alignTable(rows, ''));\n };\n\n for (const { key, label } of GROUPS) {\n renderSection(label, byGroup.get(key) ?? []);\n byGroup.delete(key);\n }\n // Anything left over (ungrouped or unknown-group) lands in a\n // catch-all section so nothing silently disappears.\n for (const [groupKey, items] of byGroup) {\n const label = groupKey === 'other' ? 'Other' : groupKey;\n renderSection(label, items);\n }\n\n lines.push('');\n return lines;\n}\n\nexport function renderUsageBlock(\n cmd: CommandDef,\n commandPath: string[],\n): string {\n const meta = (cmd.meta ?? {}) as {\n name?: string;\n description?: string;\n version?: string;\n };\n const args = resolveArgs((cmd.args ?? {}) as Record<string, unknown>);\n const subCommandEntries = collectSubCommands(cmd);\n\n const fullName = commandPath.join(' ') || meta.name || 'monoceros';\n\n const positionals = args.filter((a) => a.type === 'positional');\n const flags = args.filter((a) => a.type !== 'positional');\n\n // USAGE line: positionals come first, then [OPTIONS]. When the\n // command has subcommands, render a single `<command>` placeholder\n // instead of a pipe-separated list — anything more than a couple\n // of subcommands makes the pipe list unreadable, and the COMMANDS\n // block below carries the actual menu.\n const usageTokens: string[] = [];\n for (const p of positionals) {\n const isRequired = p.required !== false && p.default === undefined;\n const t = p.name.toUpperCase();\n usageTokens.push(isRequired ? `<${t}>` : `[${t}]`);\n }\n if (subCommandEntries.length > 0) usageTokens.push('<command>');\n if (flags.length > 0) usageTokens.push('[OPTIONS]');\n\n const lines: string[] = [];\n const version = meta.version;\n const header = `${meta.description ?? ''} (${fullName}${version ? ` v${version}` : ''})`;\n lines.push(grey(wrapText(header, terminalWidth(), '')));\n lines.push('');\n lines.push(\n `${underline(bold('USAGE'))} ${cyan([fullName, ...usageTokens].join(' '))}`,\n );\n lines.push('');\n\n if (positionals.length > 0) {\n lines.push(underline(bold('ARGUMENTS')));\n lines.push('');\n const rows: Array<[string, string]> = positionals.map((p) => {\n const isRequired = p.required !== false && p.default === undefined;\n return [cyan(p.name.toUpperCase()), renderArgDescription(p, isRequired)];\n });\n lines.push(alignTable(rows, ' '));\n lines.push('');\n }\n\n if (flags.length > 0) {\n lines.push(underline(bold('OPTIONS')));\n lines.push('');\n const rows: Array<[string, string]> = flags.map((f) => {\n const isRequired = f.required === true && f.default === undefined;\n const aliases = (\n Array.isArray(f.alias) ? f.alias : f.alias ? [f.alias] : []\n ).map((a) => `-${a}`);\n const label = [...aliases, `--${f.name}`].join(', ') + renderValueHint(f);\n return [cyan(label), renderArgDescription(f, isRequired)];\n });\n lines.push(alignTable(rows, ' '));\n lines.push('');\n }\n\n if (subCommandEntries.length > 0) {\n for (const line of renderCommandsBlock(subCommandEntries)) {\n lines.push(line);\n }\n lines.push(\n `Use ${cyan(`${fullName} <command> --help`)} for more information about a command.`,\n );\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Detect `--help` / `-h` somewhere in argv that's *not* preceded by\n * a separator `--`. Returns the command path so the caller can render\n * the right subcommand's help.\n *\n * Returns `null` when help wasn't requested at all.\n */\nexport function detectHelpRequest(\n argv: string[],\n main: CommandDef,\n): { path: string[]; cmd: CommandDef } | null {\n const helpIdx = argv.findIndex((a) => a === '--help' || a === '-h');\n const separatorIdx = argv.indexOf('--');\n if (helpIdx === -1) return null;\n if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;\n\n // Walk subcommands by matching argv tokens (in order, before --)\n // against the current command's `subCommands` map.\n const path: string[] = [];\n const tokens = argv.slice(\n 0,\n separatorIdx === -1 ? argv.length : separatorIdx,\n );\n let cursor: CommandDef = main;\n const mainName = ((main.meta ?? {}) as { name?: string }).name ?? 'monoceros';\n path.push(mainName);\n for (const tok of tokens) {\n if (tok.startsWith('-')) continue;\n const subs = (cursor.subCommands ?? {}) as Record<string, CommandDef>;\n if (tok in subs) {\n cursor = subs[tok]!;\n path.push(tok);\n continue;\n }\n // Token isn't a subcommand name — stop walking. Any further\n // tokens are positionals/values for the current command, not\n // routing hints.\n break;\n }\n return { path, cmd: cursor };\n}\n\n/**\n * If argv requests --help, print our own usage block and tell the\n * caller to exit. Returns true when help was rendered.\n */\nexport async function maybeRenderHelp(\n argv: string[],\n main: CommandDef,\n): Promise<boolean> {\n const hit = detectHelpRequest(argv, main);\n if (!hit) return false;\n // Resolve cmd's lazy fields (citty allows them to be functions)\n // before we render. We don't currently use lazy fields, so a\n // simple pass-through suffices.\n process.stdout.write(renderUsageBlock(hit.cmd, hit.path) + '\\n');\n return true;\n}\n","/**\n * Splits the user-args at the first `--` marker.\n *\n * `monoceros run -- monoceros-plugin --help` should hand `--help` to\n * `monoceros-plugin` inside the container, not trigger citty's eager\n * `--help` parser on the outer `monoceros run`. Citty parses `--help`\n * and `--version` before our subcommand handlers run, so the only\n * reliable fix is to strip everything after `--` from `process.argv`\n * before `runMain()` ever sees it.\n *\n * `splitInnerArgs` is the pure helper.\n * `consumeInnerArgsFromProcessArgv` is the side-effecting glue called\n * from `bin.ts`. `getInnerArgs()` is read by `runCommand`.\n */\n\nlet innerArgs: readonly string[] = [];\n\nexport function splitInnerArgs(userArgs: readonly string[]): {\n outerArgs: string[];\n innerArgs: string[];\n} {\n const dashIdx = userArgs.indexOf('--');\n if (dashIdx === -1) {\n return { outerArgs: [...userArgs], innerArgs: [] };\n }\n return {\n outerArgs: userArgs.slice(0, dashIdx),\n innerArgs: userArgs.slice(dashIdx + 1),\n };\n}\n\nexport function consumeInnerArgsFromProcessArgv(): void {\n // process.argv[0] = node, [1] = script path, [2..] = user args\n const userArgs = process.argv.slice(2);\n const split = splitInnerArgs(userArgs);\n process.argv = [...process.argv.slice(0, 2), ...split.outerArgs];\n innerArgs = split.innerArgs;\n}\n\nexport function getInnerArgs(): readonly string[] {\n return innerArgs;\n}\n\n/** Test seam: lets unit tests set inner args without touching process.argv. */\nexport function setInnerArgsForTesting(args: readonly string[]): void {\n innerArgs = args;\n}\n","import { defineCommand } from 'citty';\nimport { addAptPackagesCommand } from './commands/add-apt-packages.js';\nimport { addFeatureCommand } from './commands/add-feature.js';\nimport { addFromUrlCommand } from './commands/add-from-url.js';\nimport { addRepoCommand } from './commands/add-repo.js';\nimport { addLanguageCommand } from './commands/add-language.js';\nimport { addServiceCommand } from './commands/add-service.js';\nimport { applyCommand } from './commands/apply.js';\nimport { completionCommand } from './commands/completion.js';\nimport { initCommand } from './commands/init.js';\nimport { listComponentsCommand } from './commands/list-components.js';\nimport { logsCommand } from './commands/logs.js';\nimport { removeAptPackagesCommand } from './commands/remove-apt-packages.js';\nimport { removeFeatureCommand } from './commands/remove-feature.js';\nimport { removeCommand } from './commands/remove.js';\nimport { restoreCommand } from './commands/restore.js';\nimport { removeFromUrlCommand } from './commands/remove-from-url.js';\nimport { removeLanguageCommand } from './commands/remove-language.js';\nimport { removeRepoCommand } from './commands/remove-repo.js';\nimport { removeServiceCommand } from './commands/remove-service.js';\nimport { runCommand } from './commands/run.js';\nimport { shellCommand } from './commands/shell.js';\nimport { startCommand } from './commands/start.js';\nimport { statusCommand } from './commands/status.js';\nimport { stopCommand } from './commands/stop.js';\nimport { CLI_VERSION } from './version.js';\n\nexport const main = defineCommand({\n meta: {\n name: 'monoceros',\n version: CLI_VERSION,\n description:\n 'Monoceros workbench — local, sandboxed AI-coding environment for solution builders.',\n },\n subCommands: {\n init: initCommand,\n 'list-components': listComponentsCommand,\n shell: shellCommand,\n run: runCommand,\n logs: logsCommand,\n start: startCommand,\n stop: stopCommand,\n status: statusCommand,\n apply: applyCommand,\n remove: removeCommand,\n restore: restoreCommand,\n 'add-service': addServiceCommand,\n 'add-language': addLanguageCommand,\n 'add-apt-packages': addAptPackagesCommand,\n 'add-feature': addFeatureCommand,\n 'add-from-url': addFromUrlCommand,\n 'add-repo': addRepoCommand,\n 'remove-service': removeServiceCommand,\n 'remove-language': removeLanguageCommand,\n 'remove-apt-packages': removeAptPackagesCommand,\n 'remove-feature': removeFeatureCommand,\n 'remove-from-url': removeFromUrlCommand,\n 'remove-repo': removeRepoCommand,\n completion: completionCommand,\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runAddAptPackages } from '../modify/index.js';\n\nexport const addAptPackagesCommand = defineCommand({\n meta: {\n name: 'add-apt-packages',\n group: 'edit',\n description:\n 'Add Debian/Ubuntu apt packages to the container config. Pass package names after `--` (e.g. `monoceros add-apt-packages sandbox -- make openssh-client jq`). Idempotent. No curated whitelist — invalid names surface as apt errors at container build time.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n const packages = [...getInnerArgs()];\n if (packages.length === 0) {\n consola.error(\n 'No package names given. Usage: `monoceros add-apt-packages <containername> [--yes] -- <pkg> [<pkg> …]`.',\n );\n process.exit(1);\n }\n try {\n const result = await runAddAptPackages({\n name: args.name,\n packages,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport { createPatch } from 'diff';\nimport type { Document } from 'yaml';\nimport { parseConfig, stringifyConfig } from '../config/io.js';\nimport {\n containerConfigPath,\n monocerosHome as defaultMonocerosHome,\n} from '../config/paths.js';\nimport {\n KNOWN_PROVIDER_HOSTS,\n PROVIDER_VALUES,\n REGEX,\n type RepoProvider,\n} from '../config/schema.js';\nimport {\n BUILTIN_LANGUAGES,\n LANGUAGE_CATALOG,\n SERVICE_CATALOG,\n knownLanguages,\n knownServices,\n} from '../create/catalog.js';\nimport { deriveRepoName } from '../create/scaffold.js';\nimport type { FeatureOptions, RepoEntry } from '../create/types.js';\nimport {\n addAptPackagesToDoc,\n addFeatureToDoc,\n addInstallUrlToDoc,\n addLanguageToDoc,\n addRepoToDoc,\n addServiceToDoc,\n removeAptPackagesFromDoc,\n removeFeatureFromDoc,\n removeInstallUrlFromDoc,\n removeLanguageFromDoc,\n removeRepoFromDoc,\n removeServiceFromDoc,\n} from './yml.js';\n\n/**\n * `monoceros add-*` / `monoceros remove-*` — edit the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml` for one container.\n *\n * No cwd magic. The first positional argument is always the container\n * name; the command looks up the yml via convention. Comment-preserving\n * AST mutation; the builder then runs `monoceros apply <name>` to\n * materialize.\n */\n\nexport interface ModifyLogger {\n info: (message: string) => void;\n success: (message: string) => void;\n warn: (message: string) => void;\n}\n\nexport type ConfirmFn = (prompt: string) => Promise<boolean>;\n\nexport interface ModifyOptions {\n /** Container name — resolves to `<home>/container-configs/<name>.yml`. */\n name: string;\n yes?: boolean;\n logger?: ModifyLogger;\n output?: (line: string) => void;\n confirm?: ConfirmFn;\n /** Override the resolved MONOCEROS_HOME. Tests inject a tmpdir. */\n monocerosHome?: string;\n}\n\nexport interface AddLanguageInput extends ModifyOptions {\n language: string;\n}\nexport interface AddServiceInput extends ModifyOptions {\n service: string;\n}\nexport interface AddAptPackagesInput extends ModifyOptions {\n packages: string[];\n}\nexport interface AddFeatureInput extends ModifyOptions {\n ref: string;\n options?: FeatureOptions;\n}\nexport interface AddFromUrlInput extends ModifyOptions {\n url: string;\n}\nexport interface AddRepoInput extends ModifyOptions {\n url: string;\n /**\n * Explicit destination path under `projects/`. Subfolders allowed\n * via `/` (e.g. `apps/web`). When omitted, the URL-derived single-\n * segment default is used (`https://.../foo.git` → `foo` →\n * `projects/foo/`).\n */\n path?: string;\n /**\n * Optional per-repo git committer identity override. Both name and\n * email must be set together; one alone is a usage error. Falls\n * back to the container-level `git.user` (which itself falls back\n * to the host's `git config --global`) when omitted.\n */\n gitName?: string;\n gitEmail?: string;\n /**\n * Git provider hint. Required when the URL host is not one of the\n * three canonical ones (github.com / gitlab.com / bitbucket.org);\n * optional otherwise. Validated against `PROVIDER_VALUES`.\n */\n provider?: string;\n}\n\nexport interface RemoveLanguageInput extends ModifyOptions {\n language: string;\n}\nexport interface RemoveServiceInput extends ModifyOptions {\n service: string;\n}\nexport interface RemoveAptPackagesInput extends ModifyOptions {\n packages: string[];\n}\nexport interface RemoveFeatureInput extends ModifyOptions {\n ref: string;\n}\nexport interface RemoveFromUrlInput extends ModifyOptions {\n url: string;\n}\nexport interface RemoveRepoInput extends ModifyOptions {\n /** url or (effective) name — `monoceros remove-repo` accepts either. */\n target: string;\n}\n\nexport type ModifyResult =\n | { status: 'no-change' }\n | { status: 'updated'; changedPaths: string[] }\n | { status: 'aborted' };\n\ntype YmlMutator = (doc: Document) => boolean;\n\n// ─── add-* ────────────────────────────────────────────────────────\n\nexport function runAddLanguage(input: AddLanguageInput): Promise<ModifyResult> {\n if (\n !BUILTIN_LANGUAGES.has(input.language) &&\n !LANGUAGE_CATALOG[input.language]\n ) {\n throw new Error(\n `Unknown language: ${input.language}. Known: ${knownLanguages().join(', ')}.`,\n );\n }\n return mutate(input, (doc) => addLanguageToDoc(doc, input.language));\n}\n\nexport function runAddService(input: AddServiceInput): Promise<ModifyResult> {\n if (!SERVICE_CATALOG[input.service]) {\n throw new Error(\n `Unknown service: ${input.service}. Known: ${knownServices().join(', ')}.`,\n );\n }\n return mutate(input, (doc) => addServiceToDoc(doc, input.service));\n}\n\nexport function runAddAptPackages(\n input: AddAptPackagesInput,\n): Promise<ModifyResult> {\n if (input.packages.length === 0) {\n throw new Error(\n 'No package names given. Usage: monoceros add-apt-packages <containername> -- <pkg> [<pkg> …].',\n );\n }\n return mutate(input, (doc) => addAptPackagesToDoc(doc, input.packages));\n}\n\nexport async function runAddRepo(input: AddRepoInput): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing repo URL. Usage: monoceros add-repo <containername> <url>.',\n );\n }\n const path = (input.path ?? deriveRepoName(url)).trim();\n // --git-name and --git-email come as a pair. Reject half-set input\n // loudly instead of silently dropping it.\n const hasName =\n typeof input.gitName === 'string' && input.gitName.trim().length > 0;\n const hasEmail =\n typeof input.gitEmail === 'string' && input.gitEmail.trim().length > 0;\n if (hasName !== hasEmail) {\n throw new Error(\n '--git-name and --git-email must be set together. Pass both, or neither.',\n );\n }\n // --provider validation:\n // - host is canonical (github.com / gitlab.com / bitbucket.org):\n // * no --provider → fine, auto-detected at apply time\n // * --provider matches canonical → accepted, written to yml\n // (harmless; round-trip stays clean)\n // * --provider contradicts canonical → reject loudly\n // - host is non-canonical:\n // * --provider given (valid enum) → write it\n // * --provider missing → reject; the apply pre-flight would\n // fail anyway, fail at add-repo time for a better signal\n // * --provider invalid value → reject with allowed list\n const explicitProvider = normalizeProvider(input.provider);\n let host: string | undefined;\n try {\n host = url.startsWith('https://') ? new URL(url).hostname : undefined;\n } catch {\n host = undefined;\n }\n const canonical = host ? KNOWN_PROVIDER_HOSTS[host.toLowerCase()] : undefined;\n if (host && !canonical && !explicitProvider) {\n throw new Error(\n `Host '${host}' is not a canonical Git provider Monoceros can auto-detect (github.com / gitlab.com / bitbucket.org). Pass --provider=github|gitlab|bitbucket so the credential-helper hints know which CLI to suggest.`,\n );\n }\n if (canonical && explicitProvider && explicitProvider !== canonical) {\n throw new Error(\n `--provider=${explicitProvider} contradicts host '${host}' (auto-detected as ${canonical}). Drop --provider for canonical hosts, or fix the value.`,\n );\n }\n // For canonical hosts we don't persist `provider:` in the yml even\n // when the flag was passed (matches what auto-detection would do\n // and keeps the yml minimal). Non-canonical hosts: write the\n // explicit value as-is.\n const providerToWrite =\n !canonical && explicitProvider ? explicitProvider : undefined;\n const entry: RepoEntry = {\n url,\n path,\n ...(hasName && hasEmail\n ? {\n gitUser: {\n name: input.gitName!.trim(),\n email: input.gitEmail!.trim(),\n },\n }\n : {}),\n ...(providerToWrite ? { provider: providerToWrite } : {}),\n };\n return mutate(input, (doc) => addRepoToDoc(doc, entry));\n}\n\nfunction normalizeProvider(raw: string | undefined): RepoProvider | undefined {\n if (typeof raw !== 'string') return undefined;\n const trimmed = raw.trim();\n if (trimmed.length === 0) return undefined;\n const lowered = trimmed.toLowerCase() as RepoProvider;\n if (!(PROVIDER_VALUES as readonly string[]).includes(lowered)) {\n throw new Error(\n `Invalid --provider value: ${JSON.stringify(raw)}. Allowed: ${PROVIDER_VALUES.join(', ')}.`,\n );\n }\n return lowered;\n}\n\nexport function runAddFromUrl(input: AddFromUrlInput): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing URL. Usage: monoceros add-from-url <containername> <url>.',\n );\n }\n return mutate(input, (doc) => addInstallUrlToDoc(doc, url));\n}\n\nexport function runAddFeature(input: AddFeatureInput): Promise<ModifyResult> {\n const ref = input.ref.trim();\n if (ref.length === 0) {\n throw new Error(\n 'Missing feature ref. Usage: monoceros add-feature <containername> <ref>.',\n );\n }\n return mutate(input, (doc) => addFeatureToDoc(doc, ref, input.options ?? {}));\n}\n\n// ─── remove-* ─────────────────────────────────────────────────────\n\nexport function runRemoveLanguage(\n input: RemoveLanguageInput,\n): Promise<ModifyResult> {\n return mutate(input, (doc) => removeLanguageFromDoc(doc, input.language));\n}\n\nexport function runRemoveService(\n input: RemoveServiceInput,\n): Promise<ModifyResult> {\n return mutate(input, (doc) => removeServiceFromDoc(doc, input.service));\n}\n\nexport function runRemoveAptPackages(\n input: RemoveAptPackagesInput,\n): Promise<ModifyResult> {\n if (input.packages.length === 0) {\n throw new Error(\n 'No package names given. Usage: monoceros remove-apt-packages <containername> -- <pkg> [<pkg> …].',\n );\n }\n return mutate(input, (doc) => removeAptPackagesFromDoc(doc, input.packages));\n}\n\nexport function runRemoveFeature(\n input: RemoveFeatureInput,\n): Promise<ModifyResult> {\n const ref = input.ref.trim();\n if (ref.length === 0) {\n throw new Error(\n 'Missing feature ref. Usage: monoceros remove-feature <containername> <ref>.',\n );\n }\n return mutate(input, (doc) => removeFeatureFromDoc(doc, ref));\n}\n\nexport function runRemoveFromUrl(\n input: RemoveFromUrlInput,\n): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing URL. Usage: monoceros remove-from-url <containername> <url>.',\n );\n }\n return mutate(input, (doc) => removeInstallUrlFromDoc(doc, url));\n}\n\nexport function runRemoveRepo(input: RemoveRepoInput): Promise<ModifyResult> {\n const target = input.target.trim();\n if (target.length === 0) {\n throw new Error(\n 'Missing repo identifier. Usage: monoceros remove-repo <containername> <url-or-name>.',\n );\n }\n return mutate(input, (doc) => removeRepoFromDoc(doc, target));\n}\n\n// ─── core mutate skeleton ─────────────────────────────────────────\n\nasync function mutate(\n opts: ModifyOptions,\n apply: YmlMutator,\n): Promise<ModifyResult> {\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid container name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const ymlPath = containerConfigPath(opts.name, home);\n const logger = opts.logger ?? defaultLogger();\n\n let oldText: string;\n try {\n oldText = await fs.readFile(ymlPath, 'utf8');\n } catch {\n throw new Error(\n `No such config: ${ymlPath}. Run \\`monoceros init <template> ${opts.name}\\` first.`,\n );\n }\n\n const parsed = parseConfig(oldText, ymlPath);\n const changed = apply(parsed.doc);\n\n if (!changed) {\n logger.info('No changes — yml is already in the desired state.');\n return { status: 'no-change' };\n }\n\n // Re-validate via a round-trip so schema violations introduced by\n // the mutation surface here with the regular field-path error, not\n // later at apply time.\n const newText = stringifyConfig(parsed.doc);\n parseConfig(newText, ymlPath);\n\n const out = opts.output ?? ((line) => process.stdout.write(line + '\\n'));\n out(createPatch(ymlPath, oldText, newText, 'before', 'after'));\n\n if (!opts.yes) {\n const confirm = opts.confirm ?? defaultConfirm;\n const ok = await confirm('Apply these changes to the yml?');\n if (!ok) {\n logger.warn('Aborted by user. The yml was not modified.');\n return { status: 'aborted' };\n }\n }\n\n await fs.writeFile(ymlPath, newText, 'utf8');\n logger.success(`Updated ${ymlPath}.`);\n logger.info(\n `Run \\`monoceros apply ${opts.name}\\` to rebuild the dev-container and pick up the change.`,\n );\n return { status: 'updated', changedPaths: [ymlPath] };\n}\n\nfunction defaultLogger(): ModifyLogger {\n return {\n info: (m) => consola.info(m),\n success: (m) => consola.success(m),\n warn: (m) => consola.warn(m),\n };\n}\n\nconst defaultConfirm: ConfirmFn = async (message) => {\n const result = await consola.prompt(message, {\n type: 'confirm',\n initial: false,\n });\n return result === true;\n};\n","import { promises as fs } from 'node:fs';\nimport { Document, parseDocument } from 'yaml';\nimport { type SolutionConfig, validateConfig } from './schema.js';\n\n/**\n * A parsed solution-config yml plus its AST. `config` is the validated\n * plain-JS view (used to drive the apply pipeline); `doc` is the\n * `yaml.Document` (used by mutation helpers so comments and ordering\n * survive a round-trip).\n */\nexport interface ParsedConfig {\n config: SolutionConfig;\n doc: Document.Parsed;\n /** Source path or `<inline>` for an in-memory parse. Used in errors. */\n source: string;\n}\n\n/**\n * Parse a yml string and validate against the schema. Throws on\n * yaml syntax errors and on schema violations. The returned `doc`\n * preserves comments and node ordering — pass it to mutation helpers\n * (`addRepoToDoc`, …) and `stringifyConfig` so the builder's hand-\n * written comments survive `monoceros add-*`.\n */\nexport function parseConfig(\n yamlText: string,\n source = '<inline>',\n): ParsedConfig {\n const doc = parseDocument(yamlText, { prettyErrors: true });\n if (doc.errors.length > 0) {\n const first = doc.errors[0]!;\n throw new Error(`yaml parse error in ${source}: ${first.message}`);\n }\n const config = validateConfig(doc.toJS());\n return { config, doc, source };\n}\n\nexport async function readConfig(filePath: string): Promise<ParsedConfig> {\n const text = await fs.readFile(filePath, 'utf8');\n return parseConfig(text, filePath);\n}\n\n/** Serialize a Document back to yaml. */\nexport function stringifyConfig(doc: Document): string {\n return String(doc);\n}\n\nexport async function writeConfig(\n filePath: string,\n doc: Document,\n): Promise<void> {\n await fs.writeFile(filePath, stringifyConfig(doc), 'utf8');\n}\n\n/**\n * Build a fresh Document from a plain-JS config object. Used when\n * generating a yml from a template (no source comments to preserve)\n * or when migrating an existing stack.json. The resulting Document\n * is suitable for further mutation + `stringifyConfig`.\n *\n * The output is stable: keys appear in the canonical order defined\n * by `KEY_ORDER` below, so two configs with the same content yield\n * byte-identical yaml.\n */\nexport function createDoc(config: SolutionConfig): Document {\n const ordered: Record<string, unknown> = {};\n for (const key of KEY_ORDER) {\n if (key in config) {\n const value = (config as unknown as Record<string, unknown>)[key];\n if (isEmptyContainer(value)) continue;\n ordered[key] = value;\n }\n }\n const doc = new Document(ordered);\n return doc;\n}\n\n/**\n * Canonical key order in generated yaml. Matches the example skeleton\n * in `docs/backlog.md`. Hand-edited yml does not have to follow this\n * order (parser accepts any) — but anything `createDoc` writes does.\n */\nconst KEY_ORDER = [\n 'schemaVersion',\n 'name',\n 'languages',\n 'aptPackages',\n 'features',\n 'installUrls',\n 'services',\n 'repos',\n 'externalServices',\n 'git',\n] as const;\n\nfunction isEmptyContainer(value: unknown): boolean {\n if (Array.isArray(value)) return value.length === 0;\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>).length === 0;\n }\n return false;\n}\n","import { z } from 'zod';\n\n/**\n * Shape validation for a Monoceros solution-config yml. Catalog\n * validation (which languages/services actually exist) happens\n * separately in `apply`, against `create/catalog.ts` — that keeps the\n * schema decoupled from the catalog and lets the schema live without\n * pulling the whole devcontainer scaffold module in.\n *\n * Schema mirrors the StackFile shape from `create/types.ts` except:\n *\n * - `features` is an **array** of `{ ref, options }` entries (yml is\n * edited by humans and arrays diff/comment better than maps).\n * The apply step converts to the Record shape `devcontainer.json`\n * expects.\n *\n * - `externalServices.postgres` carries what `CreateOptions.postgresUrl`\n * does today.\n *\n * - `git.user.{name,email}` carries the host-captured identity so the\n * yml-as-profile is self-contained when shared across containers.\n * Optional — falls back to host-side `git config --global --get` +\n * `.monoceros/gitconfig` at apply time, same as today.\n */\n\nconst SOLUTION_NAME_RE = /^[A-Za-z0-9._-]+$/;\nconst APT_PACKAGE_NAME_RE = /^[a-z0-9][a-z0-9.+-]*$/;\n// Feature refs are OCI-style:\n// <registry>/<namespace>/<feature>:<tag>\n// e.g. ghcr.io/devcontainers/features/python:1\n// ghcr.io/getmonoceros/monoceros-features/claude-code:1\nconst FEATURE_REF_RE = /^[a-z0-9.-]+(\\/[a-z0-9._-]+)+:[a-z0-9._-]+$/;\nconst INSTALL_URL_RE = /^https:\\/\\/[A-Za-z0-9.\\-_~/:?#[\\]@!&'()*+,;=%]+$/;\n// Repo URLs are HTTPS-only by design. SSH-style URLs (git@host:...,\n// ssh://...) are explicitly out of scope — see ADR 0006 for the\n// reasoning. The schema rejects them at parse time with a clear\n// message rather than letting them through and failing opaquely\n// during the clone in post-create.sh.\nconst REPO_URL_RE = /^https:\\/\\/[A-Za-z0-9@:/+_~.#=&?-]+$/;\n// Path under `projects/`. Allows nested subfolders via `/` (e.g.\n// `apps/web`, `monorepo/libs/shared`). The regex enforces:\n// - non-empty\n// - segments use [A-Za-z0-9._-] (same charset as a leaf folder name)\n// - no leading `/`, no trailing `/`, no consecutive `//`\n// A separate refine rejects `.` / `..` segments — those would either\n// be no-ops or escape `projects/`, neither belongs in a checked-in\n// container yml.\nconst REPO_PATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\nconst POSTGRES_URL_RE = /^postgres(ql)?:\\/\\//;\n\nexport const REGEX = {\n solutionName: SOLUTION_NAME_RE,\n aptPackage: APT_PACKAGE_NAME_RE,\n featureRef: FEATURE_REF_RE,\n installUrl: INSTALL_URL_RE,\n repoUrl: REPO_URL_RE,\n repoPath: REPO_PATH_RE,\n postgresUrl: POSTGRES_URL_RE,\n};\n\n/**\n * The providers Monoceros knows how to render setup hints for.\n *\n * Canonical SaaS hostnames (`github.com` / `gitlab.com` /\n * `bitbucket.org`) auto-detect to their provider. Everything else\n * — self-hosted GitLab, GitHub Enterprise, Bitbucket Data Center,\n * Gitea / Forgejo — must declare `provider:` explicitly. Gitea has\n * no canonical SaaS host (gitea.com is a demo, not a SaaS), so any\n * `provider: gitea` entry is by definition self-hosted.\n *\n * Forgejo (the community fork of Gitea) shares Gitea's API, UI, and\n * auth flow — we bundle it under `provider: gitea` rather than\n * carrying a separate enum value.\n */\nexport const PROVIDER_VALUES = [\n 'github',\n 'gitlab',\n 'bitbucket',\n 'gitea',\n] as const;\nexport type RepoProvider = (typeof PROVIDER_VALUES)[number];\n\n/**\n * Hostnames whose provider is implicit — no `provider:` field needed\n * in the yml. Everything else (self-hosted GitLab on `git.firma.de`,\n * Gitea instances, …) requires an explicit declaration; the apply\n * pre-flight enforces that.\n */\nexport const KNOWN_PROVIDER_HOSTS: Readonly<Record<string, RepoProvider>> = {\n 'github.com': 'github',\n 'gitlab.com': 'gitlab',\n 'bitbucket.org': 'bitbucket',\n};\n\n/** Current schema version. Bumped only on breaking yml changes. */\nexport const CONFIG_SCHEMA_VERSION = 1 as const;\n\nexport const FeatureOptionValueSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n]);\n\nexport const FeatureEntrySchema = z.object({\n ref: z\n .string()\n .regex(\n FEATURE_REF_RE,\n \"Invalid feature ref. Expected an OCI-image-style ref like 'ghcr.io/devcontainers/features/<name>:<tag>'.\",\n ),\n options: z.record(z.string(), FeatureOptionValueSchema).optional(),\n});\n\nexport const GitUserSchema = z.object({\n name: z.string().min(1),\n email: z\n .string()\n .min(3)\n .regex(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/, 'Invalid email'),\n});\n\nexport const RepoEntrySchema = z.object({\n url: z\n .string()\n .regex(\n REPO_URL_RE,\n 'Invalid repo URL. Only HTTPS URLs are supported (https://...). SSH-style URLs (git@host:..., ssh://...) are not in scope — see ADR 0006.',\n ),\n path: z\n .string()\n .regex(\n REPO_PATH_RE,\n \"Invalid repo path. Use letters/digits/'._-', forward slashes for nested folders, no leading or trailing slash.\",\n )\n .refine(\n (p) => !p.split('/').some((seg) => seg === '..' || seg === '.'),\n 'Repo path segments cannot be \".\" or \"..\".',\n )\n .optional(),\n // Per-repo git identity override. Falls back to the container-level\n // `git.user` (which itself falls back to the host's\n // `git config --global` at apply time). Useful when a single\n // container clones multiple repos that need different committer\n // identities — e.g. work GitHub org vs personal projects.\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n // Provider hint for the pre-flight credential check. For the three\n // canonical hosts (github.com / gitlab.com / bitbucket.org) the\n // provider is auto-detected and this field is unnecessary. For any\n // other host (self-hosted GitLab on a custom domain, Gitea, …) the\n // builder MUST declare the provider so apply can suggest the right\n // CLI setup (`glab auth login --hostname <host>` etc.) when\n // credentials are missing. Enforced at apply pre-flight, not at\n // parse time — see ADR 0006.\n provider: z.enum(PROVIDER_VALUES).optional(),\n});\n\nexport const ExternalServicesSchema = z.object({\n postgres: z\n .string()\n .regex(\n POSTGRES_URL_RE,\n \"Postgres URL must start with 'postgres://' or 'postgresql://'\",\n )\n .optional(),\n});\n\nexport const SolutionConfigSchema = z.object({\n schemaVersion: z.literal(CONFIG_SCHEMA_VERSION),\n name: z\n .string()\n .regex(\n SOLUTION_NAME_RE,\n \"Invalid solution name. Use letters, digits, '.', '_' or '-'.\",\n ),\n languages: z.array(z.string().min(1)).default([]),\n aptPackages: z\n .array(\n z\n .string()\n .regex(\n APT_PACKAGE_NAME_RE,\n \"Invalid apt package name. Expected lowercase alphanumeric plus '.+-'.\",\n ),\n )\n .default([]),\n features: z.array(FeatureEntrySchema).default([]),\n installUrls: z\n .array(\n z\n .string()\n .regex(\n INSTALL_URL_RE,\n \"Invalid install URL. Must start with 'https://' and contain only URL-safe characters (no shell metacharacters).\",\n ),\n )\n .default([]),\n services: z.array(z.string().min(1)).default([]),\n repos: z.array(RepoEntrySchema).default([]),\n externalServices: ExternalServicesSchema.default({}),\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n});\n\nexport type SolutionConfig = z.infer<typeof SolutionConfigSchema>;\nexport type FeatureEntry = z.infer<typeof FeatureEntrySchema>;\nexport type RepoEntry = z.infer<typeof RepoEntrySchema>;\nexport type GitUser = z.infer<typeof GitUserSchema>;\nexport type ExternalServices = z.infer<typeof ExternalServicesSchema>;\n\n/**\n * Validate parsed yml (e.g. from `doc.toJS()`) against the schema. On\n * failure, throws an Error whose message lists every issue with its\n * dotted path — the apply step prints that verbatim, so the builder\n * sees exactly which yml field is wrong.\n */\nexport function validateConfig(input: unknown): SolutionConfig {\n const result = SolutionConfigSchema.safeParse(input);\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid solution config:\\n${issues}`);\n }\n return result.data;\n}\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Path helpers for the M2.5 Phase 3 yml-profile model.\n *\n * Two distinct roots:\n *\n * - `workbenchRoot()` — where the **CLI bundle** lives. In dev that's\n * the monoceros-workbench checkout (so `templates/yml/` is reachable\n * and the workbench can be bind-mounted into generated containers\n * as `/opt/monoceros-workbench`). In prod (post-M4) this is the\n * installed package directory.\n *\n * - `monocerosHome()` — where **user data** lives: container-configs,\n * materialized containers, the global `monoceros-config.yml`.\n *\n * The two used to be conflated under one `workbenchRoot()`; splitting\n * them is what lets `monoceros apply <name>` resolve a fixed\n * `<MONOCEROS_HOME>/container/<name>/` location without any cwd\n * magic, while the CLI itself still knows where its bundled templates\n * live.\n *\n * Layout under `<MONOCEROS_HOME>/`:\n * container-configs/<name>.yml ← yml-Profile (`monoceros init`)\n * container/<name>/ ← materialized dev-containers\n * monoceros-config.yml ← optional, user-edited defaults\n * monoceros-config.sample.yml ← marker (in dev) + template (in prod)\n */\n\nconst MONOCEROS_HOME_MARKER = 'monoceros-config.sample.yml';\nconst WORKBENCH_MARKER = path.join('templates', 'components', 'README.md');\nconst CHECKOUT_MARKER = 'pnpm-workspace.yaml';\n\nlet cachedWorkbenchRoot: string | null = null;\nlet cachedMonocerosHome: string | null = null;\nlet cachedCheckoutRoot: string | null | undefined = undefined;\n\n/**\n * Walk upwards from this module until we find the workbench checkout's\n * marker (`templates/components/README.md`). In dev that hits the\n * workbench root reliably; in production the file does not exist\n * outside the shipped CLI package, so callers that need a workbench\n * root for dev-only purposes (bind-mounting `/opt/monoceros-workbench`)\n * get a clear error.\n */\nexport function workbenchRoot(): string {\n if (cachedWorkbenchRoot) return cachedWorkbenchRoot;\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n if (existsSync(path.join(dir, WORKBENCH_MARKER))) {\n cachedWorkbenchRoot = dir;\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(\n `Could not locate the monoceros workbench checkout (no ${WORKBENCH_MARKER} found by walking up). Run the CLI from a workbench checkout.`,\n );\n }\n dir = parent;\n }\n}\n\n/**\n * Resolve `MONOCEROS_HOME` (where user data lives):\n *\n * 1. Honor the `MONOCEROS_HOME` env-var if set.\n * 2. Walk upwards from this module and accept the first\n * `<dir>/.local/monoceros-config.sample.yml` we find; the\n * containing `<dir>/.local` is treated as the home. This is the\n * dev-workbench detection path.\n * 3. Fall back to `~/.monoceros`.\n *\n * Caches the result for the lifetime of the process — flip `force` to\n * recompute (tests do this between cases).\n */\nexport function monocerosHome(opts: { force?: boolean } = {}): string {\n if (!opts.force && cachedMonocerosHome) return cachedMonocerosHome;\n\n const fromEnv = process.env.MONOCEROS_HOME;\n if (fromEnv && fromEnv.length > 0) {\n cachedMonocerosHome = path.resolve(fromEnv);\n return cachedMonocerosHome;\n }\n\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const candidate = path.join(dir, '.local');\n if (existsSync(path.join(candidate, MONOCEROS_HOME_MARKER))) {\n cachedMonocerosHome = candidate;\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n cachedMonocerosHome = path.join(os.homedir(), '.monoceros');\n return cachedMonocerosHome;\n}\n\n/**\n * Walk upwards from this module to find the workbench checkout root,\n * marked by `pnpm-workspace.yaml`. Distinct from `workbenchRoot()`:\n *\n * - `workbenchRoot()` returns where the **CLI bundle** lives —\n * `packages/cli/` in dev, the installed package directory in prod.\n * - `workbenchCheckoutRoot()` returns where the **full workbench\n * checkout** lives. Only meaningful in dev; returns `null` in\n * prod (the marker doesn't ship with the npm package).\n *\n * Used by features like the dev-only local-source-fallback in\n * `resolveFeatures`, where we want to look at `images/features/<name>/`\n * at the checkout root — not inside the CLI package, where that\n * directory deliberately doesn't exist.\n */\nexport function workbenchCheckoutRoot(): string | null {\n if (cachedCheckoutRoot !== undefined) return cachedCheckoutRoot;\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n if (existsSync(path.join(dir, CHECKOUT_MARKER))) {\n cachedCheckoutRoot = dir;\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n cachedCheckoutRoot = null;\n return null;\n }\n dir = parent;\n }\n}\n\n/** Reset cached lookups. Test-only. */\nexport function _resetPathCachesForTests(): void {\n cachedWorkbenchRoot = null;\n cachedMonocerosHome = null;\n cachedCheckoutRoot = undefined;\n}\n\n// ─── CLI-bundle paths (templates) ─────────────────────────────────\n\n/**\n * `templates/components/` — the components catalog used by\n * `monoceros init`. Each file under this directory is a small yml\n * snippet describing one composable component (a language, a\n * service, or a feature). See `templates/components/README.md`.\n */\nexport function componentsDir(root: string = workbenchRoot()): string {\n return path.join(root, 'templates', 'components');\n}\n\n/**\n * `features/` (inside the CLI bundle) — npm-shipped copies of the\n * Monoceros feature manifests (`devcontainer-feature.json`). Built\n * by `pnpm manifests:sync` from `images/features/<name>/` and\n * included in the published tarball via the `files` field. The\n * init generator's hint loader looks here as the production\n * fallback when the workbench checkout isn't available.\n */\nexport function bundledFeaturesDir(root: string = workbenchRoot()): string {\n return path.join(root, 'features');\n}\n\n// ─── User-home paths (configs, containers, global config) ────────\n\nexport function containerConfigsDir(home: string = monocerosHome()): string {\n return path.join(home, 'container-configs');\n}\n\nexport function containerConfigPath(\n name: string,\n home: string = monocerosHome(),\n): string {\n return path.join(containerConfigsDir(home), `${name}.yml`);\n}\n\nexport function containersDir(home: string = monocerosHome()): string {\n return path.join(home, 'container');\n}\n\nexport function containerDir(\n name: string,\n home: string = monocerosHome(),\n): string {\n return path.join(containersDir(home), name);\n}\n\nexport function monocerosConfigPath(home: string = monocerosHome()): string {\n return path.join(home, 'monoceros-config.yml');\n}\n\n// ─── User-facing path formatting ─────────────────────────────────\n\n/**\n * Format an absolute path for printing in CLI output: collapses a\n * `$HOME` prefix to `~` so messages stay short without losing\n * information. Non-home paths pass through verbatim.\n *\n * prettyPath('/Users/x/.monoceros/container-configs/hello.yml')\n * → '~/.monoceros/container-configs/hello.yml'\n *\n * Use this whenever a log line tells the user where something\n * landed on disk — `monoceros init`, `apply`, `remove`, `restore`\n * all rely on it so users see one consistent format.\n */\nexport function prettyPath(p: string): string {\n const home = os.homedir();\n if (!home) return p;\n if (p === home) return '~';\n const prefix = home.endsWith(path.sep) ? home : home + path.sep;\n if (p.startsWith(prefix)) {\n return '~' + path.sep + p.slice(prefix.length);\n }\n return p;\n}\n","// Catalogs of supported language toolchains and backing services for\n// the yml profile. Curated whitelists keep the surface small and\n// reviewable; unknown values are rejected up front rather than passed\n// through to devcontainer / compose.\n\n// Monoceros runtime image — thin layer on top of Microsoft's\n// typescript-node base (see images/runtime/Dockerfile). The default\n// points at the floating major tag on GHCR, so an `apply` from a\n// fresh install pulls a published image without further setup.\n//\n// Contributors who are iterating on the runtime image itself\n// (`pnpm image:build` → `monoceros-runtime:dev`) can override this\n// via the `MONOCEROS_BASE_IMAGE_OVERRIDE` env var to point at their\n// local tag without editing source. Empty or whitespace-only values\n// are ignored so an accidentally-set-blank var doesn't break apply.\nconst DEFAULT_BASE_IMAGE = 'ghcr.io/getmonoceros/monoceros-runtime:1';\nconst override = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();\nexport const BASE_IMAGE =\n override && override.length > 0 ? override : DEFAULT_BASE_IMAGE;\n\nexport interface LanguageEntry {\n id: string;\n feature: string;\n}\n\n// `node` is included in the base runtime image, so the bare entry\n// `languages: [node]` is accepted as input but installs nothing\n// extra. Versioned node — `node:20` — bypasses the builtin set and\n// goes through the upstream feature like the other languages,\n// because the base image's node version (22) isn't selectable\n// otherwise.\nexport const BUILTIN_LANGUAGES = new Set(['node']);\n\nexport const LANGUAGE_CATALOG: Readonly<Record<string, LanguageEntry>> = {\n node: { id: 'node', feature: 'ghcr.io/devcontainers/features/node:1' },\n python: { id: 'python', feature: 'ghcr.io/devcontainers/features/python:1' },\n java: { id: 'java', feature: 'ghcr.io/devcontainers/features/java:1' },\n go: { id: 'go', feature: 'ghcr.io/devcontainers/features/go:1' },\n rust: { id: 'rust', feature: 'ghcr.io/devcontainers/features/rust:1' },\n dotnet: { id: 'dotnet', feature: 'ghcr.io/devcontainers/features/dotnet:2' },\n};\n\n/**\n * Language entries in a container yml may carry an optional\n * version suffix: `java:17`, `node:20`. The suffix is anything\n * the upstream devcontainer feature accepts as its `version`\n * option (typically `latest`, a major like `17`, or an exact\n * semver like `3.12.1`).\n */\nexport const LANGUAGE_SPEC_RE = /^([a-z][a-z0-9-]*)(?::([A-Za-z0-9._-]+))?$/;\n\nexport interface LanguageSpec {\n name: string;\n version?: string;\n}\n\n/**\n * Split a yml language entry into name + optional version. Returns\n * `null` when the input is not a valid language spec. Callers use\n * that null to surface a schema error.\n */\nexport function parseLanguageSpec(spec: string): LanguageSpec | null {\n const m = LANGUAGE_SPEC_RE.exec(spec);\n if (!m) return null;\n return { name: m[1]!, ...(m[2] !== undefined ? { version: m[2] } : {}) };\n}\n\nexport interface ServiceEntry {\n id: string;\n image: string;\n env?: Readonly<Record<string, string>>;\n /**\n * Container-side mount target for the service's persistent data.\n * Monoceros bind-mounts this onto `<container-dir>/data/<id>/` on\n * the host so DB content is visible in the host filesystem\n * (browsable, backupable, removable with the usual tools instead\n * of `docker volume ...`). See ADR 0003 for the per-container\n * state-model the data dir slots into.\n */\n dataMount?: string;\n}\n\n// The literal `monoceros` user/password/db on the service entries\n// below is a deliberate dev-only convention, not a secret. The\n// services are only reachable from inside the workspace container\n// (no host port mapping), and the value is hardcoded into the\n// catalog + docs so any builder running this workbench knows the\n// connection string at a glance:\n//\n// postgresql://monoceros:monoceros@postgres:5432/monoceros\n// mysql://monoceros:monoceros@mysql:3306/monoceros\n//\n// Because it isn't a secret, the secret-masking layer\n// (util/mask-secrets.ts) doesn't and shouldn't mask it. Builders\n// who want a real password should either:\n// - run their own DB outside the workbench and configure it via\n// `externalServices.postgres: postgresql://…` in the container\n// yml, OR\n// - swap to a per-container generated password — open issue when\n// this becomes a real need.\nexport const SERVICE_CATALOG: Readonly<Record<string, ServiceEntry>> = {\n postgres: {\n id: 'postgres',\n image: 'postgres:18',\n env: {\n POSTGRES_USER: 'monoceros',\n POSTGRES_PASSWORD: 'monoceros',\n POSTGRES_DB: 'monoceros',\n },\n // Postgres 18+ stores data under /var/lib/postgresql/<major>/, so\n // the recommended mount is the parent directory; pre-18 used\n // /var/lib/postgresql/data directly. See\n // https://github.com/docker-library/postgres/pull/1259.\n dataMount: '/var/lib/postgresql',\n },\n mysql: {\n id: 'mysql',\n image: 'mysql:8',\n env: {\n MYSQL_ROOT_PASSWORD: 'monoceros',\n MYSQL_DATABASE: 'monoceros',\n },\n dataMount: '/var/lib/mysql',\n },\n redis: {\n id: 'redis',\n image: 'redis:8',\n dataMount: '/data',\n },\n};\n\nexport function knownLanguages(): string[] {\n return [...BUILTIN_LANGUAGES, ...Object.keys(LANGUAGE_CATALOG)].sort();\n}\n\nexport function knownServices(): string[] {\n return Object.keys(SERVICE_CATALOG).sort();\n}\n","import { existsSync, readFileSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { workbenchCheckoutRoot } from '../config/paths.js';\nimport { matchMonocerosFeature } from '../util/ref.js';\nimport {\n BASE_IMAGE,\n BUILTIN_LANGUAGES,\n LANGUAGE_CATALOG,\n SERVICE_CATALOG,\n knownLanguages,\n knownServices,\n parseLanguageSpec,\n} from './catalog.js';\nimport type { CreateOptions } from './types.js';\n\n// Debian/Ubuntu apt package name rules: start with alphanumeric, then\n// alphanumerics + `.+-` are allowed. We intentionally don't allow shell\n// metacharacters (`;`, `&`, `|`, `$`, `(`, …) so a typo can't smuggle\n// arbitrary shell into the apt-packages feature config.\nconst APT_PACKAGE_NAME_RE = /^[a-z0-9][a-z0-9.+-]*$/;\n\n// Devcontainer feature refs are OCI-style:\n// <registry>/<namespace>/<feature>:<tag>\n// e.g. ghcr.io/devcontainers/features/python:1\n// ghcr.io/getmonoceros/monoceros-features/claude-code:1\nconst FEATURE_REF_RE = /^[a-z0-9.-]+(\\/[a-z0-9._-]+)+:[a-z0-9._-]+$/;\n\n// Install URLs must be https:// (no plain http, no other schemes) and\n// contain only URL-safe characters. We deliberately reject shell\n// metacharacters even inside a query string — the URL is embedded into\n// a generated bash script, and a stray `$` or backtick would be a\n// shell-injection vector.\nconst INSTALL_URL_RE = /^https:\\/\\/[A-Za-z0-9.\\-_~/:?#[\\]@!&'()*+,;=%]+$/;\n\n// Git URLs: covers HTTPS, SSH (`git@host:path/repo.git`), and\n// `ssh://`/`git://` schemes. Permissive but no shell metacharacters.\nconst REPO_URL_RE = /^[A-Za-z0-9@:/+_~.#=&?-]+$/;\n\n// Repo destination = path under `projects/`. Allows nested subfolders\n// (`apps/web`) via `/`; segments use `[A-Za-z0-9._-]` (same charset as\n// a leaf folder name). `.` / `..` segments are rejected separately\n// because the regex alone allows pure-dot segments.\nconst REPO_PATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\n\n/**\n * Derive a repo name from its URL.\n *\n * `git@github.com:foo/bar.git` → `bar`\n * `https://github.com/foo/bar.git` → `bar`\n * `https://github.com/foo/bar` → `bar`\n * `ssh://git@host:22/foo/bar.git` → `bar`\n */\nexport function deriveRepoName(url: string): string {\n const lastSep = Math.max(url.lastIndexOf('/'), url.lastIndexOf(':'));\n const tail = url.slice(lastSep + 1);\n return tail.replace(/\\.git$/, '');\n}\n\nexport function validateOptions(opts: CreateOptions): void {\n if (!opts.name || !/^[a-zA-Z0-9._-]+$/.test(opts.name)) {\n throw new Error(\n `Invalid solution name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n for (const langSpec of opts.languages) {\n const parsed = parseLanguageSpec(langSpec);\n if (!parsed) {\n throw new Error(\n `Invalid language spec: ${JSON.stringify(langSpec)}. Expected '<name>' or '<name>:<version>'.`,\n );\n }\n if (!BUILTIN_LANGUAGES.has(parsed.name) && !LANGUAGE_CATALOG[parsed.name]) {\n throw new Error(\n `Unknown language: ${parsed.name}. Known: ${knownLanguages().join(', ')}.`,\n );\n }\n }\n for (const svc of opts.services) {\n if (!SERVICE_CATALOG[svc]) {\n throw new Error(\n `Unknown service: ${svc}. Known: ${knownServices().join(', ')}.`,\n );\n }\n }\n for (const pkg of opts.aptPackages ?? []) {\n if (!APT_PACKAGE_NAME_RE.test(pkg)) {\n throw new Error(\n `Invalid apt package name: ${JSON.stringify(pkg)}. Expected lowercase alphanumeric plus '.+-'.`,\n );\n }\n }\n for (const ref of Object.keys(opts.features ?? {})) {\n if (!FEATURE_REF_RE.test(ref)) {\n throw new Error(\n `Invalid devcontainer feature ref: ${JSON.stringify(ref)}. Expected OCI-image-style ref like 'ghcr.io/devcontainers/features/<name>:<tag>'.`,\n );\n }\n }\n for (const url of opts.installUrls ?? []) {\n if (!INSTALL_URL_RE.test(url)) {\n throw new Error(\n `Invalid install URL: ${JSON.stringify(url)}. Must start with 'https://' and contain only URL-safe characters (no shell metacharacters).`,\n );\n }\n }\n const seenRepoPaths = new Set<string>();\n for (const repo of opts.repos ?? []) {\n if (!REPO_URL_RE.test(repo.url)) {\n throw new Error(\n `Invalid repo URL: ${JSON.stringify(repo.url)}. Use HTTPS or SSH/git@ form; no shell metacharacters.`,\n );\n }\n if (!REPO_PATH_RE.test(repo.path)) {\n throw new Error(\n `Invalid repo path: ${JSON.stringify(repo.path)}. Use letters/digits/'._-', forward slashes for nested folders, no leading or trailing slash.`,\n );\n }\n if (repo.path.split('/').some((seg) => seg === '..' || seg === '.')) {\n throw new Error(\n `Invalid repo path: ${JSON.stringify(repo.path)}. Path segments cannot be \".\" or \"..\".`,\n );\n }\n if (seenRepoPaths.has(repo.path)) {\n throw new Error(\n `Duplicate repo path: ${JSON.stringify(repo.path)}. Each projects/<path> folder must be unique — pass --path to disambiguate.`,\n );\n }\n seenRepoPaths.add(repo.path);\n }\n}\n\n// Normalize: dedupe + sort + drop postgres from compose services when an\n// external --postgres-url is provided.\nexport function normalizeOptions(opts: CreateOptions): CreateOptions {\n const languages = [...new Set(opts.languages)].sort();\n let services = [...new Set(opts.services)].sort();\n if (opts.postgresUrl) {\n services = services.filter((s) => s !== 'postgres');\n }\n const aptPackages = [...new Set(opts.aptPackages ?? [])].sort();\n // Sort feature refs alphabetically so devcontainer.json + stack.json\n // output is deterministic regardless of insertion order.\n const features = opts.features\n ? Object.fromEntries(\n Object.entries(opts.features).sort(([a], [b]) => a.localeCompare(b)),\n )\n : undefined;\n // Install URLs preserve insertion order (installs may depend on each\n // other), but we deduplicate to keep stack.json stable across re-adds.\n const installUrls = opts.installUrls\n ? [...new Set(opts.installUrls)]\n : undefined;\n // Repos: preserve insertion order, dedupe by (url, name, branch)\n // signature — same triple twice is a no-op, different triples\n // coexist. (Same name with different URL is a validation error\n // in validateOptions, not silently merged here.)\n const repos = opts.repos\n ? Array.from(\n new Map(opts.repos.map((r) => [`${r.url}\u0001${r.path}`, r])).values(),\n )\n : undefined;\n return {\n name: opts.name,\n languages,\n services,\n postgresUrl: opts.postgresUrl,\n ...(aptPackages.length > 0 ? { aptPackages } : {}),\n ...(features && Object.keys(features).length > 0 ? { features } : {}),\n ...(installUrls && installUrls.length > 0 ? { installUrls } : {}),\n ...(repos && repos.length > 0 ? { repos } : {}),\n };\n}\n\nexport function needsCompose(opts: CreateOptions): boolean {\n return opts.services.length > 0;\n}\n\ninterface DevcontainerImageMode {\n name: string;\n image: string;\n remoteUser: string;\n // Scaffold-level mounts: only the SSH-agent forward for git auth when\n // the yml lists repos. Tool-specific mounts (e.g. ~/.claude for the\n // claude-code feature) come from the feature's own manifest, not from\n // here.\n mounts?: string[];\n // Override of the workspace bind-mount. Set only when the host\n // runs rootless Docker — we append `idmap` so the kernel applies\n // the user-namespace mapping to the mount, which makes files\n // written by either side appear with sane UIDs on the other.\n // Without this, host-pre-created `projects/` appears as root in\n // the container and the non-root `node` user can't write into it.\n workspaceMount?: string;\n // Required so the runtime image's entrypoint can install iptables\n // rules if MONOCEROS_EGRESS=enforce is set. Default mode is `off`\n // (see ADR 0002) so the cap is harmless when unused.\n runArgs: string[];\n forwardPorts: number[];\n postCreateCommand: string;\n features?: Record<string, Record<string, unknown>>;\n // Env vars injected into the workspace container at start time\n // (inherited by postCreateCommand). Used by add-repo to wire the\n // forwarded SSH-agent socket and a permissive SSH host-key policy.\n containerEnv?: Record<string, string>;\n}\n\ninterface DevcontainerComposeMode {\n name: string;\n dockerComposeFile: string;\n service: string;\n // Without runServices, `devcontainer up` only brings up the named service.\n // Listing the auxiliary services here ensures postgres/redis/… come up\n // alongside the workspace container.\n runServices?: string[];\n workspaceFolder: string;\n remoteUser: string;\n forwardPorts: number[];\n postCreateCommand: string;\n features?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * The host docker daemon's mode — passed in by `apply` after a\n * `docker info` probe. Drives whether we emit `idmap` on bind\n * mounts. See `devcontainer/docker-mode.ts` for the rationale.\n */\nexport type DockerMode = 'rootful' | 'rootless';\n\n// Repo auth note: Monoceros supports HTTPS-only repo URLs (see ADR\n// 0006). The host's git credential helper provides the username/token\n// per host (osxkeychain on macOS, libsecret on Linux, wincred on\n// Windows, plus `gh auth setup-git` for GitHub specifically), the\n// apply pipeline writes them to <container-dir>/.monoceros/git-\n// credentials, and post-create.sh wires `git config --global\n// credential.helper \"store --file=…\"` so the container reads from\n// the same file. SSH-agent forwarding, multi-key wiring, host-OS\n// platform-specific socket paths — all that complexity stays out.\n\nexport type DevcontainerJson = DevcontainerImageMode | DevcontainerComposeMode;\n\n/**\n * Per-feature plan for the container build.\n *\n * - `devcontainerKey` — the key used in `devcontainer.json → features`.\n * - `localSourceDir` / `localName` — set when the workbench has the\n * feature on disk. `writeScaffold` copies the directory into\n * `<container>/.devcontainer/features/<name>/` and uses the\n * relative path `./features/<name>` in devcontainer.json.\n * (devcontainer-cli accepts relative paths from `.devcontainer/`\n * but rejects absolute filesystem paths to local features.)\n */\ninterface ResolvedFeature {\n devcontainerKey: string;\n options: Record<string, unknown>;\n localSourceDir?: string;\n localName?: string;\n /**\n * Subdirectories of `/home/node/` that this feature wants to\n * persist across container rebuilds. Each entry is bind-mounted\n * from `<container-dir>/home/<path>` into `/home/node/<path>` and\n * pre-created as an empty directory on the host. Read from the\n * feature manifest's `x-monoceros.persistentHomePaths` array.\n */\n persistentHomePaths: string[];\n /**\n * Like `persistentHomePaths`, but for individual **files** rather\n * than directories. Necessary for tools that keep state in a\n * dotfile next to (not inside) their config directory — e.g.\n * Claude Code's `~/.claude.json` lives alongside `~/.claude/`.\n *\n * Each entry can be a bare path string (file pre-created empty)\n * or `{ path, initialContent }` so a feature author can seed\n * valid initial content. The latter is needed for tools that\n * refuse to parse an empty file: Claude Code, for instance, errors\n * on an empty `.claude.json` and demands at least `{}`. Read from\n * the feature manifest's `x-monoceros.persistentHomeFiles` array.\n */\n persistentHomeFiles: PersistentHomeFile[];\n}\n\ninterface PersistentHomeFile {\n path: string;\n initialContent: string;\n}\n\n/**\n * Compute the feature list for `opts`. Detects Monoceros-owned refs\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`) and, if\n * the workbench has the feature on disk, rewrites the key to\n * `./features/<name>` and records the source for the copy step.\n *\n * Third-party refs and Monoceros refs without a local source pass\n * through verbatim — devcontainer-cli pulls them from the registry.\n */\nexport function resolveFeatures(opts: CreateOptions): ResolvedFeature[] {\n const resolved: ResolvedFeature[] = [];\n\n for (const langSpec of opts.languages) {\n const parsed = parseLanguageSpec(langSpec);\n if (!parsed) continue;\n // Builtin only applies to the bare `node` (no version) — the\n // base image's node 22 isn't pinnable, so any `node:<version>`\n // has to go through the upstream feature like everything else.\n if (BUILTIN_LANGUAGES.has(parsed.name) && parsed.version === undefined) {\n continue;\n }\n const entry = LANGUAGE_CATALOG[parsed.name];\n if (!entry) continue;\n const options: Record<string, string> = {};\n if (parsed.version !== undefined) options.version = parsed.version;\n resolved.push({\n devcontainerKey: entry.feature,\n options,\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n if (opts.aptPackages && opts.aptPackages.length > 0) {\n resolved.push({\n devcontainerKey: 'ghcr.io/devcontainers-contrib/features/apt-packages:1',\n options: { packages: opts.aptPackages.join(',') },\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n if (opts.features) {\n for (const [rawRef, options] of Object.entries(opts.features)) {\n const match = matchMonocerosFeature(rawRef);\n if (match) {\n const name = match.name;\n // Dev-only fallback: when the CLI is run from a workbench\n // checkout, prefer the on-disk copy under `images/features/`\n // so feature edits are testable without a publish. In prod\n // (npm-installed), `workbenchCheckoutRoot()` returns null\n // and we fall through to the GHCR-ref passthrough.\n const checkout = workbenchCheckoutRoot();\n const localSourceDir = checkout\n ? path.join(checkout, 'images', 'features', name)\n : null;\n if (localSourceDir && existsSync(localSourceDir)) {\n const { paths, files } = readPersistentHomeEntries(localSourceDir);\n resolved.push({\n devcontainerKey: `./features/${name}`,\n options,\n localSourceDir,\n localName: name,\n persistentHomePaths: paths,\n persistentHomeFiles: files,\n });\n continue;\n }\n }\n resolved.push({\n devcontainerKey: rawRef,\n options,\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n }\n return resolved;\n}\n\n/**\n * Read `x-monoceros.persistentHomePaths` and\n * `x-monoceros.persistentHomeFiles` from a feature's manifest on\n * disk. Returns `{paths: [], files: []}` when the manifest doesn't\n * exist, can't be parsed, or the fields are missing — always\n * best-effort, never throws. Both arrays are validated to contain\n * only safe relative subpaths (no `..`, no absolute, no shell\n * metacharacters); anything else is silently dropped, since a bad\n * value here is a feature-author bug, not something a builder can fix.\n */\nfunction readPersistentHomeEntries(localSourceDir: string): {\n paths: string[];\n files: PersistentHomeFile[];\n} {\n const manifestPath = path.join(localSourceDir, 'devcontainer-feature.json');\n try {\n const text = readFileSync(manifestPath, 'utf8');\n const parsed = JSON.parse(text) as {\n 'x-monoceros'?: {\n persistentHomePaths?: unknown;\n persistentHomeFiles?: unknown;\n };\n };\n return {\n paths: filterSubpaths(parsed['x-monoceros']?.persistentHomePaths),\n files: filterFileEntries(parsed['x-monoceros']?.persistentHomeFiles),\n };\n } catch {\n return { paths: [], files: [] };\n }\n}\n\nfunction filterSubpaths(raw: unknown): string[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter(\n (p): p is string =>\n typeof p === 'string' &&\n p.length > 0 &&\n !p.startsWith('/') &&\n !p.includes('..') &&\n HOME_SUBPATH_RE.test(p),\n );\n}\n\n/**\n * Accept either bare strings or `{path, initialContent}` objects in\n * `persistentHomeFiles`. Bare string is shorthand for \"create an\n * empty file\"; the object form lets feature authors provide initial\n * content (e.g. `{}` for a JSON config that the tool refuses to\n * parse when empty).\n */\nfunction filterFileEntries(raw: unknown): PersistentHomeFile[] {\n if (!Array.isArray(raw)) return [];\n const result: PersistentHomeFile[] = [];\n for (const entry of raw) {\n if (typeof entry === 'string') {\n if (isValidHomeSubpath(entry)) {\n result.push({ path: entry, initialContent: '' });\n }\n continue;\n }\n if (\n entry !== null &&\n typeof entry === 'object' &&\n 'path' in entry &&\n typeof (entry as { path: unknown }).path === 'string'\n ) {\n const e = entry as { path: string; initialContent?: unknown };\n if (!isValidHomeSubpath(e.path)) continue;\n const initialContent =\n typeof e.initialContent === 'string' ? e.initialContent : '';\n result.push({ path: e.path, initialContent });\n }\n }\n return result;\n}\n\nfunction isValidHomeSubpath(p: string): boolean {\n return (\n p.length > 0 &&\n !p.startsWith('/') &&\n !p.includes('..') &&\n HOME_SUBPATH_RE.test(p)\n );\n}\n\n// Home subpaths: dot-prefixed dirs and config-like sub-dirs.\n// Restrictive on purpose — only `.foo`, `.foo/bar`, `foo`, `foo/bar`,\n// no whitespace, no shell metacharacters.\nconst HOME_SUBPATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\n\nexport function buildDevcontainerJson(\n opts: CreateOptions,\n dockerMode: DockerMode = 'rootful',\n): DevcontainerJson {\n const resolvedFeatures = resolveFeatures(opts);\n const features: Record<string, Record<string, unknown>> = {};\n for (const f of resolvedFeatures) {\n features[f.devcontainerKey] = f.options;\n }\n\n const featuresField =\n Object.keys(features).length > 0 ? { features } : undefined;\n\n // `idmap` is the bind-mount option that asks the Linux kernel to\n // apply the user-namespace mapping to the mount. Required on\n // rootless Docker so that host-pre-created files (`projects/`,\n // `home/`, `.monoceros/`) are writable by the container's `node`\n // user and container-written files land on the host with the\n // host user's UID instead of a shifted /etc/subuid id. On rootful\n // Docker and on Mac/Windows Docker Desktop, idmap is either a\n // no-op (extra round-trip) or an outright mount error — so we\n // ONLY emit it when the host probe confirmed rootless.\n const idmapSuffix = dockerMode === 'rootless' ? ',idmap' : '';\n\n // Bind-mounts for per-feature persistent home entries. Source on\n // the host is `<container-dir>/home/<subpath>` (under the\n // localWorkspaceFolder); target inside the container is the same\n // subpath under `/home/node/`. Files and directories both go through\n // the same `type=bind` syntax — docker decides from the source's\n // on-disk type. We pre-create both kinds in writeScaffold so the\n // owner matches the host user (otherwise docker auto-creates as\n // root on Linux, breaking writes inside the container) and so a\n // requested **file** bind doesn't get spawned as a directory.\n const homeMounts: string[] = [];\n for (const f of resolvedFeatures) {\n const allSubs = [\n ...f.persistentHomePaths,\n ...f.persistentHomeFiles.map((entry) => entry.path),\n ];\n for (const sub of allSubs) {\n homeMounts.push(\n `source=\\${localWorkspaceFolder}/home/${sub},target=/home/node/${sub},type=bind${idmapSuffix}`,\n );\n }\n }\n\n // No scaffold-level VS Code customizations today — extension hints\n // belong with the feature that needs them (e.g. the claude-code\n // feature itself recommends `anthropic.claude-code`).\n\n if (needsCompose(opts)) {\n // Compose-mode: per-feature persistent home mounts go onto the\n // workspace service in compose.yaml (see buildComposeYaml). The\n // devcontainer.json just references compose.\n return {\n name: opts.name,\n dockerComposeFile: 'compose.yaml',\n service: 'workspace',\n ...(opts.services.length > 0 ? { runServices: opts.services } : {}),\n workspaceFolder: `/workspaces/${opts.name}`,\n remoteUser: 'node',\n forwardPorts: [3000, 4000],\n postCreateCommand: '.devcontainer/post-create.sh',\n ...(featuresField ?? {}),\n };\n }\n\n // Image-mode mounts: per-feature persistent-home binds.\n const mounts: string[] = [...homeMounts];\n const mountsField = mounts.length > 0 ? { mounts } : {};\n\n // On rootless we also override the workspace bind-mount so the\n // main /workspaces/<name> mount gets idmap. Without this override,\n // devcontainer-cli generates the workspace mount itself without\n // idmap, and the post-create.sh that runs inside hits permission-\n // denied on host-pre-created `projects/`.\n const workspaceMountField =\n dockerMode === 'rootless'\n ? {\n workspaceMount: `source=\\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind${idmapSuffix}`,\n }\n : {};\n\n return {\n name: opts.name,\n image: BASE_IMAGE,\n remoteUser: 'node',\n ...workspaceMountField,\n ...mountsField,\n runArgs: ['--cap-add=NET_ADMIN'],\n forwardPorts: [3000, 4000],\n postCreateCommand: '.devcontainer/post-create.sh',\n ...(featuresField ?? {}),\n };\n}\n\n// Hand-rolled YAML for compose.yaml. The shape is narrow enough that\n// avoiding a YAML dependency outweighs the cost of careful indentation.\n//\n// `dockerMode === 'rootless'` switches every workspace-side bind to\n// the long-syntax with `bind: { create_host_path: true }` plus the\n// `idmap` flag — same fix as in image mode (see buildDevcontainerJson),\n// just expressed in compose's volume long-form. Compose spec exposes\n// idmap as of compose-spec 1.16 (docker compose v2.30+, Ubuntu 24.04\n// has compatible versions). On rootful we keep the short syntax — it\n// stays readable and avoids any compose-version friction.\nexport function buildComposeYaml(\n opts: CreateOptions,\n dockerMode: DockerMode = 'rootful',\n): string {\n const lines: string[] = ['services:'];\n const isRootless = dockerMode === 'rootless';\n\n lines.push(' workspace:');\n lines.push(` image: ${BASE_IMAGE}`);\n lines.push(\" command: 'sleep infinity'\");\n // No `user:` directive here — the runtime image's entrypoint runs as\n // root to set up iptables, then drops to the `node` user via gosu\n // before exec'ing the command. NET_ADMIN is required for that\n // iptables setup; see ADR 0002.\n lines.push(' cap_add:');\n lines.push(' - NET_ADMIN');\n lines.push(' volumes:');\n if (isRootless) {\n // Long syntax + idmap so the kernel applies the user-ns mapping\n // to the workspace bind. Without it, the container's `node` can't\n // write into host-pre-created `projects/`.\n lines.push(' - type: bind');\n lines.push(' source: ..');\n lines.push(` target: /workspaces/${opts.name}`);\n lines.push(' bind:');\n lines.push(' create_host_path: true');\n lines.push(' idmap: true');\n } else {\n lines.push(` - ..:/workspaces/${opts.name}:cached`);\n }\n // Per-feature persistent home subpaths (dirs and files alike).\n // Paths inside compose.yaml are relative to the .devcontainer/\n // directory; `..` walks up to the container root, where `home/`\n // lives. Docker reads the host-side inode type to decide whether\n // the mount target inside the container is a file or a directory.\n const resolvedFeatures = resolveFeatures(opts);\n for (const f of resolvedFeatures) {\n const allSubs = [\n ...f.persistentHomePaths,\n ...f.persistentHomeFiles.map((entry) => entry.path),\n ];\n for (const sub of allSubs) {\n if (isRootless) {\n lines.push(' - type: bind');\n lines.push(` source: ../home/${sub}`);\n lines.push(` target: /home/node/${sub}`);\n lines.push(' bind:');\n lines.push(' create_host_path: true');\n lines.push(' idmap: true');\n } else {\n lines.push(` - ../home/${sub}:/home/node/${sub}`);\n }\n }\n }\n for (const svcId of opts.services) {\n const def = SERVICE_CATALOG[svcId];\n if (!def) continue;\n lines.push(` ${def.id}:`);\n lines.push(` image: ${def.image}`);\n if (def.env) {\n lines.push(' environment:');\n for (const [k, v] of Object.entries(def.env)) {\n lines.push(` ${k}: ${v}`);\n }\n }\n if (def.dataMount) {\n // Per-service data dir bind-mounted from the host so DB content\n // is visible at `<container-dir>/data/<svc>/`. See ADR 0003 for\n // the per-container state-model this slots into. Pre-created in\n // writeScaffold so docker doesn't auto-mkdir as root.\n lines.push(' volumes:');\n lines.push(` - ../data/${def.id}:${def.dataMount}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\ninterface CodeWorkspaceFolder {\n path: string;\n name?: string;\n}\n\ninterface CodeWorkspaceFile {\n folders: CodeWorkspaceFolder[];\n}\n\n/**\n * The `<name>.code-workspace` file VS Code uses to open the solution as\n * a multi-root workspace. The first entry is `.` so the workspace root\n * (with its system dotfolders) stays visible in the Explorer. Each\n * repo added via `monoceros add-repo` appears as a sibling root\n * pointing at `projects/<name>/`.\n */\nexport function buildCodeWorkspaceJson(opts: CreateOptions): CodeWorkspaceFile {\n const folders: CodeWorkspaceFolder[] = [{ path: '.' }];\n // Sort repos by path so the Explorer order is deterministic and\n // doesn't depend on insertion order. (Clone order in post-create\n // stays as-added so deps still work.)\n const sortedRepos = [...(opts.repos ?? [])].sort((a, b) =>\n a.path.localeCompare(b.path),\n );\n for (const repo of sortedRepos) {\n // The folder's display label is the leaf segment of the path\n // (the deepest folder name). VS Code shows it in the Explorer\n // tree; for nested clones (`apps/web`) we want `web`, not the\n // whole path.\n const label = repo.path.split('/').pop() ?? repo.path;\n folders.push({ path: `projects/${repo.path}`, name: label });\n }\n return { folders };\n}\n\n/**\n * Generate the `post-create.sh` content for a solution. Base sections\n * (git include + pnpm install) are fixed. The `installUrls` and\n * `repos` sections are appended only when those yml fields are\n * populated.\n */\nexport function buildPostCreateScript(opts: CreateOptions): string {\n const lines: string[] = [\n '#!/usr/bin/env bash',\n 'set -euo pipefail',\n '',\n '# Inherit host-side git identity (user.name / user.email) captured',\n '# into .monoceros/gitconfig by `monoceros apply`. Container-local',\n \"# git config loads first; the include below merges the host's\",\n '# identity values in.',\n `git config --global include.path \"/workspaces/${opts.name}/.monoceros/gitconfig\"`,\n '',\n '# Per-feature post-create hooks. Each Monoceros-curated feature',\n '# may drop a script into /usr/local/share/monoceros/post-create.d/',\n '# during its install.sh — typical job is a non-interactive login',\n '# against bind-mounted state under /home/node, using the option',\n '# values the feature received as env vars at install time. Scripts',\n '# run in lexicographic order, each in its own subshell, and a',\n '# failure aborts post-create (set -e is in effect).',\n 'if [ -d /usr/local/share/monoceros/post-create.d ]; then',\n ' for hook in /usr/local/share/monoceros/post-create.d/*.sh; do',\n ' [ -f \"$hook\" ] || continue',\n ' echo \"→ post-create hook: $(basename \"$hook\")\"',\n ' bash \"$hook\"',\n ' done',\n 'fi',\n '',\n '# Bring up Node dependencies if the workspace has a package.json.',\n 'if [ -f package.json ]; then',\n ' pnpm install',\n 'fi',\n ];\n\n if (opts.installUrls && opts.installUrls.length > 0) {\n lines.push(\n '',\n '# Custom install URLs added via `monoceros add-from-url`. Each is',\n '# fetched and piped to `sh` on every container rebuild. URLs run',\n '# in insertion order so later installs can build on earlier ones.',\n '#',\n '# Why `sh` (not `bash`): most install scripts target POSIX `sh`',\n '# and some (starship, rustup, …) explicitly refuse to run under',\n '# `bash`. Outer `set -o pipefail` in this script makes a curl',\n '# failure abort the post-create as expected.',\n `echo \"→ Running ${opts.installUrls.length} install URL(s) added via add-from-url…\"`,\n );\n for (const url of opts.installUrls) {\n lines.push(`echo \"→ ${url}\"`, `curl -fsSL \"${url}\" | sh`);\n }\n }\n\n if (opts.repos && opts.repos.length > 0) {\n const hasHttpsRepo = opts.repos.some((r) => r.url.startsWith('https://'));\n if (hasHttpsRepo) {\n lines.push(\n '',\n '# Wire git to the per-dev-container credentials file populated',\n '# by `monoceros apply` (via `git credential fill` on the host).',\n '# Path uses the workspace bind-mount so the file is reachable',\n '# from inside the container.',\n `git config --global credential.helper \"store --file=/workspaces/${opts.name}/.monoceros/git-credentials\"`,\n );\n }\n lines.push(\n '',\n '# Repos managed by `monoceros add-repo`. Each entry is cloned',\n '# into `projects/<path>/` if (and only if) the directory does',\n '# not exist yet. Existing project subfolders are left alone so',\n '# local changes survive `monoceros apply` rebuilds. Nested',\n '# `<path>` (e.g. apps/web) is created via `mkdir -p` before the',\n '# clone so the parent directories exist.',\n 'mkdir -p projects',\n );\n for (const repo of opts.repos) {\n // For nested paths (`apps/web`), make sure the parent dir\n // exists before git clone — otherwise git fails with \"could\n // not create work tree dir\".\n const parent = repo.path.includes('/')\n ? repo.path.slice(0, repo.path.lastIndexOf('/'))\n : null;\n if (parent) {\n lines.push(`mkdir -p \"projects/${parent}\"`);\n }\n lines.push(\n `if [ ! -d \"projects/${repo.path}\" ]; then`,\n ` echo \"→ Cloning ${repo.path} from ${repo.url}…\"`,\n ` git clone \"${repo.url}\" \"projects/${repo.path}\"`,\n `else`,\n ` echo \"→ projects/${repo.path} already exists, skipping clone\"`,\n `fi`,\n );\n // Per-repo git identity override: set user.name/email inside\n // the cloned repo, so commits from THIS repo go out under the\n // override identity. Idempotent — git config overwrites the\n // value each run, no duplicate accumulation. Falls outside the\n // `if [ ! -d ... ]` clone-guard so an explicit yml update of\n // gitUser also takes effect on re-apply against an existing\n // clone.\n if (repo.gitUser) {\n const safeName = repo.gitUser.name.replace(/\"/g, '\\\\\"');\n const safeEmail = repo.gitUser.email.replace(/\"/g, '\\\\\"');\n lines.push(\n `git -C \"projects/${repo.path}\" config user.name \"${safeName}\"`,\n `git -C \"projects/${repo.path}\" config user.email \"${safeEmail}\"`,\n );\n }\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\nexport async function writePostCreateScript(\n devcontainerDir: string,\n opts: CreateOptions,\n): Promise<void> {\n const dest = path.join(devcontainerDir, 'post-create.sh');\n await fs.writeFile(dest, buildPostCreateScript(opts));\n await fs.chmod(dest, 0o755);\n}\n\n/**\n * Materialize the full devcontainer scaffold for `opts` into\n * `targetDir`. Idempotent overwrite — re-running with different opts\n * produces the new scaffold and overwrites any older files.\n *\n * Writes:\n * - `.devcontainer/devcontainer.json`\n * - `.devcontainer/post-create.sh`\n * - `.devcontainer/compose.yaml` (only when services are configured)\n * - `.monoceros/.gitignore`\n * - `projects/.gitkeep`\n * - `<name>.code-workspace`\n *\n * Does NOT write `README.md` — the README is a once-only stub that\n * `runCreate` produces but `runApplyFromYml` should leave alone (the\n * builder may have edited it).\n *\n * Caller is responsible for `validateOptions(opts)` and\n * `normalizeOptions(opts)`; this function trusts the input.\n */\nexport async function writeScaffold(\n opts: CreateOptions,\n targetDir: string,\n scaffoldOpts: { dockerMode?: DockerMode } = {},\n): Promise<void> {\n const dockerMode: DockerMode = scaffoldOpts.dockerMode ?? 'rootful';\n const devcontainerDir = path.join(targetDir, '.devcontainer');\n const monocerosDir = path.join(targetDir, '.monoceros');\n const projectsDir = path.join(targetDir, 'projects');\n const homeDir = path.join(targetDir, 'home');\n const dataDir = path.join(targetDir, 'data');\n await fs.mkdir(devcontainerDir, { recursive: true });\n await fs.mkdir(monocerosDir, { recursive: true });\n await fs.mkdir(projectsDir, { recursive: true });\n await fs.mkdir(homeDir, { recursive: true });\n if (needsCompose(opts)) {\n await fs.mkdir(dataDir, { recursive: true });\n // Pre-create one subdir per service so docker bind-mounts onto\n // an existing host path (and doesn't auto-mkdir as root, which\n // breaks postgres/mysql first-run on Linux).\n for (const svcId of opts.services) {\n const def = SERVICE_CATALOG[svcId];\n if (def?.dataMount) {\n await fs.mkdir(path.join(dataDir, def.id), { recursive: true });\n }\n }\n }\n\n // Container-root `.gitignore`. Excludes the directories that hold\n // builder-private or container-runtime state:\n // - `home/` — logins, sessions, secrets baked into tool\n // config files\n // - `.monoceros/` — git-credentials captured from the host\n // credential helper, machine-local gitconfig\n // - `data/` — DB data the compose services write at\n // runtime (postgres/mysql/redis), often big,\n // always container-specific\n // Inside `projects/<repo>/` builders have their own `.git` and\n // any wrapping git operation should be at that level, not at the\n // container root — but a stray `git init` at the root is exactly\n // the accident this .gitignore protects against.\n const containerGitignore = path.join(targetDir, '.gitignore');\n await fs.writeFile(containerGitignore, '/home/\\n/.monoceros/\\n/data/\\n');\n\n // `.gitkeep` so `projects/` survives a fresh git clone before any\n // sub-project has been added.\n const gitkeep = path.join(projectsDir, '.gitkeep');\n if (!existsSync(gitkeep)) {\n await fs.writeFile(gitkeep, '');\n }\n\n // `.monoceros/.gitignore` keeps per-builder runtime state out of any\n // wrapping git repo. Always overwrite — content is fixed.\n await fs.writeFile(\n path.join(monocerosDir, '.gitignore'),\n 'git-credentials*\\ngitconfig\\n',\n );\n\n const devcontainerJson = buildDevcontainerJson(opts, dockerMode);\n await fs.writeFile(\n path.join(devcontainerDir, 'devcontainer.json'),\n JSON.stringify(devcontainerJson, null, 2) + '\\n',\n );\n\n // Copy any Monoceros-owned features that the workbench has on disk\n // into `<devcontainerDir>/features/<name>/`. The devcontainer.json\n // references them via the relative path `./features/<name>` — the\n // devcontainer-cli accepts relative paths from the `.devcontainer/`\n // directory but rejects absolute filesystem paths to local features.\n //\n // We always rebuild the whole `features/` directory: drop the old\n // copy and recreate from current sources, so a feature that was\n // removed from the yml doesn't linger as stale on-disk content that\n // devcontainer-cli would still see.\n const featuresDir = path.join(devcontainerDir, 'features');\n if (existsSync(featuresDir)) {\n await fs.rm(featuresDir, { recursive: true, force: true });\n }\n const resolvedFeatures = resolveFeatures(opts);\n for (const f of resolvedFeatures) {\n if (!f.localSourceDir || !f.localName) continue;\n const dest = path.join(featuresDir, f.localName);\n await fs.mkdir(dest, { recursive: true });\n await fs.cp(f.localSourceDir, dest, { recursive: true });\n }\n\n // Pre-create persistent home entries so docker doesn't auto-mkdir\n // them as root at container start. We only ensure existence; any\n // existing content survives, which is the whole point — apply\n // never touches `home/<sub>` once it's there. Directories get\n // mkdir; files get an empty touch (only when missing — already-\n // populated files like a complete .claude.json must not be\n // truncated on re-apply).\n for (const f of resolvedFeatures) {\n for (const sub of f.persistentHomePaths) {\n await fs.mkdir(path.join(homeDir, sub), { recursive: true });\n }\n for (const entry of f.persistentHomeFiles) {\n const filePath = path.join(homeDir, entry.path);\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n if (!existsSync(filePath)) {\n // Seed with the feature-author's initial content (defaults\n // to empty). For JSON configs this should be at least `{}`\n // so the tool doesn't choke on an unparseable empty file.\n await fs.writeFile(filePath, entry.initialContent);\n }\n }\n }\n\n await writePostCreateScript(devcontainerDir, opts);\n\n const composePath = path.join(devcontainerDir, 'compose.yaml');\n if (needsCompose(opts)) {\n await fs.writeFile(composePath, buildComposeYaml(opts, dockerMode));\n } else if (existsSync(composePath)) {\n // Services dropped from the yml — clean up the now-stale file so a\n // later `monoceros start` doesn't pick it up.\n await fs.rm(composePath);\n }\n\n await fs.writeFile(\n path.join(targetDir, `${opts.name}.code-workspace`),\n JSON.stringify(buildCodeWorkspaceJson(opts), null, 2) + '\\n',\n );\n}\n","/**\n * Shape detection for OCI refs that point at Monoceros-owned\n * devcontainer features. Two regexes live here:\n *\n * - `MONOCEROS_FEATURE_RE` matches the current ref shape\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`),\n * which is what `monoceros init` writes and what GHCR serves.\n *\n * - `DEPRECATED_MONOCEROS_FEATURE_RE` matches the legacy shape\n * (`ghcr.io/monoceros/features/<name>:<tag>`) that was used\n * before the M4 cut. We keep this only to emit a migration\n * warning at `apply` time when a builder's yml still carries\n * the old ref.\n */\n\nconst FEATURE_NAME_CHARSET = '[a-z0-9._-]+';\nconst FEATURE_TAG_CHARSET = '[a-z0-9._-]+';\n\nexport const MONOCEROS_FEATURE_RE = new RegExp(\n `^ghcr\\\\.io/getmonoceros/monoceros-features/(${FEATURE_NAME_CHARSET}):${FEATURE_TAG_CHARSET}$`,\n);\n\nexport const DEPRECATED_MONOCEROS_FEATURE_RE = new RegExp(\n `^ghcr\\\\.io/monoceros/features/(${FEATURE_NAME_CHARSET}):(${FEATURE_TAG_CHARSET})$`,\n);\n\n/**\n * Extract `{ name }` from a current-shape Monoceros feature ref,\n * or `null` for anything else (third-party features, deprecated\n * shape, malformed input).\n */\nexport function matchMonocerosFeature(ref: string): { name: string } | null {\n const match = MONOCEROS_FEATURE_RE.exec(ref);\n if (!match) return null;\n return { name: match[1]! };\n}\n\n/**\n * Translate a legacy-shape ref into the current-shape ref it would\n * map to (`ghcr.io/monoceros/features/<name>:<tag>` →\n * `ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`). Returns\n * `null` for refs that don't match the legacy shape, so callers can\n * use it as a \"should I warn?\" check.\n */\nexport function migrateDeprecatedFeatureRef(ref: string): string | null {\n const match = DEPRECATED_MONOCEROS_FEATURE_RE.exec(ref);\n if (!match) return null;\n const name = match[1]!;\n const tag = match[2]!;\n return `ghcr.io/getmonoceros/monoceros-features/${name}:${tag}`;\n}\n","import { type Document, isMap, isScalar, isSeq, YAMLMap, YAMLSeq } from 'yaml';\nimport type { FeatureOptions, RepoEntry } from '../create/types.js';\nimport { deriveRepoName } from '../create/scaffold.js';\n\n/**\n * AST-level mutators for solution-config yml. Each function:\n * - takes a yaml.Document obtained from `parseConfig`\n * - mutates it in place if the operation introduces a change\n * - returns `true` iff the doc was changed, `false` for a no-op\n * (matches the `mutate()` skeleton's \"no-change\" branch)\n *\n * All mutators preserve every comment and blank line in the surrounding\n * yml — that's the whole point of the AST approach over a plain\n * `toJS → edit → toString` cycle.\n *\n * Validation is NOT done here. The caller is expected to re-validate\n * the doc (via `parseConfig(stringifyConfig(doc))`) before persisting\n * — that surfaces schema violations with the regular field-path-aware\n * error message.\n */\n\n/** Ensure `doc[key]` is a sequence and return it. */\nfunction ensureSeq(doc: Document, key: string): YAMLSeq {\n const existing = doc.get(key, true);\n if (existing && isSeq(existing)) return existing;\n const seq = new YAMLSeq();\n doc.set(key, seq);\n return seq;\n}\n\n/** Drop `doc[key]` when its sequence is empty. */\nfunction pruneEmptySeq(doc: Document, key: string): void {\n const node = doc.get(key, true);\n if (node && isSeq(node) && node.items.length === 0) {\n doc.delete(key);\n }\n}\n\n/** Compare a scalar item's value (handles both Scalar nodes and plain JS). */\nfunction scalarValue(item: unknown): unknown {\n return isScalar(item) ? item.value : item;\n}\n\nexport function addLanguageToDoc(doc: Document, lang: string): boolean {\n const seq = ensureSeq(doc, 'languages');\n if (seq.items.some((i) => scalarValue(i) === lang)) return false;\n seq.add(lang);\n return true;\n}\n\nexport function addServiceToDoc(doc: Document, service: string): boolean {\n const seq = ensureSeq(doc, 'services');\n if (seq.items.some((i) => scalarValue(i) === service)) return false;\n seq.add(service);\n return true;\n}\n\nexport function addAptPackagesToDoc(\n doc: Document,\n packages: string[],\n): boolean {\n const seq = ensureSeq(doc, 'aptPackages');\n let changed = false;\n for (const pkg of packages) {\n if (seq.items.some((i) => scalarValue(i) === pkg)) continue;\n seq.add(pkg);\n changed = true;\n }\n return changed;\n}\n\nexport function addInstallUrlToDoc(doc: Document, url: string): boolean {\n const seq = ensureSeq(doc, 'installUrls');\n if (seq.items.some((i) => scalarValue(i) === url)) return false;\n seq.add(url);\n return true;\n}\n\n/**\n * Add (or no-op) a devcontainer feature entry. Mirrors the legacy\n * `add-feature` semantics: re-adding the same ref with different\n * options is an explicit error (the builder must remove + re-add to\n * change options); same ref + same options is a no-op.\n */\nexport function addFeatureToDoc(\n doc: Document,\n ref: string,\n options: FeatureOptions = {},\n): boolean {\n const seq = ensureSeq(doc, 'features');\n for (const item of seq.items) {\n if (!isMap(item)) continue;\n const itemRef = item.get('ref');\n if (itemRef !== ref) continue;\n // Same ref: check options equality. Use the live doc's toJS so the\n // sub-map's scalars resolve to plain values; passing doc as the\n // schema/context is required by yaml@2.\n const itemJs = item.toJS(doc) as { options?: FeatureOptions };\n const existingJs = itemJs.options ?? {};\n if (JSON.stringify(existingJs) === JSON.stringify(options)) {\n return false;\n }\n throw new Error(\n `Feature ${ref} is already configured with different options. Remove it first (\\`monoceros remove-feature ${ref}\\`) before re-adding.`,\n );\n }\n const entry = new YAMLMap();\n entry.set('ref', ref);\n if (Object.keys(options).length > 0) {\n entry.set('options', options);\n }\n seq.add(entry);\n return true;\n}\n\n/**\n * Add (or no-op) a repo entry to the `repos:` sequence.\n *\n * Idempotency: if an existing entry has the same URL AND the same\n * effective path AND the same gitUser, this is a no-op (returns\n * false). \"Effective path\" means the explicit `path:` value if set,\n * or the URL-derived single-segment default otherwise. Same URL\n * with a different path is intentionally allowed — that's the \"I\n * want two clones of the same repo into different folders\" case.\n *\n * `gitUser` is an optional per-repo override of the container-level\n * git.user. When set, persisted as a `git.user` nested map; falls\n * back to the container default at apply time when omitted.\n *\n * Branches are not part of this model. Switching branches is a\n * `git checkout` inside the container, not a yml-level concern.\n */\nexport function addRepoToDoc(doc: Document, repo: RepoEntry): boolean {\n const seq = ensureSeq(doc, 'repos');\n for (const item of seq.items) {\n if (!isMap(item)) continue;\n const url = item.get('url');\n if (url !== repo.url) continue;\n const existingPath = item.get('path');\n const effectivePath =\n typeof existingPath === 'string'\n ? existingPath\n : deriveRepoName(url as string);\n if (effectivePath !== repo.path) continue;\n // Same url + same path. Check gitUser + provider equivalence too\n // so an entry that adds/changes either field is treated as an\n // update, not silently ignored.\n const existingGit = item.get('git', true);\n const existingUser =\n existingGit && isMap(existingGit) ? existingGit.get('user', true) : null;\n const existingName =\n existingUser && isMap(existingUser) ? existingUser.get('name') : null;\n const existingEmail =\n existingUser && isMap(existingUser) ? existingUser.get('email') : null;\n const existingGitUser =\n typeof existingName === 'string' && typeof existingEmail === 'string'\n ? { name: existingName, email: existingEmail }\n : undefined;\n const sameGitUser =\n (existingGitUser?.name ?? null) === (repo.gitUser?.name ?? null) &&\n (existingGitUser?.email ?? null) === (repo.gitUser?.email ?? null);\n const existingProvider = item.get('provider');\n const sameProvider =\n (typeof existingProvider === 'string' ? existingProvider : null) ===\n (repo.provider ?? null);\n if (sameGitUser && sameProvider) {\n return false;\n }\n // Different gitUser or provider → update in place instead of\n // appending a duplicate. Re-running add-repo with new values is\n // the natural way to change either field.\n if (repo.gitUser) {\n const gitMap = new YAMLMap();\n const userMap = new YAMLMap();\n userMap.set('name', repo.gitUser.name);\n userMap.set('email', repo.gitUser.email);\n gitMap.set('user', userMap);\n item.set('git', gitMap);\n } else {\n item.delete('git');\n }\n if (repo.provider) {\n item.set('provider', repo.provider);\n } else {\n item.delete('provider');\n }\n return true;\n }\n const entry = new YAMLMap();\n entry.set('url', repo.url);\n // Only persist `path` when it differs from the URL-derived default.\n // Keeps the yml minimal — the apply pipeline re-derives at runtime.\n if (repo.path !== deriveRepoName(repo.url)) {\n entry.set('path', repo.path);\n }\n if (repo.gitUser) {\n const gitMap = new YAMLMap();\n const userMap = new YAMLMap();\n userMap.set('name', repo.gitUser.name);\n userMap.set('email', repo.gitUser.email);\n gitMap.set('user', userMap);\n entry.set('git', gitMap);\n }\n if (repo.provider) {\n entry.set('provider', repo.provider);\n }\n seq.add(entry);\n return true;\n}\n\n/**\n * Remove helpers — symmetric to add, used by Task 6's `remove-*`\n * commands. Returning false when the target isn't present makes them\n * idempotent (caller logs \"no-change\" instead of erroring).\n */\nexport function removeLanguageFromDoc(doc: Document, lang: string): boolean {\n return removeScalarFromSeq(doc, 'languages', lang);\n}\n\nexport function removeServiceFromDoc(doc: Document, service: string): boolean {\n return removeScalarFromSeq(doc, 'services', service);\n}\n\nexport function removeAptPackageFromDoc(doc: Document, pkg: string): boolean {\n return removeScalarFromSeq(doc, 'aptPackages', pkg);\n}\n\n/** Plural form — for `monoceros remove-apt-packages a b c`. */\nexport function removeAptPackagesFromDoc(\n doc: Document,\n packages: string[],\n): boolean {\n let changed = false;\n for (const pkg of packages) {\n if (removeAptPackageFromDoc(doc, pkg)) changed = true;\n }\n return changed;\n}\n\nexport function removeInstallUrlFromDoc(doc: Document, url: string): boolean {\n return removeScalarFromSeq(doc, 'installUrls', url);\n}\n\nexport function removeFeatureFromDoc(doc: Document, ref: string): boolean {\n const seq = doc.get('features', true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((i) => isMap(i) && i.get('ref') === ref);\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, 'features');\n return true;\n}\n\n/**\n * Remove a repo by either its url or its (effective) path. Symmetry\n * to add-repo: `monoceros remove-repo <url-or-path>` matches either\n * field. For nested paths the full path is the match key\n * (`remove-repo apps/web`), not the leaf segment.\n */\nexport function removeRepoFromDoc(doc: Document, urlOrPath: string): boolean {\n const seq = doc.get('repos', true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((item) => {\n if (!isMap(item)) return false;\n const url = item.get('url');\n if (url === urlOrPath) return true;\n const path = item.get('path');\n const effectivePath =\n typeof path === 'string'\n ? path\n : typeof url === 'string'\n ? deriveRepoName(url)\n : undefined;\n return effectivePath === urlOrPath;\n });\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, 'repos');\n return true;\n}\n\nfunction removeScalarFromSeq(\n doc: Document,\n key: string,\n value: string,\n): boolean {\n const seq = doc.get(key, true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((i) => scalarValue(i) === value);\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, key);\n return true;\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport type { FeatureOptions } from '../create/types.js';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runAddFeature } from '../modify/index.js';\n\nexport const addFeatureCommand = defineCommand({\n meta: {\n name: 'add-feature',\n group: 'edit',\n description:\n 'Add a devcontainer feature by ref to the container config. Options follow `--` as `key=value` pairs. Idempotent (same ref + same options is a no-op). Adding the same ref with different options is an error.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n ref: {\n type: 'positional',\n description:\n 'Devcontainer feature ref (OCI image style, e.g. `ghcr.io/devcontainers/features/docker-in-docker:2`).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n let options: FeatureOptions;\n try {\n options = parseOptionsAfterDashes(getInnerArgs());\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n try {\n const result = await runAddFeature({\n name: args.name,\n ref: args.ref,\n options,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\n/**\n * Parse `key=value` tokens (one per arg) into a feature options hash.\n * Coerces `true`/`false` to booleans and pure-integer strings to\n * numbers; everything else stays a string.\n */\nfunction parseOptionsAfterDashes(tokens: readonly string[]): FeatureOptions {\n const result: FeatureOptions = {};\n for (const token of tokens) {\n const eqIdx = token.indexOf('=');\n if (eqIdx <= 0) {\n throw new Error(\n `Invalid option: ${JSON.stringify(token)}. Expected key=value (e.g. version=latest).`,\n );\n }\n const key = token.slice(0, eqIdx);\n const raw = token.slice(eqIdx + 1);\n result[key] = coerce(raw);\n }\n return result;\n}\n\nfunction coerce(value: string): string | number | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (/^-?\\d+$/.test(value)) {\n const n = Number(value);\n if (Number.isSafeInteger(n)) return n;\n }\n return value;\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddFromUrl } from '../modify/index.js';\n\nexport const addFromUrlCommand = defineCommand({\n meta: {\n name: 'add-from-url',\n group: 'edit',\n description:\n 'Add an https:// install URL to the container config. The URL gets piped to sh on every container rebuild. Loudly warns about remote-code execution before persisting. Idempotent.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description:\n 'https:// URL of an install script (e.g. https://starship.rs/install.sh).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description:\n 'Skip the security warning + diff confirm. Use only in scripts where you have already audited the URL.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n if (!args.yes) {\n printSecurityWarning(args.url);\n }\n try {\n const result = await runAddFromUrl({\n name: args.name,\n url: args.url,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\nfunction printSecurityWarning(url: string): void {\n const w = (line: string) => process.stderr.write(line + '\\n');\n w('');\n w('⚠️ SECURITY WARNING — `monoceros add-from-url`');\n w('');\n w(` URL: ${url}`);\n w('');\n w(' This URL will be fetched and piped to sh on every container rebuild.');\n w(\n ' Remote-code execution against a URL you do not control is a supply-chain',\n );\n w(\n ' risk: the maintainer could change the script tomorrow and your container',\n );\n w(' would silently run the new payload.');\n w('');\n w(' Before confirming below:');\n w(' 1. Open the URL in a browser, read what the script does.');\n w(\n ' 2. Verify the maintainer is who you think they are (HTTPS cert, repo).',\n );\n w(' 3. Ideally, vendor the install steps as `add-apt-packages` or');\n w(\n ' `add-feature` instead — those reference signed/versioned artifacts.',\n );\n w('');\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddRepo } from '../modify/index.js';\n\nexport const addRepoCommand = defineCommand({\n meta: {\n name: 'add-repo',\n group: 'edit',\n description:\n 'Add a git repo to the container config. Cloned into projects/<path>/ on container build. Idempotent — existing project subfolders are left alone. Destination path derived from URL by default; override with --path (supports nested subfolders like apps/web). Branches/PRs are git-level concerns: clone, then `git checkout` inside the container.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description:\n 'Git URL (HTTPS or SSH/git@ form). E.g. https://github.com/foo/bar.git, git@github.com:foo/bar.git.',\n required: true,\n },\n path: {\n type: 'string',\n description:\n 'Destination under projects/. Subfolders via `/` (e.g. apps/web). Default: URL-derived single segment (bar.git → bar).',\n },\n 'git-name': {\n type: 'string',\n description:\n 'Per-repo git committer name. Overrides the container-level git.user.name for this repo only. Pair with --git-email.',\n },\n 'git-email': {\n type: 'string',\n description:\n 'Per-repo git committer email. Overrides the container-level git.user.email for this repo only. Pair with --git-name.',\n },\n provider: {\n type: 'string',\n description:\n 'Git provider for credential-helper guidance: github | gitlab | bitbucket. Required when the URL host is not github.com, gitlab.com, or bitbucket.org — Monoceros uses this to suggest the right CLI (gh / glab / Atlassian token) on missing credentials.',\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddRepo({\n name: args.name,\n url: args.url,\n ...(typeof args.path === 'string' ? { path: args.path } : {}),\n ...(typeof args['git-name'] === 'string'\n ? { gitName: args['git-name'] }\n : {}),\n ...(typeof args['git-email'] === 'string'\n ? { gitEmail: args['git-email'] }\n : {}),\n ...(typeof args.provider === 'string'\n ? { provider: args.provider }\n : {}),\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddLanguage } from '../modify/index.js';\n\nexport const addLanguageCommand = defineCommand({\n meta: {\n name: 'add-language',\n group: 'edit',\n description:\n 'Add a language toolchain (devcontainer feature) to the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n language: {\n type: 'positional',\n description:\n 'Language identifier from the feature whitelist (e.g. python, java, rust).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddLanguage({\n name: args.name,\n language: args.language,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddService } from '../modify/index.js';\n\nexport const addServiceCommand = defineCommand({\n meta: {\n name: 'add-service',\n group: 'edit',\n description:\n 'Add a compose service (postgres, mysql, redis, …) to the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'positional',\n description: 'Service identifier (postgres, mysql, redis).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddService({\n name: args.name,\n service: args.service,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { runApply } from '../apply/index.js';\nimport { CLI_VERSION } from '../version.js';\nimport { dispatch } from './_dispatch.js';\n\n/**\n * `monoceros apply <name>` — materialize the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml` into\n * `<MONOCEROS_HOME>/container/<name>/` and bring the container up.\n *\n * The target location is fixed by convention. cwd is irrelevant. No\n * `--path` override — one config maps to exactly one container\n * directory, and that's the whole mental model.\n */\nexport const applyCommand = defineCommand({\n meta: {\n name: 'apply',\n group: 'lifecycle',\n description:\n 'Materialize a container config into $MONOCEROS_HOME/container/<name>/ and bring the dev-container up. Close any VS Code Remote Containers session for the target first — the extension auto-recreates and races with apply.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.',\n required: true,\n },\n },\n run({ args }) {\n return dispatch(async () => {\n const result = await runApply({\n name: args.name,\n cliVersion: CLI_VERSION,\n });\n return result.containerExitCode;\n });\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport { type MonocerosConfig, readMonocerosConfig } from '../config/global.js';\nimport { readConfig } from '../config/io.js';\nimport {\n containerConfigPath,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\nimport { REGEX } from '../config/schema.js';\nimport {\n buildStateFile,\n readStateFile,\n writeStateFile,\n} from '../config/state.js';\nimport type { SolutionConfig } from '../config/schema.js';\nimport { solutionConfigToCreateOptions } from '../config/transform.js';\nimport {\n needsCompose,\n normalizeOptions,\n validateOptions,\n writeScaffold,\n} from '../create/scaffold.js';\nimport { cyan, dim, sectionLine } from '../util/format.js';\nimport { migrateDeprecatedFeatureRef } from '../util/ref.js';\nimport {\n type ComposeSpawn,\n runContainerCycle,\n} from '../devcontainer/compose.js';\nimport {\n type CredentialsSpawn,\n collectGitCredentials,\n uniqueHttpsHosts,\n formatMissingCredentialsError,\n formatUnknownProviderError,\n} from '../devcontainer/credentials.js';\nimport {\n type ReachabilitySpawn,\n checkRepoReachability,\n formatUnreachableReposError,\n} from '../devcontainer/repo-reachability.js';\nimport {\n type DockerInfoSpawn,\n detectDockerMode,\n} from '../devcontainer/docker-mode.js';\nimport { type DevcontainerSpawn } from '../devcontainer/cli.js';\nimport {\n collectGitIdentity,\n type IdentityPrompt,\n type IdentitySpawn,\n} from '../devcontainer/identity.js';\n\n/**\n * `monoceros apply <name>` — read the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml`, materialize the\n * devcontainer scaffold at `<MONOCEROS_HOME>/container/<name>/`, write\n * `.monoceros/state.json` with `origin: <name>`, then teardown + bring\n * the container up.\n *\n * The target location is determined by convention, not by cwd or an\n * explicit path argument. That's deliberate: a config is the source of\n * truth, the container directory mirrors it 1:1, and the builder never\n * has to remember \"which directory was sandbox materialized into\".\n *\n * Idempotent: re-running picks up the current yml, overwrites scaffold\n * files, restarts the container.\n *\n * Refuses to materialize into a non-empty directory whose state.json\n * points at a different origin — protects against accidental clobber\n * if a builder somehow seeded `<MONOCEROS_HOME>/container/<name>/`\n * outside of this command.\n */\n\nexport interface RunApplyOptions {\n /** Config name — resolves to `<home>/container-configs/<name>.yml`. */\n name: string;\n cliVersion: string;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n now?: Date;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n warn?: (msg: string) => void;\n /**\n * Print a structural section marker (`▸ Configuration` etc).\n * Optional — tests typically pass a silent logger without one,\n * in which case section markers are no-ops.\n */\n section?: (label: string) => void;\n };\n cleanupSpawn?: ComposeSpawn;\n devcontainerSpawn?: DevcontainerSpawn;\n credentialsSpawn?: CredentialsSpawn;\n reachabilitySpawn?: ReachabilitySpawn;\n dockerInfoSpawn?: DockerInfoSpawn;\n identitySpawn?: IdentitySpawn;\n identityPrompt?: IdentityPrompt;\n}\n\nexport interface RunApplyResult {\n /** Absolute path to the materialized container directory. */\n targetDir: string;\n /** Absolute path to the source yml. */\n configPath: string;\n /** Exit code of the trailing `devcontainer up` step. */\n containerExitCode: number;\n}\n\nexport async function runApply(opts: RunApplyOptions): Promise<RunApplyResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n warn: (msg) => consola.warn(msg),\n // Default section renderer: empty line, bold-underlined \"▸ Label\",\n // empty line. Mirrors install.sh's section visuals.\n section: (label) => process.stderr.write(`\\n${sectionLine(label)}\\n\\n`),\n };\n const section = (label: string) => logger.section?.(label);\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const ymlPath = containerConfigPath(opts.name, home);\n if (!existsSync(ymlPath)) {\n throw new Error(\n `No such config: ${ymlPath}. Run \\`monoceros init <template> ${opts.name}\\` first.`,\n );\n }\n\n const targetDir = containerDir(opts.name, home);\n await assertSafeTargetDir(targetDir, opts.name);\n\n // ── Configuration ────────────────────────────────────────────\n section('Configuration');\n\n const parsed = await readConfig(ymlPath);\n // Read global defaults early — feature option defaults from\n // `monoceros-config.yml` need to be merged before scaffold codegen,\n // and the git identity logic later in this function also needs the\n // global config.\n const globalConfig = await readMonocerosConfig({ monocerosHome: home });\n\n // Pre-M4 the canonical feature namespace was\n // `ghcr.io/monoceros/features/…`. After the M4 cut it moved to\n // `ghcr.io/getmonoceros/monoceros-features/…`. Old refs still\n // structurally parse as third-party refs, so apply would silently\n // try to pull them from GHCR and 404. Warn loudly and tell the\n // builder what to write instead — but don't rewrite their yml or\n // fail the apply, so they stay in control of the migration.\n warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);\n\n // Shape validation happened in readConfig; catalog validation\n // (which language/service exists) happens here against\n // create/scaffold's known set.\n const createOpts = normalizeOptions(\n solutionConfigToCreateOptions(\n parsed.config,\n globalConfig?.defaults?.features ?? {},\n ),\n );\n validateOptions(createOpts);\n logger.success(`yml validated ${dim(`(${prettyPath(ymlPath)})`)}`);\n\n // Refresh host git identity and HTTPS credentials before the\n // container teardown so they're in place when post-create.sh runs.\n // Identity resolution priority: yml override → monoceros-config.yml\n // defaults → host global → persisted .monoceros/gitconfig → prompt.\n const idLogger = {\n info: logger.info,\n warn: logger.warn ?? logger.info,\n };\n await collectGitIdentity(targetDir, {\n ...(opts.identitySpawn ? { spawn: opts.identitySpawn } : {}),\n ...(opts.identityPrompt ? { prompt: opts.identityPrompt } : {}),\n ...(parsed.config.git?.user\n ? { containerOverride: parsed.config.git.user }\n : {}),\n ...(globalConfig?.defaults?.git?.user\n ? { defaults: globalConfig.defaults.git.user }\n : {}),\n logger: idLogger,\n });\n // Pre-fetch HTTPS credentials for every unique host derived from\n // the declared repos. Pre-flight: if any host returns no credentials,\n // fail fast with provider-specific setup hints — much more\n // actionable than letting the in-container `git clone` later die\n // with \"could not read Username\".\n //\n // First pass: reject hosts whose provider couldn't be resolved\n // (non-canonical host without an explicit `provider:` in the yml).\n // Those produce a separate \"set provider:\" error message — much\n // more useful than a generic \"no credentials\" hint because the\n // builder might actually have credentials in their helper, but we\n // wouldn't know which CLI to suggest.\n const hostsToFetch = uniqueHttpsHosts(createOpts.repos ?? []);\n const unknownProviderHosts = hostsToFetch\n .filter((h) => h.provider === 'unknown')\n .map((h) => h.host);\n if (unknownProviderHosts.length > 0) {\n throw new Error(formatUnknownProviderError(unknownProviderHosts));\n }\n if (hostsToFetch.length > 0) {\n const credResult = await collectGitCredentials(targetDir, hostsToFetch, {\n ...(opts.credentialsSpawn ? { spawn: opts.credentialsSpawn } : {}),\n logger: idLogger,\n });\n const missing = credResult.perHost.filter((p) => p.status !== 'ok');\n if (missing.length > 0) {\n throw new Error(formatMissingCredentialsError(missing));\n }\n }\n\n // Pre-flight stage 2: now that credentials are in place, probe each\n // declared repo URL via host-side `git ls-remote`. Catches the\n // \"repo doesn't exist / token can't see it / DNS broken\" failure\n // modes before the docker build runs — saving ~1–2 min on first\n // apply and replacing the noisy devcontainer-cli stack trace with\n // a focused per-repo error.\n const declaredRepos = createOpts.repos ?? [];\n if (declaredRepos.length > 0) {\n const reachability = await checkRepoReachability(declaredRepos, {\n ...(opts.reachabilitySpawn ? { spawn: opts.reachabilitySpawn } : {}),\n });\n const unreachable = reachability.filter((r) => !r.ok);\n if (unreachable.length > 0) {\n throw new Error(formatUnreachableReposError(unreachable));\n }\n }\n\n // ── Scaffold ─────────────────────────────────────────────────\n section('Scaffold');\n\n // Probe the host docker daemon for its mode (rootful vs rootless).\n // The result drives whether the generated devcontainer.json /\n // compose.yaml include `idmap` on their bind mounts — required for\n // rootless Docker on Linux where bind mounts otherwise apply a\n // user-namespace shift that breaks file ownership across the\n // host/container boundary. See docker-mode.ts for the rationale.\n const dockerMode = await detectDockerMode({\n ...(opts.dockerInfoSpawn ? { spawn: opts.dockerInfoSpawn } : {}),\n });\n\n await fs.mkdir(targetDir, { recursive: true });\n await writeScaffold(createOpts, targetDir, { dockerMode });\n await writeStateFile(\n targetDir,\n buildStateFile({\n origin: opts.name,\n cliVersion: opts.cliVersion,\n ...(opts.now ? { now: opts.now } : {}),\n }),\n );\n logger.success(`materialized into ${prettyPath(targetDir)}`);\n\n // ── Container ────────────────────────────────────────────────\n section('Container');\n\n // Pre-announce the feature list so the builder knows what's about\n // to be installed before devcontainer-cli's stream takes over.\n // Empty list = base-image-only container, no features section needed.\n const featureRefs = parsed.config.features.map((f) => f.ref);\n if (featureRefs.length > 0) {\n logger.info(`Features: ${featureRefs.map((r) => cyan(r)).join(', ')}`);\n }\n\n // First-apply UX: devcontainer-cli's upstream output prints\n // `Error fetching image details: No manifest found for …` for\n // multi-arch GHCR images, then sits silent for ~1 min while\n // Docker actually pulls the runtime image. Both are non-fatal —\n // the docker buildx step right after consumes the image just\n // fine. Flag in dim grey so it reads as ambient context.\n logger.info(\n dim(\n 'Pulling runtime image and building feature layers. First apply takes ~1–2 min (Docker downloads the multi-arch base); subsequent applies are cached and fast. devcontainer-cli may log a \"No manifest found\" line — harmless, the pull continues.',\n ),\n );\n\n const exitCode = await runContainerCycle(targetDir, {\n hasCompose: needsCompose(createOpts),\n ...(opts.cleanupSpawn !== undefined\n ? { cleanupSpawn: opts.cleanupSpawn }\n : {}),\n ...(opts.devcontainerSpawn !== undefined\n ? { devcontainerSpawn: opts.devcontainerSpawn }\n : {}),\n logger,\n });\n\n // ── Next steps ───────────────────────────────────────────────\n // Only print the wrap-up on a successful container start;\n // otherwise the failing devcontainer-cli output is the relevant\n // signal and a cheery \"shell into it!\" line would be misleading.\n if (exitCode === 0) {\n section('Next steps');\n logger.info(` ${cyan(`monoceros shell ${opts.name}`)}`);\n }\n\n return { targetDir, configPath: ymlPath, containerExitCode: exitCode };\n}\n\n/**\n * `<MONOCEROS_HOME>/container/<name>/` is safe to (re-)materialize iff:\n * - it doesn't exist or is empty (fresh apply), OR\n * - it already carries `.monoceros/state.json` with the same origin\n * (re-apply against the same yml), OR\n * - the only top-level entry is `.monoceros/` and there's no\n * state.json — that's a partial-apply remnant: pre-flight wrote\n * `gitconfig` / `git-credentials` into `.monoceros/` before\n * something (reachability failure, Ctrl-C, expired token, …)\n * aborted the apply ahead of `writeStateFile`. We own\n * `.monoceros/`, so re-running is safe.\n *\n * Anything else — state.json with a different origin, or files\n * outside `.monoceros/` that aren't ours — stays an error so we\n * don't clobber unrelated work.\n */\nasync function assertSafeTargetDir(\n targetDir: string,\n expectedOrigin: string,\n): Promise<void> {\n if (!existsSync(targetDir)) return;\n const entries = await fs.readdir(targetDir);\n if (entries.length === 0) return;\n\n const state = await readStateFile(targetDir);\n if (state) {\n if (state.origin !== expectedOrigin) {\n throw new Error(\n `${targetDir} is already materialized from config '${state.origin}', not '${expectedOrigin}'. Delete the directory to re-target, or run \\`monoceros apply ${state.origin}\\`.`,\n );\n }\n return; // safe: re-apply same origin\n }\n\n // No state.json. If the only top-level entry is `.monoceros/`, this\n // is a partial-apply remnant from a failed earlier run — pre-flight\n // writes `.monoceros/gitconfig` and `.monoceros/git-credentials`\n // BEFORE the scaffold + state.json sequence, so a mid-apply abort\n // leaves exactly this shape behind. Treat it as recoverable.\n if (entries.length === 1 && entries[0] === '.monoceros') {\n return;\n }\n\n throw new Error(\n `Refusing to materialize into non-empty directory ${targetDir} (no Monoceros state.json found, and the directory has files we don't recognise). Delete the directory before re-running.`,\n );\n}\n\ninterface MigrationLogger {\n warn?: (msg: string) => void;\n info: (msg: string) => void;\n}\n\nfunction warnOnDeprecatedFeatureRefs(\n containerFeatures: SolutionConfig['features'],\n globalConfig: MonocerosConfig | undefined,\n logger: MigrationLogger,\n): void {\n const warn = logger.warn ?? logger.info;\n const seen = new Set<string>();\n const emit = (oldRef: string, source: string) => {\n if (seen.has(oldRef)) return;\n seen.add(oldRef);\n const newRef = migrateDeprecatedFeatureRef(oldRef);\n if (!newRef) return;\n warn(\n `Deprecated feature ref in ${source}: '${oldRef}'. ` +\n `Replace with '${newRef}' — the old namespace is no longer published. ` +\n `See docs/MIGRATION-M4.md for a sed snippet.`,\n );\n };\n\n for (const entry of containerFeatures) {\n emit(entry.ref, 'container yml');\n }\n const globalDefaults = globalConfig?.defaults?.features;\n if (globalDefaults) {\n for (const ref of Object.keys(globalDefaults)) {\n emit(ref, 'monoceros-config.yml');\n }\n }\n}\n","import { promises as fs } from 'node:fs';\nimport { z } from 'zod';\nimport { parseDocument } from 'yaml';\nimport { FeatureOptionValueSchema, GitUserSchema, REGEX } from './schema.js';\nimport { monocerosConfigPath, monocerosHome } from './paths.js';\n\n/**\n * `<MONOCEROS_HOME>/monoceros-config.yml` — optional builder-owned\n * defaults that apply across every container materialized through\n * this home. Today the only field is git identity; future fields\n * (default editor, Claude auth profile, ...) plug into the same\n * structure.\n *\n * Schema is permissive: missing top-level keys are fine, the file\n * itself is optional. Schema violations surface as a hard error\n * (better to refuse than silently ignore a typo'd key the builder\n * thought was effective).\n */\n\nconst SCHEMA_VERSION = 1 as const;\n\n/**\n * `defaults.features` — map of devcontainer feature ref to a default\n * option object. When a container yml references the same feature ref\n * without overriding a specific option, the value from here is used.\n * Per-container options always win.\n *\n * Typical use: stash the Atlassian apiToken / Anthropic apiKey here\n * once globally instead of repeating them in every container yml.\n */\nexport const MonocerosConfigSchema = z.object({\n schemaVersion: z.literal(SCHEMA_VERSION),\n // .nullish() (= .optional().nullable()) on defaults so the shipped\n // sample yml — where `defaults:` is uncommented but every sub-block\n // is commented out — parses cleanly. YAML produces `defaults: null`\n // in that case; without .nullish() the schema would reject it and\n // we'd be back to forcing builders to comment-juggle three lines.\n defaults: z\n .object({\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n features: z\n .record(\n z\n .string()\n .regex(\n REGEX.featureRef,\n \"Invalid feature ref. Expected an OCI-image-style ref like 'ghcr.io/getmonoceros/monoceros-features/<name>:<tag>'.\",\n ),\n z.record(z.string(), FeatureOptionValueSchema),\n )\n .optional(),\n })\n .nullish(),\n});\n\nexport type MonocerosConfig = z.infer<typeof MonocerosConfigSchema>;\n\nexport interface ReadMonocerosConfigOptions {\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n}\n\n/**\n * Read `<home>/monoceros-config.yml`. Returns `undefined` if the file\n * isn't there (the normal case for a fresh setup). Throws on a parse\n * or schema error — the builder explicitly created the file, so a\n * silent ignore would be worse than a loud abort.\n */\nexport async function readMonocerosConfig(\n opts: ReadMonocerosConfigOptions = {},\n): Promise<MonocerosConfig | undefined> {\n const home = opts.monocerosHome ?? monocerosHome();\n const filePath = monocerosConfigPath(home);\n let text: string;\n try {\n text = await fs.readFile(filePath, 'utf8');\n } catch {\n return undefined;\n }\n const doc = parseDocument(text, { prettyErrors: true });\n if (doc.errors.length > 0) {\n throw new Error(\n `yaml parse error in ${filePath}: ${doc.errors[0]!.message}`,\n );\n }\n const result = MonocerosConfigSchema.safeParse(doc.toJS());\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(\n `Invalid ${filePath}:\\n${issues}\\n\\nSee ${filePath.replace(\n /\\.yml$/,\n '.sample.yml',\n )} for a valid example.`,\n );\n }\n return result.data;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_SCHEMA_VERSION } from './schema.js';\n\n/**\n * `.monoceros/state.json` — the Phase-3 replacement for `stack.json`.\n *\n * The yml at `.local/container-configs/<origin>.yml` is the source\n * of truth. `state.json` is a back-reference: it tells `monoceros\n * apply` (no args) which yml to read and re-apply for this dev-\n * container. Other fields (appliedAt, cliVersion) are pure diagnostics\n * for `monoceros status` and ad-hoc debugging.\n *\n * `materializedAt` is the timestamp of the most recent apply, NOT\n * the create. There is no `createdAt` because the yml is the\n * lifecycle anchor now — multiple dev-containers can share one yml,\n * and each has its own state.json timeline.\n */\nexport interface StateFile {\n schemaVersion: typeof CONFIG_SCHEMA_VERSION;\n /** Config name the yml is stored under (`<origin>.yml`). */\n origin: string;\n /** Monoceros CLI version that wrote this state.json. */\n monocerosCliVersion: string;\n /** ISO-8601 timestamp of the most recent apply. */\n materializedAt: string;\n}\n\nexport function buildStateFile(opts: {\n origin: string;\n cliVersion: string;\n now?: Date;\n}): StateFile {\n return {\n schemaVersion: CONFIG_SCHEMA_VERSION,\n origin: opts.origin,\n monocerosCliVersion: opts.cliVersion,\n materializedAt: (opts.now ?? new Date()).toISOString(),\n };\n}\n\nexport function stateFilePath(targetDir: string): string {\n return path.join(targetDir, '.monoceros', 'state.json');\n}\n\nexport async function readStateFile(\n targetDir: string,\n): Promise<StateFile | undefined> {\n try {\n const content = await fs.readFile(stateFilePath(targetDir), 'utf8');\n return JSON.parse(content) as StateFile;\n } catch {\n return undefined;\n }\n}\n\nexport async function writeStateFile(\n targetDir: string,\n state: StateFile,\n): Promise<void> {\n const monocerosDir = path.join(targetDir, '.monoceros');\n await fs.mkdir(monocerosDir, { recursive: true });\n await fs.writeFile(\n stateFilePath(targetDir),\n JSON.stringify(state, null, 2) + '\\n',\n );\n}\n","import { deriveRepoName } from '../create/scaffold.js';\nimport type { CreateOptions, FeatureOptions } from '../create/types.js';\nimport type { SolutionConfig } from './schema.js';\n\n/**\n * Translate a yml-shaped `SolutionConfig` into the `CreateOptions`\n * shape the existing scaffolders (devcontainer.json, compose.yaml,\n * post-create.sh) consume.\n *\n * The big shape mismatch is `features`:\n * - yml: `[{ ref, options }, …]` (array; humans edit/comment this)\n * - CreateOptions: `Record<ref, options>` (devcontainer.json shape)\n * We dedupe by `ref` and keep the LAST occurrence — same rule the\n * existing `add-feature` command uses when a builder re-adds with new\n * options.\n *\n * `externalServices.postgres` → `postgresUrl` (the CreateOptions\n * field name predates the yml schema; rename would touch every M1\n * caller, so the translator absorbs the diff here).\n *\n * `featureDefaults` (optional) — `defaults.features` from\n * `monoceros-config.yml`. Per-container options always override these;\n * keys not set per-container fall back to the default. A feature ref\n * that exists only in `featureDefaults` (not in the container yml)\n * does NOT get included — the container yml is what decides whether\n * a feature is active at all; the defaults only fill in option values.\n */\nexport function solutionConfigToCreateOptions(\n config: SolutionConfig,\n featureDefaults: Record<string, FeatureOptions> = {},\n): CreateOptions {\n const featureRecord: Record<string, FeatureOptions> = {};\n for (const entry of config.features) {\n const defaults = featureDefaults[entry.ref] ?? {};\n featureRecord[entry.ref] = { ...defaults, ...(entry.options ?? {}) };\n }\n\n const result: CreateOptions = {\n name: config.name,\n languages: [...config.languages],\n services: [...config.services],\n };\n\n if (config.externalServices.postgres !== undefined) {\n result.postgresUrl = config.externalServices.postgres;\n }\n if (config.aptPackages.length > 0) {\n result.aptPackages = [...config.aptPackages];\n }\n if (Object.keys(featureRecord).length > 0) {\n result.features = featureRecord;\n }\n if (config.installUrls.length > 0) {\n result.installUrls = [...config.installUrls];\n }\n if (config.repos.length > 0) {\n result.repos = config.repos.map((r) => ({\n url: r.url,\n // `path` is optional in the yml; CreateOptions requires it.\n // When the yml omits `path`, fall back to the URL-derived\n // single-segment default (`https://.../foo.git` → `foo`),\n // which lands the clone at `projects/foo/`.\n path: r.path ?? deriveRepoName(r.url),\n ...(r.git?.user\n ? { gitUser: { name: r.git.user.name, email: r.git.user.email } }\n : {}),\n ...(r.provider ? { provider: r.provider } : {}),\n }));\n }\n return result;\n}\n","// Shared terminal-formatting helpers for CLI output. Same palette\n// as install.sh and packages/cli/src/help.ts:\n//\n// cyan = identifiers you type (commands, refs, component names)\n// grey = supplementary metadata (paths, version notes, hints)\n// bold+und. = structural section markers\n//\n// Status semantics (green ✓, red ✗, yellow !) live in consola for\n// log-level lines and aren't duplicated here.\n//\n// Two flavours of consumer:\n// - status output (install.sh, apply) goes to stderr — use the\n// top-level helpers below; they gate on process.stderr.isTTY.\n// - data output (list-components) goes to stdout — use\n// `colorsFor(process.stdout)` to get the same helpers gated\n// against the right stream, so colours drop out cleanly when\n// the user pipes the output into grep/less/etc.\n\nconst ESC = '\\x1b[';\nconst ANSI_BOLD = `${ESC}1m`;\nconst ANSI_UNDERLINE = `${ESC}4m`;\nconst ANSI_CYAN = `${ESC}36m`;\nconst ANSI_GREY = `${ESC}90m`;\nconst ANSI_RESET = `${ESC}0m`;\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;]*m/g;\n\n/**\n * Visible character count, ANSI escape sequences stripped. Used\n * for column-padding so coloured labels still line up.\n */\nexport function visibleLen(s: string): number {\n return s.replace(ANSI_RE, '').length;\n}\n\nexport interface Palette {\n bold: (s: string) => string;\n underline: (s: string) => string;\n cyan: (s: string) => string;\n dim: (s: string) => string;\n /**\n * Section marker — bold + underlined with a `▸` chevron prefix.\n * Same visual treatment as install.sh's section headers.\n */\n sectionLine: (label: string) => string;\n}\n\nfunction makeWrap(isTty: boolean): (s: string, ...codes: string[]) => string {\n return (s, ...codes) => (isTty ? codes.join('') + s + ANSI_RESET : s);\n}\n\nfunction makePalette(isTty: boolean): Palette {\n const wrap = makeWrap(isTty);\n return {\n bold: (s) => wrap(s, ANSI_BOLD),\n underline: (s) => wrap(s, ANSI_UNDERLINE),\n cyan: (s) => wrap(s, ANSI_CYAN),\n dim: (s) => wrap(s, ANSI_GREY),\n sectionLine: (label) => wrap(`▸ ${label}`, ANSI_BOLD, ANSI_UNDERLINE),\n };\n}\n\n/**\n * Resolve a stream-specific palette. Pass `process.stdout` for\n * commands whose payload goes to stdout (so colours drop out when\n * piped); `process.stderr` for status output that stays on stderr\n * regardless of stdout's destination.\n */\nexport function colorsFor(stream: NodeJS.WriteStream): Palette {\n return makePalette(stream.isTTY ?? false);\n}\n\n// Top-level convenience helpers — gated on stderr, matching the\n// install-/apply-style status output that's always written to\n// stderr. Existing call sites keep working unchanged.\nconst stderrPalette = makePalette(process.stderr.isTTY ?? false);\nexport const bold = stderrPalette.bold;\nexport const underline = stderrPalette.underline;\nexport const cyan = stderrPalette.cyan;\nexport const dim = stderrPalette.dim;\nexport const sectionLine = stderrPalette.sectionLine;\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport { createSecretMaskStream } from '../util/mask-secrets.js';\nimport { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\n\nexport type ComposeSpawn = (args: string[], cwd: string) => Promise<number>;\n\n// Default spawn: shells out to `docker compose` (the v2 docker\n// subcommand). Stdout/stderr are streamed through a secret masker\n// (see util/mask-secrets.ts) so feature option dumps, ENV-printouts\n// and similar do not leak Atlassian/GitHub/Anthropic tokens onto\n// the host terminal.\nexport const spawnDockerCompose: ComposeSpawn = (args, cwd) => {\n return new Promise((resolve, reject) => {\n const child = spawn('docker', ['compose', ...args], {\n cwd,\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n\n// Generic shell spawn used by `monoceros apply`/`remove` for label-\n// based docker cleanup pipelines. Same ComposeSpawn shape so tests\n// can inject a fake; `args[0]` is `-c`, `args[1]` is the shell\n// command string. Output goes through the secret masker for the\n// same reasons spawnDockerCompose does.\nexport const spawnBash: ComposeSpawn = (args, cwd) => {\n return new Promise((resolve, reject) => {\n const child = spawn('bash', args, {\n cwd,\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n\ninterface ResolvedCompose {\n composeFile: string;\n projectName: string;\n}\n\n// Match the project name `@devcontainers/cli` derives when it brings a\n// compose-mode devcontainer up: `<root-basename>_devcontainer`.\n// Aligning here means `monoceros start/stop/status/logs` and the\n// implicit `devcontainer up` from `monoceros run/shell` act on the\n// same compose project — without it docker would create two parallel\n// stacks.\nexport function composeProjectName(root: string): string {\n return `${path.basename(root)}_devcontainer`;\n}\n\n/**\n * Resolve compose-mode metadata for the container rooted at `root`.\n * `root` is `<MONOCEROS_HOME>/container/<name>/` and must already\n * exist with a `.devcontainer/compose.yaml` inside. The compose-only\n * lifecycle commands (`start/stop/status/logs/down`) error when the\n * file is missing.\n */\nexport function resolveCompose(root: string): ResolvedCompose {\n if (!existsSync(path.join(root, '.devcontainer'))) {\n throw new Error(\n `No .devcontainer/ at ${root}. Run \\`monoceros apply <name>\\` first.`,\n );\n }\n const composeFile = path.join(root, '.devcontainer', 'compose.yaml');\n if (!existsSync(composeFile)) {\n throw new Error(\n `No compose.yaml at ${composeFile}. \\`start\\` / \\`stop\\` / \\`status\\` / \\`logs\\` require services configured via \\`monoceros add-service <name> <svc>\\`. Use \\`monoceros shell <name>\\` to enter the container directly.`,\n );\n }\n return { composeFile, projectName: composeProjectName(root) };\n}\n\nexport interface ComposeActionOptions {\n root: string;\n service?: string;\n spawn?: ComposeSpawn;\n}\n\nasync function runComposeAction(\n buildSubArgs: (service: string | undefined) => string[],\n opts: ComposeActionOptions,\n): Promise<number> {\n const { composeFile, projectName } = resolveCompose(opts.root);\n const spawnFn = opts.spawn ?? spawnDockerCompose;\n const subArgs = buildSubArgs(opts.service);\n return spawnFn(['-f', composeFile, '-p', projectName, ...subArgs], opts.root);\n}\n\nexport interface StartOptions {\n root: string;\n spawn?: DevcontainerSpawn;\n logger?: { info: (message: string) => void };\n}\n\n// `monoceros start` delegates to `devcontainer up` rather than to\n// `docker compose up -d`. The detour through @devcontainers/cli matters\n// because:\n// - it labels the workspace container with `devcontainer.local_folder`\n// so subsequent `devcontainer exec` (from `monoceros run/shell`) can\n// find the container by workspace path,\n// - it applies devcontainer features (which docker compose ignores), and\n// - it triggers the postCreateCommand once.\n// The auxiliary services come up alongside because the generated\n// devcontainer.json lists them under `runServices`.\nexport async function runStart(opts: StartOptions): Promise<number> {\n resolveCompose(opts.root); // throws if no compose.yaml\n const logger = opts.logger ?? { info: (msg) => consola.info(msg) };\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n logger.info(`Bringing devcontainer up at ${opts.root}…`);\n return spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n );\n}\n\nexport interface RunContainerCycleOptions {\n hasCompose: boolean;\n cleanupSpawn?: ComposeSpawn;\n devcontainerSpawn?: DevcontainerSpawn;\n logger: {\n info: (message: string) => void;\n warn?: (message: string) => void;\n };\n}\n\n/**\n * Container teardown + up for a devcontainer rooted at `root`.\n * Used by `runApply` (apply/index.ts) after writing the scaffold.\n */\nexport async function runContainerCycle(\n root: string,\n opts: RunContainerCycleOptions,\n): Promise<number> {\n const { hasCompose, logger } = opts;\n\n if (hasCompose) {\n const projectName = composeProjectName(root);\n logger.info(\n `Force-removing existing ${projectName} containers (volumes preserved)…`,\n );\n const cleanupSpawn = opts.cleanupSpawn ?? spawnBash;\n // Two-step removal so a container with stale/missing labels still\n // gets caught:\n // - by docker-compose project label\n // - by container-name prefix `<project>-*`\n // After removal we re-query: if anything remains, VS Code's Remote\n // Containers extension is the likely culprit (auto-recreates on\n // container loss); we abort with a clear hint rather than letting\n // `devcontainer up` collide.\n const script = [\n `set -u`,\n `echo \"[cleanup] checking project ${projectName}…\"`,\n `by_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n `by_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n `to_remove=$(printf \"%s\\\\n%s\\\\n\" \"$by_label\" \"$by_name\" | sort -u | grep -v \"^$\" || true)`,\n `if [ -n \"$to_remove\" ]; then echo \"[cleanup] removing: $(echo $to_remove | tr \"\\\\n\" \" \")\"; docker rm -f $to_remove >/dev/null || true; else echo \"[cleanup] no containers to remove\"; fi`,\n `docker network rm ${projectName}_default 2>/dev/null && echo \"[cleanup] network ${projectName}_default removed\" || echo \"[cleanup] network ${projectName}_default not present\"`,\n `remaining_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n `remaining_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n `if [ -n \"$remaining_label\" ] || [ -n \"$remaining_name\" ]; then echo \"\" >&2; echo \"ERROR: containers under project ${projectName} reappeared after removal.\" >&2; echo \"This typically means VS Code's Remote Containers extension is connected to\" >&2; echo \"this devcontainer and auto-recreated it. Close the dev container session\" >&2; echo \"in VS Code (Cmd+Shift+P → 'Dev Containers: Close Remote Connection')\" >&2; echo \"and retry \\\\\\`monoceros apply\\\\\\`.\" >&2; exit 1; fi`,\n `echo \"[cleanup] done\"`,\n ].join('; ');\n const cleanupCode = await cleanupSpawn(['-c', script], root);\n if (cleanupCode !== 0) return cleanupCode;\n\n return runStart({\n root,\n ...(opts.devcontainerSpawn ? { spawn: opts.devcontainerSpawn } : {}),\n logger,\n });\n }\n\n logger.info(`Recreating image-mode devcontainer at ${root}…`);\n const spawnFn = opts.devcontainerSpawn ?? spawnDevcontainer;\n return spawnFn(\n [\n 'up',\n '--workspace-folder',\n root,\n '--mount-workspace-git-root=false',\n '--remove-existing-container',\n ],\n root,\n );\n}\n\nexport function runStop(opts: ComposeActionOptions): Promise<number> {\n return runComposeAction(\n (service) => ['stop', ...(service ? [service] : [])],\n opts,\n );\n}\n\nexport function runStatus(opts: ComposeActionOptions): Promise<number> {\n return runComposeAction(\n (service) => ['ps', ...(service ? [service] : [])],\n opts,\n );\n}\n\nexport interface LogsOptions extends ComposeActionOptions {\n follow?: boolean;\n}\n\nexport function runLogs(opts: LogsOptions): Promise<number> {\n const follow = opts.follow ?? true;\n return runComposeAction(\n (service) => [\n 'logs',\n ...(follow ? ['-f'] : []),\n ...(service ? [service] : []),\n ],\n opts,\n );\n}\n","import { Transform, type TransformCallback } from 'node:stream';\n\n/**\n * Mask known token-shaped strings in arbitrary text.\n *\n * Devcontainer-cli and docker compose stream the build/up output\n * straight to stdout. They include the feature options (Atlassian\n * apiToken, GitHub PAT, Anthropic apiKey, …) verbatim, which leaks\n * real, per-builder secrets onto the user's terminal and into any\n * captured CI log.\n *\n * The fix is a regex sweep on each output line: when a token\n * matches a known prefix shape, replace its middle with `…` and\n * keep the prefix + last 6 characters so the value is still\n * recognizable for debugging (\"did the right token get loaded?\")\n * without exposing the secret.\n *\n * What's **not** masked here, by design:\n *\n * - The literal `monoceros` user/password baked into the compose\n * service catalog (postgres, mysql). It's a documented dev-\n * convention, identical on every Monoceros container, openly\n * listed in `create/catalog.ts` and the components README. Not\n * a secret. Masking it would just make the connection string\n * harder to spot for the builder.\n * - Anything that looks \"password-shaped\" via a key= pattern.\n * Risk of false positives outweighs cosmetic benefit when the\n * value isn't actually sensitive (see also ADR-style note in\n * `create/catalog.ts`).\n */\n\ninterface SecretPattern {\n /** Short label for the pattern, useful in debugging. */\n name: string;\n /** Match shape. Must be a /g regex so all occurrences get replaced. */\n re: RegExp;\n}\n\n// Order doesn't matter — patterns are disjoint by prefix.\nconst PATTERNS: SecretPattern[] = [\n // Atlassian Cloud API token. Starts with literal `ATATT3xFf` plus\n // a long URL-safe-base64 tail. Tightened to that prefix to avoid\n // matching unrelated all-caps words.\n { name: 'atlassian-api', re: /ATATT3xFf[A-Za-z0-9+/=_-]{20,}/g },\n // Bitbucket Cloud app password.\n { name: 'bitbucket-app', re: /ATBB[A-Za-z0-9+/=_-]{20,}/g },\n // GitHub PAT (classic), OAuth, user, server, refresh — all share\n // the `gh<lower-letter>_<base62>` shape per GitHub's token format.\n { name: 'github-token', re: /gh[a-z]_[A-Za-z0-9]{20,}/g },\n // GitHub fine-grained PAT.\n { name: 'github-pat', re: /github_pat_[A-Za-z0-9_]{20,}/g },\n // Anthropic API key.\n { name: 'anthropic-api', re: /sk-ant-[A-Za-z0-9_-]{20,}/g },\n];\n\n/**\n * Replace token-shaped substrings with a masked form. Idempotent\n * for already-masked strings (the elision character isn't part of\n * any pattern's alphabet).\n */\nexport function maskSecrets(text: string): string {\n let result = text;\n for (const { re } of PATTERNS) {\n result = result.replace(re, maskOne);\n }\n return result;\n}\n\nfunction maskOne(token: string): string {\n if (token.length <= 12) return token;\n return `${token.slice(0, 5)}…${token.slice(-6)}`;\n}\n\n/**\n * Transform stream that runs every chunk through `maskSecrets`.\n *\n * Tokens can in theory straddle a chunk boundary if the upstream\n * writer flushes mid-token, leaving an unmasked tail. Mitigation:\n * the transform holds back the last line of every chunk until a\n * newline arrives, since real Docker / devcontainer-cli output is\n * line-oriented and tokens don't contain newlines. On final flush\n * any leftover buffer is masked and emitted.\n */\nexport function createSecretMaskStream(): Transform {\n let buffer = '';\n return new Transform({\n decodeStrings: true,\n transform(chunk: Buffer | string, _enc, cb: TransformCallback): void {\n const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n buffer += text;\n const lastNewline = buffer.lastIndexOf('\\n');\n if (lastNewline === -1) {\n // No complete line yet — keep buffering. We'd rather hold\n // back partial output briefly than emit half a token.\n cb(null);\n return;\n }\n const flushable = buffer.slice(0, lastNewline + 1);\n buffer = buffer.slice(lastNewline + 1);\n cb(null, maskSecrets(flushable));\n },\n flush(cb: TransformCallback): void {\n if (buffer.length > 0) {\n const tail = maskSecrets(buffer);\n buffer = '';\n cb(null, tail);\n return;\n }\n cb(null);\n },\n });\n}\n","import { spawn } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { createSecretMaskStream, maskSecrets } from '../util/mask-secrets.js';\n\nconst require_ = createRequire(import.meta.url);\n\nlet cachedBinaryPath: string | null = null;\n\n// Resolve the absolute path to the `@devcontainers/cli` JS entry point. We\n// invoke it via `node <path>` rather than relying on a `.bin/` shim being on\n// PATH, so the CLI works regardless of how the user installed the workbench.\nexport function devcontainerCliPath(): string {\n if (cachedBinaryPath) return cachedBinaryPath;\n const pkgJsonPath = require_.resolve('@devcontainers/cli/package.json');\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8')) as {\n bin?: string | Record<string, string>;\n };\n const binEntry =\n typeof pkg.bin === 'string' ? pkg.bin : (pkg.bin?.devcontainer ?? '');\n if (!binEntry) {\n throw new Error('Could not resolve @devcontainers/cli bin entry.');\n }\n cachedBinaryPath = path.resolve(path.dirname(pkgJsonPath), binEntry);\n return cachedBinaryPath;\n}\n\nexport interface DevcontainerSpawnOptions {\n // When true, capture stdout and stderr instead of inheriting them.\n // The buffered output is only flushed (to stderr) if the process exits\n // non-zero, so successful no-op invocations stay silent. Use this for\n // intermediate steps like the implicit `up` that `monoceros run` does\n // before `exec`; leave it unset for explicit lifecycle calls\n // (`monoceros start`) and for the final exec where the user expects to\n // see output.\n quiet?: boolean;\n // When true, hand the child process direct stdio. Pure `inherit` —\n // no piping, no secret masking, no buffering. Required for any\n // interactive use case (`monoceros shell`, the `exec` step of\n // `monoceros run`): bash detects a non-TTY stdin/stdout and exits\n // immediately, which makes `--with` stdio pipes a non-starter.\n // The build/log paths in apply and start still go through the\n // masked-pipe path, where there's no TTY at stake.\n interactive?: boolean;\n}\n\nexport type DevcontainerSpawn = (\n args: string[],\n cwd: string,\n options?: DevcontainerSpawnOptions,\n) => Promise<number>;\n\n// Default spawn implementation: runs the @devcontainers/cli binary\n// directly. Both stdout and stderr are streamed through a secret\n// masker (see util/mask-secrets.ts) so that feature options like\n// Atlassian apiTokens or GitHub PATs do not leak verbatim to the\n// terminal when devcontainer-cli logs the build args / feature\n// option dumps. With `{ quiet: true }` output is buffered (and\n// masked) and only flushed on a non-zero exit.\nexport const spawnDevcontainer: DevcontainerSpawn = (\n args,\n cwd,\n options = {},\n) => {\n const binPath = devcontainerCliPath();\n return new Promise((resolve, reject) => {\n if (options.interactive) {\n // Direct inherit — required so the child binary sees a real\n // TTY on stdin/stdout/stderr. Secret masking is irrelevant\n // here (the builder is running an interactive command;\n // build-time option dumps don't fire on this path).\n const child = spawn(process.execPath, [binPath, ...args], {\n cwd,\n stdio: 'inherit',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n return;\n }\n const child = spawn(process.execPath, [binPath, ...args], {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n if (options.quiet) {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n child.stdout?.on('data', (chunk: Buffer) => stdoutChunks.push(chunk));\n child.stderr?.on('data', (chunk: Buffer) => stderrChunks.push(chunk));\n child.on('error', reject);\n child.on('exit', (code) => {\n const exitCode = code ?? 0;\n if (exitCode !== 0) {\n process.stderr.write(\n maskSecrets(Buffer.concat(stderrChunks).toString('utf8')),\n );\n process.stderr.write(\n maskSecrets(Buffer.concat(stdoutChunks).toString('utf8')),\n );\n }\n resolve(exitCode);\n });\n return;\n }\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { RepoEntry } from '../create/types.js';\nimport { KNOWN_PROVIDER_HOSTS, type RepoProvider } from '../config/schema.js';\nimport { cyan } from '../util/format.js';\n\n/**\n * Spawn signature for `git credential fill`: takes the credential-\n * protocol input on stdin, returns the helper's response on stdout\n * plus the process exit code. Injected by tests.\n */\nexport type CredentialsSpawn = (\n input: string,\n) => Promise<{ stdout: string; exitCode: number }>;\n\nconst realGitCredentialFill: CredentialsSpawn = (input) => {\n return new Promise((resolve, reject) => {\n // GIT_TERMINAL_PROMPT=0 disables git's interactive\n // username/password fallback. Without this, when no credential\n // helper has an entry for the host, `git credential fill` would\n // open /dev/tty and prompt the user — which hangs apply\n // indefinitely because the parent process is running non-\n // interactively. With the env var set, git returns whatever\n // the helpers produced (possibly empty) and exits cleanly,\n // letting our pre-flight detect \"no credentials\" reliably.\n //\n // GIT_ASKPASS='' / SSH_ASKPASS='' are belt-and-suspenders for\n // setups where a GUI askpass helper is configured globally —\n // emptying them prevents a popup that would also block apply.\n const child = spawn('git', ['credential', 'fill'], {\n stdio: ['pipe', 'pipe', 'inherit'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: '',\n SSH_ASKPASS: '',\n },\n });\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve({ stdout, exitCode: code ?? 0 }));\n child.stdin.write(input);\n child.stdin.end();\n });\n};\n\n/**\n * Resolve a host's provider:\n * - canonical hosts (github.com / gitlab.com / bitbucket.org) →\n * their fixed provider, ignoring any explicit hint (the canonical\n * mapping is the source of truth)\n * - any other host → the explicit hint if given, else 'unknown'\n *\n * Returning 'unknown' triggers the apply pre-flight error that asks\n * the builder to set `provider:` in the yml. We deliberately never\n * guess from hostname patterns (\"starts with `gitlab.`\" etc.) —\n * those produced wrong results for corporate domains like\n * `git.firma.de` and silently fell through to the generic hint.\n */\nexport type ResolvedProvider = RepoProvider | 'unknown';\n\nexport function resolveProvider(\n host: string,\n explicit?: RepoProvider,\n): ResolvedProvider {\n const canonical = KNOWN_PROVIDER_HOSTS[host.toLowerCase()];\n if (canonical) return canonical;\n return explicit ?? 'unknown';\n}\n\nexport interface HostWithProvider {\n host: string;\n provider: ResolvedProvider;\n}\n\n/**\n * Reduce a repo list to one entry per host, carrying along the\n * resolved provider so the pre-flight check can render the right\n * setup hint (or error out with \"set provider:\" for unknowns).\n *\n * If the same host appears with conflicting provider declarations\n * across multiple repo entries, the first one wins — the apply\n * pre-flight surfaces the conflict as a separate diagnostic before\n * we get here in normal flow. (Schema-level dedup would lock us in\n * before the builder ever sees the warning.)\n */\nfunction uniqueHttpsHosts(repos: readonly RepoEntry[]): HostWithProvider[] {\n const byHost = new Map<string, HostWithProvider>();\n for (const repo of repos) {\n if (!repo.url.startsWith('https://')) continue;\n let host: string;\n try {\n host = new URL(repo.url).hostname;\n } catch {\n // Skip malformed URLs — validateOptions catches them at the\n // add-repo step, so reaching this in production means a stack\n // file was hand-edited. Don't fail the whole apply for it.\n continue;\n }\n if (byHost.has(host)) continue;\n byHost.set(host, { host, provider: resolveProvider(host, repo.provider) });\n }\n return [...byHost.values()];\n}\n\n/**\n * Render a provider-specific install command, filtered to the host\n * OS. Returns the relevant line for the current platform — macOS gets\n * the brew command, Windows gets the winget command, Linux falls back\n * to a docs link unless the provider officially recommends a uniform\n * Linux command (then `linuxBrew` is set and used). Callers embed the\n * resulting single line into a setup-instructions block.\n */\nfunction installCommandForOS(opts: {\n brew: string;\n winget: string;\n /**\n * Linux install command when the provider's docs officially\n * recommend a single uniform path (e.g. GitLab's glab CLI ships\n * Homebrew as the supported Linux install method). When omitted,\n * Linux gets a docs link instead because distro packaging is too\n * heterogeneous to pick a winner.\n */\n linuxBrew?: string;\n linuxDocsUrl: string;\n}): string {\n switch (process.platform) {\n case 'darwin':\n return cyan(opts.brew);\n case 'win32':\n return cyan(opts.winget);\n default:\n if (opts.linuxBrew) return cyan(opts.linuxBrew);\n return `See ${opts.linuxDocsUrl} for package instructions.`;\n }\n}\n\n/**\n * Provider-specific setup hint per host. Used in the pre-flight\n * error message when `git credential fill` returns nothing for a\n * host. Shows only the install command for the current host OS —\n * less visual noise, no \"is this me?\" guesswork for the builder.\n *\n * Provider is resolved upstream (canonical-host lookup or explicit\n * yml field). This function NEVER guesses from hostname patterns;\n * see `resolveProvider` for the rationale.\n */\nexport function providerSetupHint(\n host: string,\n provider: RepoProvider,\n): {\n /** Short title for the host, formatted as \"host — Provider\". */\n title: string;\n /** Multiline body, left-aligned, no leading indentation. */\n body: string;\n} {\n if (provider === 'github') {\n // `--hostname` is only needed for self-hosted GitHub Enterprise\n // Server. For github.com (SaaS) gh defaults to that host, so we\n // omit the flag. Both `gh auth login` and `gh auth setup-git`\n // accept --hostname with identical semantics — verified against\n // https://cli.github.com/manual/gh_auth_login and\n // https://cli.github.com/manual/gh_auth_setup-git .\n const isSaas = host.toLowerCase() === 'github.com';\n const hostArg = isSaas ? '' : ` --hostname ${host}`;\n const install = installCommandForOS({\n brew: 'brew install gh',\n winget: 'winget install --id GitHub.cli',\n linuxDocsUrl: 'https://github.com/cli/cli#installation',\n });\n return {\n title: `${host} — GitHub`,\n body: [\n 'Install the GitHub CLI:',\n install,\n '',\n 'Then run once:',\n cyan(`gh auth login${hostArg}`),\n cyan(`gh auth setup-git${hostArg}`),\n '',\n '`gh auth login` walks through OAuth in your browser.',\n '`gh auth setup-git` wires gh into git as a credential helper.',\n ].join('\\n'),\n };\n }\n if (provider === 'gitlab') {\n // `--hostname` is only needed for self-hosted GitLab. For\n // gitlab.com glab defaults to the SaaS host, so we omit the flag.\n const isSaas = host.toLowerCase() === 'gitlab.com';\n const hostArg = isSaas ? '' : ` --hostname ${host}`;\n // GitLab's official install docs (https://gitlab.com/gitlab-org/\n // cli/-/blob/main/docs/installation_options.md) state that\n // Homebrew is \"the officially supported installation method for\n // Linux\" — so we use the same brew command on macOS AND Linux,\n // with winget on Windows and a docs link as the absolute last\n // resort.\n const install = installCommandForOS({\n brew: 'brew install glab',\n winget: 'winget install --id GLab.GLab',\n linuxBrew: 'brew install glab',\n linuxDocsUrl: 'https://gitlab.com/gitlab-org/cli#installation',\n });\n return {\n title: `${host} — GitLab`,\n body: [\n 'Install the GitLab CLI (glab):',\n install,\n '',\n 'Then run once:',\n cyan(`glab auth login${hostArg}`),\n '',\n 'Choose `HTTPS` when asked for git-protocol, then accept',\n '\"Authenticate Git with your GitLab credentials\" — glab',\n 'configures itself as the git credential helper.',\n ].join('\\n'),\n };\n }\n if (provider === 'bitbucket') {\n // Bitbucket has no first-party CLI for git-credentials (no\n // `bb auth login` equivalent to gh/glab), so this is a manual\n // one-time setup either way. The Cloud and Data-Center variants\n // differ in where you get the token and what the username field\n // expects — same pattern as the github / gitlab branches above\n // (canonical SaaS host vs. self-hosted).\n const isCloud = host.toLowerCase() === 'bitbucket.org';\n if (isCloud) {\n return {\n title: `${host} — Bitbucket Cloud`,\n body: [\n 'Bitbucket has no first-party CLI for git-credentials, so this',\n 'is a manual one-time setup. Generate an Atlassian API token at',\n 'https://id.atlassian.com/manage-profile/security/api-tokens',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-atlassian-email>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n }\n return {\n title: `${host} — Bitbucket Data Center`,\n body: [\n 'Bitbucket has no first-party CLI for git-credentials, so this',\n 'is a manual one-time setup. Generate a personal HTTP access',\n `token in your Bitbucket UI: profile picture (top right on ${host})`,\n '→ Manage account → HTTP access tokens → Create token. Give it',\n 'at least repo-read + repo-write scopes for the repos you need.',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-bitbucket-username>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n }\n // provider === 'gitea' — Gitea is always self-hosted (gitea.com is\n // a demo / sandbox, not a SaaS), so there's no canonical-host\n // branch. The `tea` CLI exists but logs into its own config and\n // doesn't register as a git credential helper (verified against\n // https://gitea.com/gitea/tea), so we point at the UI flow + a\n // direct `git credential approve` — same pattern as Bitbucket\n // Data Center. Forgejo (the Gitea fork) shares this flow exactly.\n return {\n title: `${host} — Gitea`,\n body: [\n 'Gitea has no first-party CLI helper for git-credentials (the',\n '`tea` CLI logs into its own config, not into your git credential',\n 'helper), so this is a manual one-time setup. Generate an access',\n `token in your Gitea UI: profile picture (top right on ${host}) →`,\n 'Settings → Applications → \"Generate New Token\". Give it at',\n 'least the `read:repository` scope (add `write:repository` if you',\n 'need push from the container).',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-gitea-username>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n}\n\ninterface ParsedCreds {\n username?: string;\n password?: string;\n}\n\nfunction parseCredentialFillOutput(output: string): ParsedCreds {\n const result: ParsedCreds = {};\n for (const line of output.split('\\n')) {\n const eqIdx = line.indexOf('=');\n if (eqIdx <= 0) continue;\n const key = line.slice(0, eqIdx);\n const value = line.slice(eqIdx + 1);\n if (key === 'username') result.username = value;\n if (key === 'password') result.password = value;\n }\n return result;\n}\n\nfunction formatCredentialLine(\n host: string,\n username: string,\n password: string,\n): string {\n // Both fields percent-encoded so a `@`, `:`, or `/` in the token\n // doesn't break URL parsing inside git's `store` helper.\n const encUser = encodeURIComponent(username);\n const encPass = encodeURIComponent(password);\n return `https://${encUser}:${encPass}@${host}`;\n}\n\nexport interface CollectCredentialsOptions {\n spawn?: CredentialsSpawn;\n logger?: { info: (msg: string) => void; warn: (msg: string) => void };\n}\n\nexport interface HostCredentialStatus {\n host: string;\n /**\n * Resolved provider for this host — canonical lookup for the three\n * known hosts, explicit yml hint for anything else. Carried into\n * the failure message so `formatMissingCredentialsError` can render\n * the right setup block without re-resolving.\n */\n provider: RepoProvider;\n /** 'ok' when username+password came back from `git credential fill`. */\n status: 'ok' | 'no-credentials' | 'spawn-error' | 'non-zero-exit';\n /** Diagnostic text — empty when status is 'ok'. */\n detail: string;\n}\n\nexport interface CollectCredentialsResult {\n /** Hosts for which credentials were successfully written. */\n hostsWritten: number;\n /** Hosts for which `git credential fill` failed or returned no creds. */\n hostsSkipped: number;\n /** Per-host status (in input order). */\n perHost: HostCredentialStatus[];\n /** Absolute path to the written credentials file (always written, possibly empty). */\n credentialsPath: string;\n}\n\n/**\n * For each unique HTTPS host across the dev-container's repos, ask the\n * host-side git for credentials and write them to\n * `<devContainerRoot>/.monoceros/git-credentials`. The container's\n * post-create.sh configures git to read from that file via `store`\n * credential helper.\n *\n * Host-side `git credential fill` consults whatever helper the host\n * has configured (osxkeychain on macOS, manager on Windows, libsecret\n * on Linux). If a helper has the cached credentials, returns silent.\n * If not, the helper prompts the builder via its native UI\n * (Keychain-popup, GCM-window, terminal-prompt). That's the intended\n * UX — Monoceros never prompts directly, the host's helper does.\n *\n * Always writes the file (possibly empty) so the bind-mount target\n * exists in the container. A host that returns no credentials simply\n * yields a credentials file with no matching entries, and the in-\n * container `git clone` falls back to whatever default git would do\n * (which is to prompt — and there we lose, but the diagnostic is\n * clear).\n */\nexport async function collectGitCredentials(\n devContainerRoot: string,\n hosts: readonly HostWithProvider[],\n options: CollectCredentialsOptions = {},\n): Promise<CollectCredentialsResult> {\n const credsDir = path.join(devContainerRoot, '.monoceros');\n const credentialsPath = path.join(credsDir, 'git-credentials');\n\n const spawnFn = options.spawn ?? realGitCredentialFill;\n const logger = options.logger ?? { info: () => {}, warn: () => {} };\n\n // Callers must filter out 'unknown' providers before invoking this\n // function — those should fail the apply pre-flight earlier with a\n // \"set provider:\" error, never reach the credential helper. We\n // narrow the type here for the renderer's sake.\n const lines: string[] = [];\n const perHost: HostCredentialStatus[] = [];\n for (const { host, provider } of hosts) {\n if (provider === 'unknown') {\n // Defensive: should not happen — pre-flight is supposed to\n // bail before this. Record it anyway with no-credentials so\n // the caller doesn't see a partial success.\n perHost.push({\n host,\n provider: 'github', // placeholder — never rendered because pre-flight already bailed\n status: 'no-credentials',\n detail: 'provider not declared (internal: should not reach here)',\n });\n continue;\n }\n logger.info(`Fetching credentials for ${host} from host git…`);\n const input = `protocol=https\\nhost=${host}\\n\\n`;\n let result;\n try {\n result = await spawnFn(input);\n } catch (err) {\n // No logger.warn here — the caller (apply pre-flight) renders\n // a consolidated, provider-specific error message per failing\n // host. A separate WARN line per host would just add visual\n // noise above the actionable error.\n const detail = err instanceof Error ? err.message : String(err);\n perHost.push({ host, provider, status: 'spawn-error', detail });\n continue;\n }\n if (result.exitCode !== 0) {\n perHost.push({\n host,\n provider,\n status: 'non-zero-exit',\n detail: `exit code ${result.exitCode}`,\n });\n continue;\n }\n const { username, password } = parseCredentialFillOutput(result.stdout);\n if (!username || !password) {\n perHost.push({\n host,\n provider,\n status: 'no-credentials',\n detail: 'host credential helper returned no username/password',\n });\n continue;\n }\n lines.push(formatCredentialLine(host, username, password));\n perHost.push({ host, provider, status: 'ok', detail: '' });\n }\n\n await fs.mkdir(credsDir, { recursive: true });\n await fs.writeFile(\n credentialsPath,\n lines.join('\\n') + (lines.length > 0 ? '\\n' : ''),\n {\n mode: 0o600,\n },\n );\n\n return {\n hostsWritten: lines.length,\n hostsSkipped: perHost.filter((p) => p.status !== 'ok').length,\n perHost,\n credentialsPath,\n };\n}\n\n/**\n * Expose `uniqueHttpsHosts` for callers that need the host list\n * directly (apply uses it to build the pre-flight check input).\n */\nexport { uniqueHttpsHosts };\n\n/**\n * Build the multi-host pre-flight error message that gets thrown when\n * apply discovers missing credentials. Header inlines the provider\n * for single-host cases; body is left-aligned setup instructions.\n *\n * Format:\n *\n * Missing Git credentials: <host> — <Provider>\n *\n * <setup instructions, left-aligned, multi-line>\n *\n * Then re-run `monoceros apply`.\n *\n * For multi-host failures, each block is separated by a blank line\n * and gets its own provider title.\n */\nexport function formatMissingCredentialsError(\n missing: readonly HostCredentialStatus[],\n): string {\n if (missing.length === 1) {\n const m = missing[0]!;\n const hint = providerSetupHint(m.host, m.provider);\n return [\n `Missing Git credentials: ${hint.title}`,\n '',\n hint.body,\n '',\n `Then re-run ${cyan('monoceros apply')}.`,\n ].join('\\n');\n }\n const lines: string[] = [\n `Missing Git credentials for ${missing.length} hosts:`,\n '',\n ];\n for (const m of missing) {\n const hint = providerSetupHint(m.host, m.provider);\n lines.push(hint.title);\n lines.push('');\n lines.push(hint.body);\n lines.push('');\n }\n lines.push(`Then re-run ${cyan('monoceros apply')}.`);\n return lines.join('\\n');\n}\n\n/**\n * Build the pre-flight error for repos whose host has no provider\n * declared and isn't one of the canonical ones (github.com /\n * gitlab.com / bitbucket.org). The builder needs to add a\n * `provider:` field to the yml before apply can continue.\n */\nexport function formatUnknownProviderError(hosts: readonly string[]): string {\n const sorted = [...new Set(hosts)].sort();\n const lines: string[] = [\n sorted.length === 1\n ? `Unknown Git provider for host ${sorted[0]!}.`\n : `Unknown Git provider for ${sorted.length} hosts: ${sorted.join(', ')}.`,\n '',\n 'Monoceros auto-detects only github.com / gitlab.com / bitbucket.org.',\n 'For any other host (self-hosted GitLab, Gitea, Bitbucket Server, …)',\n 'declare the provider explicitly in the yml. Edit the repo entry:',\n '',\n cyan(' repos:'),\n cyan(` - url: https://${sorted[0]!}/…`),\n cyan(' provider: gitlab # or: github, bitbucket, gitea'),\n '',\n `Or re-add with ${cyan('monoceros add-repo <name> <url> --provider=<github|gitlab|bitbucket|gitea>')}.`,\n ];\n return lines.join('\\n');\n}\n\n// Exported for tests.\nexport const _internals = {\n uniqueHttpsHosts,\n parseCredentialFillOutput,\n formatCredentialLine,\n};\n","import { spawn } from 'node:child_process';\nimport { cyan } from '../util/format.js';\n\n/**\n * Apply pre-flight stage 2: after credentials have been collected,\n * verify host-side that each declared repo URL actually resolves and\n * the stored credentials can read from it.\n *\n * The idea: `git ls-remote <url>` is a one-roundtrip probe that\n * exercises exactly the same auth path as the in-container `git clone`\n * would. If it succeeds host-side, the clone inside the container\n * will succeed too (we write the same credentials into\n * `.monoceros/git-credentials`). If it fails — repo doesn't exist,\n * token is wrong, host unreachable — we surface a per-repo error\n * BEFORE the docker build runs, saving 1–2 min of build time on\n * first apply and avoiding a noisy devcontainer-cli stack trace\n * for what's really just a typo in the URL or a missing token scope.\n *\n * This runs AFTER the credential pre-flight (`collectGitCredentials`).\n * Order matters: a missing-creds error wants a provider-specific\n * setup hint (gh / glab / Atlassian token), a present-but-wrong-creds\n * error wants a \"regenerate / fix scope\" hint. The stage-1 check\n * catches the first; this stage-2 check catches the rest.\n */\n\n/**\n * Spawn signature for `git ls-remote <url>`. Returns stdout+stderr\n * plus exit code. Injected by tests. stdout is empty on success\n * (we don't care about the ref list, just whether the call worked).\n */\nexport type ReachabilitySpawn = (url: string) => Promise<{\n stdout: string;\n stderr: string;\n exitCode: number;\n}>;\n\nconst realGitLsRemote: ReachabilitySpawn = (url) => {\n return new Promise((resolve, reject) => {\n // Same env-var hardening as credentials.ts: prevent any kind of\n // interactive prompt (terminal, GUI askpass) so the pre-flight\n // never hangs and always returns a useful exit code + stderr.\n const child = spawn('git', ['ls-remote', '--heads', '--', url], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: '',\n SSH_ASKPASS: '',\n },\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) =>\n resolve({ stdout, stderr, exitCode: code ?? 0 }),\n );\n });\n};\n\n/**\n * Categorization of a reachability failure. Drives the per-host hint\n * in the consolidated error message. Patterns were observed across\n * GitHub, GitLab, and Bitbucket Cloud responses to non-existent /\n * unauthorized repos. We err on the side of grouping rather than\n * fine-grained kinds — the actionable advice is largely the same\n * within each kind.\n */\nexport type ReachabilityFailureKind =\n | 'not-found-or-no-access'\n | 'auth-failed'\n | 'dns'\n | 'unknown';\n\nexport interface RepoReachabilityStatus {\n url: string;\n ok: boolean;\n kind?: ReachabilityFailureKind;\n /** Raw stderr from git, trimmed. Empty when ok. */\n detail: string;\n}\n\n/**\n * Classify a non-zero `git ls-remote` failure by stderr content.\n * Patterns are matched case-insensitively against the union of\n * substrings each provider tends to emit. Order matters: DNS errors\n * sometimes also produce \"Authentication failed\" follow-up lines on\n * some platforms, so we check DNS first.\n */\nfunction classifyStderr(stderr: string): ReachabilityFailureKind {\n const s = stderr.toLowerCase();\n if (\n s.includes('could not resolve host') ||\n s.includes('name or service not known') ||\n s.includes('temporary failure in name resolution') ||\n s.includes('no address associated with hostname')\n ) {\n return 'dns';\n }\n if (\n s.includes('repository not found') ||\n s.includes('may not have access') ||\n s.includes('no longer exists') ||\n s.includes(\"don't have permission\") ||\n s.includes('could not be found') ||\n s.includes('the requested url returned error: 404')\n ) {\n return 'not-found-or-no-access';\n }\n if (\n s.includes('authentication failed') ||\n s.includes('could not read username') ||\n s.includes('incorrect username or password') ||\n s.includes('the requested url returned error: 401') ||\n s.includes('the requested url returned error: 403')\n ) {\n return 'auth-failed';\n }\n return 'unknown';\n}\n\n/**\n * Probe each declared repo URL via host-side `git ls-remote`. Runs\n * sequentially (not parallel) so the output order matches the yml\n * order — easier to reason about when multiple repos fail, and the\n * total time is bounded by ~200 ms per repo against typical SaaS\n * hosts. Spawn-injected for tests.\n */\nexport async function checkRepoReachability(\n repos: readonly { url: string }[],\n options: { spawn?: ReachabilitySpawn } = {},\n): Promise<RepoReachabilityStatus[]> {\n const spawnFn = options.spawn ?? realGitLsRemote;\n const results: RepoReachabilityStatus[] = [];\n for (const repo of repos) {\n // Only HTTPS URLs reach this code path (schema enforces it; pre-\n // flight already filtered). Skip belt-and-suspenders is in\n // credentials.ts — here we trust the input.\n let result: Awaited<ReturnType<ReachabilitySpawn>>;\n try {\n result = await spawnFn(repo.url);\n } catch (err) {\n results.push({\n url: repo.url,\n ok: false,\n kind: 'unknown',\n detail: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n if (result.exitCode === 0) {\n results.push({ url: repo.url, ok: true, detail: '' });\n continue;\n }\n results.push({\n url: repo.url,\n ok: false,\n kind: classifyStderr(result.stderr),\n detail: result.stderr.trim(),\n });\n }\n return results;\n}\n\n/**\n * Render the consolidated pre-flight error for repos that couldn't\n * be reached. Groups failures by kind so each kind's actionable\n * advice appears once, with the failing URLs listed underneath.\n *\n * Layout:\n *\n * Cannot reach <N> declared repo(s):\n *\n * Repository not found (or your credentials don't grant access):\n * • https://...\n * • https://...\n * - <actionable advice>\n * - <actionable advice>\n *\n * Authentication failed:\n * • https://...\n * - <actionable advice>\n *\n * Then re-run `monoceros apply`.\n */\nexport function formatUnreachableReposError(\n failures: readonly RepoReachabilityStatus[],\n): string {\n const byKind = new Map<ReachabilityFailureKind, RepoReachabilityStatus[]>();\n for (const f of failures) {\n const kind = f.kind ?? 'unknown';\n const list = byKind.get(kind) ?? [];\n list.push(f);\n byKind.set(kind, list);\n }\n const totalUrls = failures.length;\n const lines: string[] = [\n totalUrls === 1\n ? `Cannot reach declared repo: ${failures[0]!.url}`\n : `Cannot reach ${totalUrls} declared repos:`,\n '',\n ];\n\n const sectionOrder: ReachabilityFailureKind[] = [\n 'not-found-or-no-access',\n 'auth-failed',\n 'dns',\n 'unknown',\n ];\n for (const kind of sectionOrder) {\n const entries = byKind.get(kind);\n if (!entries || entries.length === 0) continue;\n lines.push(headerForKind(kind));\n for (const e of entries) {\n lines.push(` • ${e.url}`);\n }\n for (const advice of adviceForKind(kind)) {\n lines.push(` - ${advice}`);\n }\n lines.push('');\n }\n lines.push(`Then re-run ${cyan('monoceros apply')}.`);\n return lines.join('\\n');\n}\n\nfunction headerForKind(kind: ReachabilityFailureKind): string {\n switch (kind) {\n case 'not-found-or-no-access':\n return \"Repository not found (or your credentials don't grant access):\";\n case 'auth-failed':\n return 'Authentication failed (credentials are present but rejected):';\n case 'dns':\n return \"Host unreachable (DNS / VPN / offline — git couldn't resolve the hostname):\";\n case 'unknown':\n return 'Unrecognised git error:';\n }\n}\n\nfunction adviceForKind(kind: ReachabilityFailureKind): string[] {\n switch (kind) {\n case 'not-found-or-no-access':\n return [\n 'Re-check the URL for typos (case-sensitive on most hosts).',\n 'Verify the repo still exists / is not archived in a way that hides it.',\n 'Ensure your token covers this org / workspace and has read scope (GitHub: `repo`; GitLab: `read_repository`; Bitbucket: repo read).',\n ];\n case 'auth-failed':\n return [\n 'Token may be expired or revoked — regenerate it from the provider UI.',\n 'Re-run the provider CLI login (gh auth login / glab auth login) — Monoceros picks up the refreshed token on the next apply.',\n ];\n case 'dns':\n return [\n 'Check your internet / VPN — corporate Git hosts often require VPN.',\n 'Verify the hostname spelling in the yml.',\n ];\n case 'unknown':\n return [\n 'Run `git ls-remote <url>` manually on the host to see the raw error.',\n ];\n }\n}\n","import { spawn } from 'node:child_process';\n\n/**\n * Whether the host's docker daemon runs as root (rootful) or under a\n * user namespace (rootless). The mode decides whether bind-mounts\n * need the `idmap` option.\n *\n * Why this matters: in rootless Docker the host user's UID is mapped\n * to container UID 0 (root), and the container's non-root user\n * (uid 1000 inside) lives at a shifted host UID (uid 65536+1000+ via\n * /etc/subuid). Without `idmap`, files written by either side end up\n * with the wrong ownership on the other — host can't read what the\n * container created, container's node user can't write into what the\n * host pre-created (which is what bit M5 testing on Ubuntu rootless).\n *\n * The Linux kernel supports `idmap` as a bind-mount option since 5.12;\n * Docker exposes it since 25.x. Ubuntu 24.04 and other modern distros\n * are well past both. Older kernels (RHEL 8 with 4.18) would fail the\n * mount with an \"unsupported\" error — accepted trade-off, the error\n * surfaces clearly.\n *\n * On macOS / Windows Docker Desktop, idmap is a no-op at best and a\n * mount-error at worst because those platforms use their own\n * file-sharing layer (VirtioFS / WSL2 + Plan9) instead of native\n * Linux bind mounts. We MUST only emit idmap when the daemon is\n * actually rootless on Linux — otherwise we'd break the working\n * Mac/Windows cases.\n */\nexport type DockerMode = 'rootful' | 'rootless';\n\n/**\n * Spawn signature for `docker info`. Returns stdout + exit code.\n * Injected by tests.\n */\nexport type DockerInfoSpawn = () => Promise<{\n stdout: string;\n exitCode: number;\n}>;\n\nconst realDockerInfo: DockerInfoSpawn = () => {\n return new Promise((resolve, reject) => {\n // `--format '{{json .SecurityOptions}}'` returns a JSON array like\n // `[\"name=seccomp,profile=builtin\",\"name=rootless\"]` on rootless,\n // or `[\"name=seccomp,profile=builtin\"]` on rootful. Cheaper to\n // parse than the full human-readable `docker info` output.\n const child = spawn(\n 'docker',\n ['info', '--format', '{{json .SecurityOptions}}'],\n {\n stdio: ['ignore', 'pipe', 'inherit'],\n },\n );\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve({ stdout, exitCode: code ?? 0 }));\n });\n};\n\n/**\n * Probe the host docker daemon and return its mode. Defaults to\n * `'rootful'` whenever we can't reliably determine otherwise — the\n * downstream `docker run` would surface a clearer error if the\n * daemon is unreachable, so we don't pre-emptively fail here.\n */\nexport async function detectDockerMode(\n options: { spawn?: DockerInfoSpawn } = {},\n): Promise<DockerMode> {\n const spawnFn = options.spawn ?? realDockerInfo;\n try {\n const result = await spawnFn();\n if (result.exitCode !== 0) return 'rootful';\n // Match both the bare `rootless` token and the modern\n // `name=rootless` form. Case-insensitive to be defensive against\n // future docker output tweaks.\n return /\\brootless\\b/i.test(result.stdout) ? 'rootless' : 'rootful';\n } catch {\n return 'rootful';\n }\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\n\n/**\n * Spawn signature for `git config --global --get <key>`: takes the\n * key, returns stdout (trimmed) and exit code. Exit code 1 with empty\n * stdout means \"no value set\" — that's how git signals an unset key.\n * Injected by tests.\n */\nexport type IdentitySpawn = (\n key: string,\n) => Promise<{ value: string; exitCode: number }>;\n\n/**\n * Async prompt for a single identity key. Used as a fallback when the\n * host has no `--global` identity and `.monoceros/gitconfig` has no\n * persisted value from an earlier run. Returns the entered value or\n * `undefined` if the builder skips.\n */\nexport type IdentityPrompt = (\n key: 'user.name' | 'user.email',\n) => Promise<string | undefined>;\n\nconst realGitConfigGet: IdentitySpawn = (key) => {\n return new Promise((resolve, reject) => {\n const child = spawn('git', ['config', '--global', '--get', key], {\n stdio: ['ignore', 'pipe', 'inherit'],\n });\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) =>\n resolve({ value: stdout.trim(), exitCode: code ?? 0 }),\n );\n });\n};\n\nconst realIdentityPrompt: IdentityPrompt = async (key) => {\n // Non-interactive (CI, scripts): never hang waiting for input. The\n // identity stays unset; builder fixes it later by setting host\n // `git config --global` or editing `.monoceros/gitconfig` directly.\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n return undefined;\n }\n const label =\n key === 'user.name'\n ? 'Git user.name for this dev container (full name)'\n : 'Git user.email for this dev container';\n const value = await consola.prompt(`${label}:`, { type: 'text' });\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\nexport interface CollectIdentityOptions {\n spawn?: IdentitySpawn;\n /**\n * Fallback prompt when the host has no `--global` identity and\n * `.monoceros/gitconfig` has no persisted value either. Tests inject\n * a canned answer; production uses an interactive `consola.prompt`\n * that auto-skips in non-interactive contexts.\n */\n prompt?: IdentityPrompt;\n /**\n * Per-container override from the container's yml `git.user`. Wins\n * over everything else (host global, workbench-wide defaults,\n * persisted state, interactive prompt).\n */\n containerOverride?: { name?: string; email?: string };\n /**\n * Workbench-wide defaults from `<MONOCEROS_HOME>/monoceros-config.yml`\n * `defaults.git.user`. Wins over host global git config (the\n * monoceros-config.yml is an explicit builder choice for Monoceros\n * containers; host global is the catch-all default), loses to the\n * per-container override.\n */\n defaults?: { name?: string; email?: string };\n logger?: { info: (msg: string) => void; warn: (msg: string) => void };\n}\n\nexport interface CollectIdentityResult {\n name?: string;\n email?: string;\n gitconfigPath: string;\n}\n\n/**\n * Extract `user.name` and `user.email` from the host's global git\n * config, write them as `<devContainerRoot>/.monoceros/gitconfig` for\n * the container to include. Done both at `monoceros create` time (so\n * the first `start` has identity) and at every `monoceros apply` (so\n * host changes propagate in).\n *\n * Always writes the file, even when host has nothing set — keeps the\n * include.path target valid (git silently ignores missing files, but\n * present-but-empty is more deterministic).\n *\n * Returns the captured values; the caller can use them for logging.\n * Missing values surface as `undefined`, plus a warn log line.\n */\nexport async function collectGitIdentity(\n devContainerRoot: string,\n options: CollectIdentityOptions = {},\n): Promise<CollectIdentityResult> {\n const gitconfigDir = path.join(devContainerRoot, '.monoceros');\n const gitconfigPath = path.join(gitconfigDir, 'gitconfig');\n const spawnFn = options.spawn ?? realGitConfigGet;\n const promptFn = options.prompt ?? realIdentityPrompt;\n const logger = options.logger ?? { info: () => {}, warn: () => {} };\n\n const existing = await readExistingGitconfig(gitconfigPath);\n\n // Resolution order per key:\n // 1. containerOverride (yml's `git.user`)\n // 2. defaults (monoceros-config.yml's `defaults.git.user`)\n // 3. host `git config --global --get <key>`\n // 4. previously persisted value (.monoceros/gitconfig)\n // 5. interactive prompt (skipped in non-TTY contexts)\n const name = await resolveKey('user.name', {\n override: options.containerOverride?.name,\n defaultValue: options.defaults?.name,\n spawnFn,\n persistedValue: existing.name,\n promptFn,\n logger,\n });\n const email = await resolveKey('user.email', {\n override: options.containerOverride?.email,\n defaultValue: options.defaults?.email,\n spawnFn,\n persistedValue: existing.email,\n promptFn,\n logger,\n });\n\n const lines: string[] = ['[user]'];\n if (name !== undefined) lines.push(`\\tname = ${name}`);\n if (email !== undefined) lines.push(`\\temail = ${email}`);\n\n await fs.mkdir(gitconfigDir, { recursive: true });\n await fs.writeFile(gitconfigPath, lines.join('\\n') + '\\n');\n\n return {\n ...(name !== undefined ? { name } : {}),\n ...(email !== undefined ? { email } : {}),\n gitconfigPath,\n };\n}\n\ninterface ResolveKeyOpts {\n override?: string;\n defaultValue?: string;\n spawnFn: IdentitySpawn;\n persistedValue?: string;\n promptFn: IdentityPrompt;\n logger: { warn: (msg: string) => void };\n}\n\nasync function resolveKey(\n key: 'user.name' | 'user.email',\n opts: ResolveKeyOpts,\n): Promise<string | undefined> {\n if (opts.override !== undefined && opts.override.length > 0) {\n return opts.override;\n }\n if (opts.defaultValue !== undefined && opts.defaultValue.length > 0) {\n return opts.defaultValue;\n }\n const hostValue = await readKeyFromHost(opts.spawnFn, key, opts.logger);\n if (hostValue !== undefined) return hostValue;\n if (opts.persistedValue !== undefined && opts.persistedValue.length > 0) {\n return opts.persistedValue;\n }\n const prompted = await opts.promptFn(key);\n if (prompted !== undefined) return prompted;\n opts.logger.warn(\n `No ${key} resolvable (yml override, monoceros-config.yml defaults, host \\`git config --global\\`, persisted .monoceros/gitconfig, prompt). Container git will have no ${key} until set explicitly.`,\n );\n return undefined;\n}\n\nasync function readKeyFromHost(\n spawnFn: IdentitySpawn,\n key: string,\n logger: { warn: (msg: string) => void },\n): Promise<string | undefined> {\n try {\n const result = await spawnFn(key);\n if (result.exitCode === 0 && result.value.length > 0) {\n return result.value;\n }\n return undefined;\n } catch (err) {\n logger.warn(\n `Host git not runnable (${err instanceof Error ? err.message : String(err)}); identity not captured.`,\n );\n return undefined;\n }\n}\n\nasync function readExistingGitconfig(\n filePath: string,\n): Promise<{ name?: string; email?: string }> {\n try {\n const content = await fs.readFile(filePath, 'utf8');\n const result: { name?: string; email?: string } = {};\n const nameMatch = /^\\s*name\\s*=\\s*(.+?)\\s*$/m.exec(content);\n const emailMatch = /^\\s*email\\s*=\\s*(.+?)\\s*$/m.exec(content);\n if (nameMatch?.[1]) result.name = nameMatch[1];\n if (emailMatch?.[1]) result.email = emailMatch[1];\n return result;\n } catch {\n return {};\n }\n}\n","// Single source of truth for the CLI version. Kept in sync with\n// `packages/cli/package.json` by hand — the release-cli workflow\n// reads the value from `package.json` and refuses to publish if the\n// two are out of step, so a manual desync gets caught loudly.\nexport const CLI_VERSION = '1.5.0';\n","import { consola } from 'consola';\n\n// Shared exit-code dispatcher: runs the orchestrator, propagates its\n// exit code, and turns thrown errors into a clean console message + exit 1.\nexport async function dispatch(runner: () => Promise<number>): Promise<never> {\n try {\n const exitCode = await runner();\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n","import { defineCommand } from 'citty';\n\n/**\n * `monoceros completion <shell>` — prints a shell-completion script\n * for bash or zsh to stdout. The user redirects it into a file their\n * shell loads at startup.\n *\n * Both scripts complete:\n * - subcommand names at position 1\n * - container names (read from `<MONOCEROS_HOME>/container-configs/`)\n * for the second positional of every command that takes a\n * `<NAME>` argument referring to an existing container — i.e.\n * everything *except* `init` (which expects a fresh name) and the\n * verb-only commands like `list-components` / `completion`.\n *\n * MONOCEROS_HOME respects the same precedence as the CLI itself: env\n * var first, then `$HOME/.monoceros`. Container-name completion in\n * the workbench-checkout dev environment looks at the env var if set;\n * otherwise it falls back to `~/.monoceros/`, which matches the\n * global-install case. A contributor who wants dev-container names\n * completed sets `MONOCEROS_HOME=$PWD/.local` in their shell.\n *\n * Install:\n * bash: monoceros completion bash > ~/.bash_completion.d/monoceros\n * (or any path your shell sources; `source` it from .bashrc)\n * zsh: monoceros completion zsh > \"${fpath[1]}/_monoceros\"\n * (after ensuring compinit is active)\n */\n\n// Keep these arrays in sync with main.ts. Single source of truth\n// would be nice but adds startup cost — citty's subCommands aren't\n// trivial to enumerate from a static context. Tests guard the\n// list in completion.test.ts.\nconst ALL_COMMANDS = [\n 'init',\n 'list-components',\n 'shell',\n 'run',\n 'logs',\n 'start',\n 'stop',\n 'status',\n 'apply',\n 'remove',\n 'restore',\n 'add-service',\n 'add-language',\n 'add-apt-packages',\n 'add-feature',\n 'add-from-url',\n 'add-repo',\n 'remove-service',\n 'remove-language',\n 'remove-apt-packages',\n 'remove-feature',\n 'remove-from-url',\n 'remove-repo',\n 'completion',\n] as const;\n\n// Commands whose first positional is an existing container name.\n// Everything else either takes no positional (`list-components`,\n// `completion`) or expects a fresh name (`init`, `restore`).\nconst COMMANDS_WITH_CONTAINER_ARG = [\n 'shell',\n 'run',\n 'logs',\n 'start',\n 'stop',\n 'status',\n 'apply',\n 'remove',\n 'add-service',\n 'add-language',\n 'add-apt-packages',\n 'add-feature',\n 'add-from-url',\n 'add-repo',\n 'remove-service',\n 'remove-language',\n 'remove-apt-packages',\n 'remove-feature',\n 'remove-from-url',\n 'remove-repo',\n] as const;\n\nconst SHELLS = ['bash', 'zsh', 'pwsh'] as const;\ntype Shell = (typeof SHELLS)[number];\n\nexport function renderCompletionScript(shell: Shell): string {\n const commands = ALL_COMMANDS.join(' ');\n const containerCommandsRegex = COMMANDS_WITH_CONTAINER_ARG.join('|');\n\n if (shell === 'bash') {\n return [\n '# bash completion for monoceros',\n '# install: source this file from .bashrc, e.g.',\n '# monoceros completion bash > ~/.bash_completion.d/monoceros',\n '# echo \"source ~/.bash_completion.d/monoceros\" >> ~/.bashrc',\n '',\n '_monoceros() {',\n ' local cur prev cmd home configs_dir names',\n ' cur=\"${COMP_WORDS[COMP_CWORD]}\"',\n '',\n ' if [[ $COMP_CWORD -eq 1 ]]; then',\n ` COMPREPLY=( $(compgen -W \"${commands}\" -- \"$cur\") )`,\n ' return',\n ' fi',\n '',\n ' cmd=\"${COMP_WORDS[1]}\"',\n ' if [[ $COMP_CWORD -eq 2 ]]; then',\n ' case \"$cmd\" in',\n ` ${containerCommandsRegex})`,\n ' home=\"${MONOCEROS_HOME:-$HOME/.monoceros}\"',\n ' configs_dir=\"$home/container-configs\"',\n ' if [[ -d \"$configs_dir\" ]]; then',\n ` names=$(cd \"$configs_dir\" && ls *.yml 2>/dev/null | sed 's/\\\\.yml$//')`,\n ' COMPREPLY=( $(compgen -W \"$names\" -- \"$cur\") )',\n ' fi',\n ' ;;',\n ' completion)',\n ` COMPREPLY=( $(compgen -W \"${SHELLS.join(' ')}\" -- \"$cur\") )`,\n ' ;;',\n ' esac',\n ' fi',\n '}',\n 'complete -F _monoceros monoceros',\n '',\n ].join('\\n');\n }\n\n if (shell === 'pwsh') {\n return [\n '# PowerShell completion for monoceros',\n '# install: dot-source this file from your $PROFILE, e.g.',\n '# monoceros completion pwsh > $HOME/.config/monoceros/completion.ps1',\n \"# Add-Content $PROFILE '. $HOME/.config/monoceros/completion.ps1'\",\n '',\n 'Register-ArgumentCompleter -Native -CommandName monoceros -ScriptBlock {',\n ' param($wordToComplete, $commandAst, $cursorPosition)',\n '',\n ' $commands = @(',\n ...ALL_COMMANDS.map((c) => ` '${c}'`),\n ' )',\n ` $shells = @('${SHELLS.join(\"', '\")}')`,\n ' $containerCommands = @(',\n ...COMMANDS_WITH_CONTAINER_ARG.map((c) => ` '${c}'`),\n ' )',\n '',\n ' $tokens = $commandAst.CommandElements',\n ' $position = $tokens.Count',\n ' if ($wordToComplete) { $position-- }',\n '',\n ' if ($position -eq 1) {',\n ' $commands | Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' return',\n ' }',\n '',\n ' if ($position -eq 2) {',\n ' $cmd = $tokens[1].Value',\n ' if ($containerCommands -contains $cmd) {',\n ' $home = if ($env:MONOCEROS_HOME) { $env:MONOCEROS_HOME } else { Join-Path $env:USERPROFILE \".monoceros\" }',\n ' $configsDir = Join-Path $home \"container-configs\"',\n ' if (Test-Path $configsDir) {',\n ' Get-ChildItem -Path $configsDir -Filter \"*.yml\" |',\n ' ForEach-Object { $_.BaseName } |',\n ' Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' }',\n ' } elseif ($cmd -eq \"completion\") {',\n ' $shells | Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' }',\n ' }',\n '}',\n '',\n ].join('\\n');\n }\n\n // zsh\n return [\n '#compdef monoceros',\n '# zsh completion for monoceros',\n '# install: drop this file somewhere on your $fpath as `_monoceros`,',\n '# then start a new shell (or run `compinit`). Example:',\n '# monoceros completion zsh > \"${fpath[1]}/_monoceros\"',\n '',\n '_monoceros() {',\n ' local -a commands shells',\n ' commands=(',\n ...ALL_COMMANDS.map((c) => ` '${c}'`),\n ' )',\n ` shells=(${SHELLS.map((s) => `'${s}'`).join(' ')})`,\n '',\n ' if (( CURRENT == 2 )); then',\n \" _describe 'monoceros command' commands\",\n ' return',\n ' fi',\n '',\n ' local cmd=${words[2]}',\n ' if (( CURRENT == 3 )); then',\n ' case $cmd in',\n ` ${containerCommandsRegex})`,\n ' local home=\"${MONOCEROS_HOME:-$HOME/.monoceros}\"',\n ' local configs_dir=\"$home/container-configs\"',\n ' if [[ -d $configs_dir ]]; then',\n ' local -a names',\n ' names=(${configs_dir}/*.yml(N:t:r))',\n \" _describe 'container' names\",\n ' fi',\n ' ;;',\n ' completion)',\n \" _describe 'shell' shells\",\n ' ;;',\n ' esac',\n ' fi',\n '}',\n '',\n '_monoceros \"$@\"',\n '',\n ].join('\\n');\n}\n\nexport const completionCommand = defineCommand({\n meta: {\n name: 'completion',\n group: 'tooling',\n description:\n 'Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically.',\n },\n args: {\n shell: {\n type: 'positional',\n description: \"Target shell. One of: 'bash', 'zsh', 'pwsh'.\",\n required: true,\n },\n },\n run({ args }) {\n const shell = args.shell as string;\n if (shell !== 'bash' && shell !== 'zsh' && shell !== 'pwsh') {\n process.stderr.write(\n `Unknown shell: ${JSON.stringify(shell)}. Supported: ${SHELLS.join(', ')}.\\n`,\n );\n process.exit(2);\n }\n process.stdout.write(renderCompletionScript(shell));\n },\n});\n\n// Exposed for tests so the static command list stays in sync with\n// what main.ts wires up.\nexport const COMPLETION_COMMANDS_FOR_TEST = ALL_COMMANDS;\nexport const COMPLETION_CONTAINER_COMMANDS_FOR_TEST =\n COMMANDS_WITH_CONTAINER_ARG;\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runInit } from '../init/index.js';\n\nexport const initCommand = defineCommand({\n meta: {\n name: 'init',\n group: 'lifecycle',\n description:\n 'Create a fresh container-config yml at .local/container-configs/<name>.yml. Without --with, the file is a documented default with every component commented out. With --with=<names>, the named components are composed into an active, immediately-applyable yml. Then run `monoceros apply <name>`.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Config name. The yml lands at <MONOCEROS_HOME>/container-configs/<name>.yml and becomes the source-of-truth for `monoceros apply <name>`.',\n required: true,\n },\n with: {\n type: 'string',\n description:\n \"Comma-separated list of component names to compose, e.g. 'node,postgres,github,claude'. Sub-components use a slash, e.g. 'atlassian/twg'. When omitted, init writes a documented default with every catalog component commented out.\",\n required: false,\n },\n 'with-repo': {\n type: 'string',\n description:\n 'Git URL of a repo to clone into projects/ on first apply. Repeatable: pass --with-repo=URL1 --with-repo=URL2 for multiple repos. Folder name derived from URL (foo.git → projects/foo/); use `monoceros add-repo --path=...` post-init for subfolder paths.',\n required: false,\n },\n },\n async run({ args, rawArgs }) {\n try {\n const withList = collectWithList(args.with, rawArgs);\n const withRepoList = collectWithRepoList(rawArgs);\n await runInit({\n name: args.name,\n ...(withList ? { with: withList } : {}),\n ...(withRepoList.length > 0 ? { withRepo: withRepoList } : {}),\n });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\n/**\n * Collect all `--with-repo=<url>` and `--with-repo <url>` tokens from\n * rawArgs. citty doesn't natively give us repeatable strings (the\n * single `args['with-repo']` only carries the last value), so we\n * scan the original argv. Returns URLs in order of appearance.\n */\nfunction collectWithRepoList(rawArgs: string[]): string[] {\n const urls: string[] = [];\n for (let i = 0; i < rawArgs.length; i += 1) {\n const t = rawArgs[i]!;\n if (t === '--with-repo') {\n const next = rawArgs[i + 1];\n if (typeof next === 'string' && !next.startsWith('-')) {\n urls.push(next);\n i += 1;\n }\n } else if (t.startsWith('--with-repo=')) {\n urls.push(t.slice('--with-repo='.length));\n }\n }\n return urls;\n}\n\n/**\n * Reconstruct the --with list from `args.with` plus any rawArgs\n * tokens that the shell tokenization split off.\n *\n * Background: a user writing\n * monoceros init dummy --with=a, b, c\n * gets shell-tokenized into argv entries:\n * ['init', 'dummy', '--with=a,', 'b,', 'c']\n * citty assigns `args.with = \"a,\"` and the rest float as extra\n * positionals that the `name` arg won't accept. To avoid forcing\n * the user to quote or remove the spaces, we look at rawArgs to\n * find the original --with token and pull in any subsequent non-\n * flag tokens until we hit something that looks like a flag or\n * run out. The collected pieces are joined back with commas and\n * re-split — same parser as before, but now seeing the full list.\n */\nfunction collectWithList(\n withArg: string | undefined,\n rawArgs: string[],\n): string[] | undefined {\n if (typeof withArg !== 'string' || withArg.trim().length === 0) {\n return undefined;\n }\n let combined = withArg.trim();\n // Find where --with starts in rawArgs, then keep eating non-flag\n // tokens. Both forms are supported by citty:\n // --with=value (combined in one token)\n // --with value (two tokens)\n const startIdx = rawArgs.findIndex(\n (t) => t === '--with' || t.startsWith('--with='),\n );\n if (startIdx >= 0) {\n // Skip the with token itself, plus its detached value when\n // `--with` was used in the two-token form.\n let scanFrom = startIdx + 1;\n if (rawArgs[startIdx] === '--with') scanFrom += 1;\n for (let i = scanFrom; i < rawArgs.length; i += 1) {\n const t = rawArgs[i]!;\n if (t.startsWith('--') || t === '-h' || t === '--help') break;\n // Re-join with a comma — the user separated with commas plus\n // (now-eaten) whitespace; comma alone is what our parser wants.\n const sep = combined.endsWith(',') ? '' : ',';\n combined += sep + t;\n }\n }\n const pieces = combined\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return pieces.length > 0 ? pieces : undefined;\n}\n","import { existsSync, promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerConfigsDir,\n monocerosHome as defaultMonocerosHome,\n workbenchRoot as defaultWorkbenchRoot,\n workbenchCheckoutRoot,\n componentsDir as defaultComponentsDir,\n prettyPath,\n} from '../config/paths.js';\nimport { KNOWN_PROVIDER_HOSTS, REGEX } from '../config/schema.js';\nimport { loadComponentCatalog, resolveComponents } from './components.js';\nimport { generateComposedYml, generateDocumentedYml } from './generator.js';\nimport { loadFeatureManifestSummary } from './manifest.js';\n\n/**\n * `monoceros init <name> [--with=<components>]` — produce a fresh\n * container-config yml at `<MONOCEROS_HOME>/container-configs/<name>.yml`.\n *\n * Two modes:\n *\n * - With `--with=node,postgres,github,claude` (or any comma-list\n * of component names from the catalog): the listed components\n * are merged and the result written as an active, immediately-\n * applyable yml. Per-feature option hints (auth/credentials\n * from the feature manifest) appear as commented lines next to\n * the active options so the builder can see what's available\n * without leaving the file.\n *\n * - Without `--with`: a documented-default yml is written. Every\n * section is commented out, every catalog component appears as\n * a suggestion with prose describing what it adds. Builder\n * un-comments what they want, then `monoceros apply <name>`.\n *\n * Errors loudly if:\n *\n * - the target config already exists (delete it first if you want\n * to start over — protects hand-edits)\n * - a `--with` name is not in the catalog (the error message\n * lists what *is* available)\n * - the chosen container name is shape-invalid\n */\n\nexport interface RunInitOptions {\n name: string;\n /**\n * Component names to compose. When empty/undefined → documented\n * mode (every component commented out). When set → composed mode\n * with exactly these components active.\n */\n with?: string[];\n /**\n * Git URLs to clone into `projects/` on the first apply. Each URL\n * lands at `projects/<URL-derived-leaf>/` (e.g.\n * `https://.../foo.git` → `projects/foo/`). For nested destination\n * paths (`projects/apps/web/`) use `monoceros add-repo --path=...`\n * post-init — the init flag intentionally keeps the syntax minimal.\n */\n withRepo?: string[];\n /** Override of the CLI-bundle root that holds `templates/components/`. */\n workbenchRoot?: string;\n /** Override of the user-data home that owns `container-configs/`. */\n monocerosHome?: string;\n logger?: {\n success: (msg: string) => void;\n info: (msg: string) => void;\n };\n}\n\nexport interface RunInitResult {\n configPath: string;\n /** True when the documented-default mode was used. */\n documented: boolean;\n}\n\nexport async function runInit(opts: RunInitOptions): Promise<RunInitResult> {\n const workbench = opts.workbenchRoot ?? defaultWorkbenchRoot();\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n success: (msg) => consola.success(msg),\n info: (msg) => consola.info(msg),\n };\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const dest = containerConfigPath(opts.name, home);\n if (existsSync(dest)) {\n throw new Error(\n `Config already exists: ${dest}. Delete it manually before re-running \\`monoceros init\\` — this protects any hand-edits.`,\n );\n }\n\n const catalog = await loadComponentCatalog(defaultComponentsDir(workbench));\n if (catalog.size === 0) {\n throw new Error(\n `No components found under ${defaultComponentsDir(workbench)}. The workbench checkout is incomplete.`,\n );\n }\n\n // Feature manifests live at the workbench-checkout root, not in\n // the CLI bundle. In tests the fixture sets `workbenchRoot` to a\n // dir that happens to hold both the templates *and* an\n // `images/features/` tree; honour that override. In real use we\n // fall back to `workbenchCheckoutRoot()` which returns null when\n // the CLI is run from an npm install — manifest lookups then\n // return undefined and init renders without optionHints.\n const checkoutRoot = opts.workbenchRoot ?? workbenchCheckoutRoot();\n const lookup = (ref: string) => loadFeatureManifestSummary(ref, checkoutRoot);\n\n // --with-repo URL validation: only canonical hosts. Non-canonical\n // hosts (self-hosted GitLab, Gitea, …) need `provider:` in the yml,\n // and init has no --provider flag, so the builder takes the\n // `monoceros init` + `monoceros add-repo … --provider=…` path\n // instead.\n // Dedupe input URLs (preserve insertion order) — same URL passed\n // twice from the CLI is a no-op, matching how `monoceros add-repo`\n // treats the second-add case.\n const reposRaw = (opts.withRepo ?? [])\n .map((u) => u.trim())\n .filter((u) => u.length > 0);\n const repos: string[] = [];\n const seenRepoUrls = new Set<string>();\n for (const url of reposRaw) {\n if (seenRepoUrls.has(url)) continue;\n seenRepoUrls.add(url);\n repos.push(url);\n }\n if (repos.length > 0) {\n const offending: string[] = [];\n for (const url of repos) {\n let host: string | undefined;\n try {\n host = url.startsWith('https://') ? new URL(url).hostname : undefined;\n } catch {\n host = undefined;\n }\n if (!host || !KNOWN_PROVIDER_HOSTS[host.toLowerCase()]) {\n offending.push(url);\n }\n }\n if (offending.length > 0) {\n throw new Error(\n [\n `--with-repo only supports github.com / gitlab.com / bitbucket.org URLs.`,\n `These are not canonical: ${offending.join(', ')}`,\n `For other hosts, run \\`monoceros init <name>\\` first, then`,\n `\\`monoceros add-repo <name> <url> --provider=github|gitlab|bitbucket\\`.`,\n ].join('\\n'),\n );\n }\n }\n\n // Both generators take the URL list directly — no AST round-trip\n // after the fact. That lets each generator decide how to render the\n // repos block (commented hints in documented mode, active entries\n // with commented per-entry hint lines in composed mode), keeping\n // the \"all available options visible\" rule consistent across\n // sections.\n let text: string;\n const requested = opts.with ?? [];\n if (requested.length === 0) {\n text = generateDocumentedYml(opts.name, catalog, lookup, repos);\n } else {\n const components = resolveComponents(catalog, requested);\n text = generateComposedYml(opts.name, components, lookup, repos);\n }\n\n await fs.mkdir(containerConfigsDir(home), { recursive: true });\n await fs.writeFile(dest, text, 'utf8');\n\n const documented = requested.length === 0;\n const displayPath = prettyPath(dest);\n if (documented) {\n logger.success(\n `Wrote documented default to ${displayPath}. Un-comment what you need, then \\`monoceros apply ${opts.name}\\`.`,\n );\n } else {\n logger.success(\n `Composed ${requested.length} component(s) into ${displayPath}: ${requested.join(', ')}`,\n );\n logger.info(\n `Edit the file if you need to tweak, then \\`monoceros apply ${opts.name}\\`.`,\n );\n }\n\n return { configPath: dest, documented };\n}\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { parse as parseYaml } from 'yaml';\nimport { componentsDir as defaultComponentsDir } from '../config/paths.js';\nimport { FeatureOptionValueSchema, REGEX } from '../config/schema.js';\n\n/**\n * Components catalog — small, composable yml snippets that\n * `monoceros init` can merge into a container config.\n *\n * Each file under `templates/components/` is one component:\n *\n * - `templates/components/node.yml` → component name `node`\n * - `templates/components/atlassian/twg.yml` → component name\n * `atlassian/twg`\n *\n * Sub-components live inside a directory whose name matches a parent\n * component (and which may itself have a top-level `<group>.yml`,\n * e.g. `atlassian.yml` for the \"both tools\" preset). The convention\n * is: a sub-component sets every sibling boolean option explicitly\n * (`true` for its own feature, `false` for the others), and the\n * merge applies OR-semantics on booleans so combining\n * `--with=atlassian/rovodev,atlassian/twg` correctly yields both\n * `true`. See `templates/components/README.md` for the full design.\n */\n\nconst CategorySchema = z.enum(['language', 'service', 'feature']);\nexport type ComponentCategory = z.infer<typeof CategorySchema>;\n\nconst FeatureContributionSchema = z.object({\n ref: z.string().regex(REGEX.featureRef),\n options: z.record(z.string(), FeatureOptionValueSchema).optional(),\n});\n\n/**\n * Shape validation for one component file. The contributes section is\n * deliberately narrow — exactly one of languages/services/features may\n * be set, and it must line up with the declared category.\n */\nconst ComponentFileSchema = z\n .object({\n displayName: z.string().min(1),\n description: z.string().min(1),\n category: CategorySchema,\n contributes: z.object({\n languages: z.array(z.string().min(1)).optional(),\n services: z.array(z.string().min(1)).optional(),\n features: z.array(FeatureContributionSchema).optional(),\n }),\n })\n .superRefine((data, ctx) => {\n const c = data.contributes;\n const filled = [\n c.languages && c.languages.length > 0 ? 'languages' : null,\n c.services && c.services.length > 0 ? 'services' : null,\n c.features && c.features.length > 0 ? 'features' : null,\n ].filter((x): x is string => x !== null);\n\n if (filled.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'contributes must set at least one of languages/services/features',\n });\n return;\n }\n if (filled.length > 1) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `contributes must set exactly one of languages/services/features, got: ${filled.join(', ')}`,\n });\n return;\n }\n const expected =\n data.category === 'language'\n ? 'languages'\n : data.category === 'service'\n ? 'services'\n : 'features';\n if (filled[0] !== expected) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `category '${data.category}' requires contributes.${expected}, got contributes.${filled[0]}`,\n });\n }\n });\n\nexport type ComponentFile = z.infer<typeof ComponentFileSchema>;\n\nexport interface Component {\n /** Catalog name, e.g. `node`, `atlassian/twg`. Always slash-form. */\n name: string;\n /** Absolute filesystem path of the source yml — useful for errors. */\n sourcePath: string;\n file: ComponentFile;\n}\n\n/**\n * Walk `templates/components/` recursively, parse every `.yml` file,\n * validate it, return as a name-keyed map. README files and other\n * non-yml files are silently skipped.\n *\n * Throws on the first invalid component file with a path-anchored\n * error — better to refuse than to load an inconsistent catalog.\n */\nexport async function loadComponentCatalog(\n rootDir: string = defaultComponentsDir(),\n): Promise<Map<string, Component>> {\n if (!existsSync(rootDir)) {\n return new Map();\n }\n const out = new Map<string, Component>();\n await walk(rootDir, rootDir, out);\n return out;\n}\n\nasync function walk(\n baseDir: string,\n currentDir: string,\n out: Map<string, Component>,\n): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(baseDir, full, out);\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith('.yml')) continue;\n const relative = path.relative(baseDir, full);\n const name = relative\n .replace(/\\.yml$/, '')\n .split(path.sep)\n .join('/');\n const text = await fs.readFile(full, 'utf8');\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (err) {\n throw new Error(\n `Failed to parse component ${name} (${full}): ${(err as Error).message}`,\n );\n }\n const parsed = ComponentFileSchema.safeParse(raw);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid component ${name} (${full}):\\n${issues}`);\n }\n out.set(name, { name, sourcePath: full, file: parsed.data });\n }\n}\n\n/**\n * A `SolutionConfig`-shaped fragment produced by merging the\n * `contributes` of one or more components. Caller wraps this into\n * a full config (adds schemaVersion + name) before writing the yml.\n */\nexport interface MergedComponents {\n languages: string[];\n services: string[];\n features: Array<{\n ref: string;\n options: Record<string, string | number | boolean>;\n }>;\n}\n\n/**\n * Merge the contributions of the given components into a single\n * fragment.\n *\n * Rules:\n * - `languages`/`services`: concat + dedupe (insertion order kept\n * stable; first occurrence wins).\n * - `features`: deduped by `ref`. When two components contribute\n * the same ref, their options are merged with the per-key rules\n * below.\n * - Per-key feature option merge:\n * - booleans: OR (true wins)\n * - strings + numbers: later component overrides (rare in\n * practice — components should set activation flags, not\n * credentials; credentials come from monoceros-config.yml\n * defaults.features or the user editing the yml directly).\n *\n * The OR-merge for booleans is what makes\n * `--with=atlassian/rovodev,atlassian/twg` yield both `true` even\n * though each sub-component sets the sibling flag to `false`.\n */\n/**\n * One entry of the resolved-components list. The optional `version`\n * is the `<name>:<version>` suffix from the CLI flag; today it\n * only applies to language components (we append it to each\n * contributed language string so the scaffold passes it as the\n * upstream feature's `version` option). For other categories,\n * providing a version is a builder error and resolveComponents\n * rejects it up front.\n */\nexport interface ResolvedComponent {\n component: Component;\n version?: string;\n}\n\nexport function mergeComponents(\n resolved: Array<Component | ResolvedComponent>,\n): MergedComponents {\n const languages: string[] = [];\n const services: string[] = [];\n const featureByRef = new Map<\n string,\n { ref: string; options: Record<string, string | number | boolean> }\n >();\n\n for (const entry of resolved) {\n const c = isResolvedComponent(entry) ? entry.component : entry;\n const version = isResolvedComponent(entry) ? entry.version : undefined;\n const ct = c.file.contributes;\n for (const lang of ct.languages ?? []) {\n // Language components can carry a `:version` suffix from the\n // CLI. We emit `<lang>:<version>` in the final yml; the\n // scaffold parses it back to the upstream feature's\n // `version` option at apply time.\n const value = version !== undefined ? `${lang}:${version}` : lang;\n if (!languages.includes(value)) languages.push(value);\n }\n for (const svc of ct.services ?? []) {\n if (!services.includes(svc)) services.push(svc);\n }\n for (const f of ct.features ?? []) {\n const existing = featureByRef.get(f.ref);\n if (!existing) {\n featureByRef.set(f.ref, {\n ref: f.ref,\n options: { ...(f.options ?? {}) },\n });\n continue;\n }\n existing.options = mergeFeatureOptions(existing.options, f.options ?? {});\n }\n }\n\n return {\n languages,\n services,\n features: [...featureByRef.values()],\n };\n}\n\nfunction isResolvedComponent(\n x: Component | ResolvedComponent,\n): x is ResolvedComponent {\n return 'component' in x;\n}\n\nfunction mergeFeatureOptions(\n a: Record<string, string | number | boolean>,\n b: Record<string, string | number | boolean>,\n): Record<string, string | number | boolean> {\n const result = { ...a };\n for (const [key, valueB] of Object.entries(b)) {\n const valueA = result[key];\n if (typeof valueA === 'boolean' && typeof valueB === 'boolean') {\n result[key] = valueA || valueB;\n continue;\n }\n result[key] = valueB;\n }\n return result;\n}\n\n/**\n * Resolve `--with=…` names against the catalog. Accepts plain\n * names (`node`) and language-version pairs (`node:20`). Splits\n * the `:version` off, looks up the bare name in the catalog, and\n * carries the version forward only for language components — a\n * version on any other category is rejected with a clear error.\n *\n * Throws with the full list of unknown names so the builder fixes\n * them all at once rather than running into them one at a time.\n */\nexport function resolveComponents(\n catalog: Map<string, Component>,\n names: string[],\n): ResolvedComponent[] {\n const unknown: string[] = [];\n const out: ResolvedComponent[] = [];\n for (const raw of names) {\n const colon = raw.indexOf(':');\n const name = colon === -1 ? raw : raw.slice(0, colon);\n const version = colon === -1 ? undefined : raw.slice(colon + 1);\n\n const c = catalog.get(name);\n if (!c) {\n // The unknown-name message reports the form the user typed\n // (including the :version) so it's easy to spot the typo.\n unknown.push(raw);\n continue;\n }\n if (version !== undefined && c.file.category !== 'language') {\n throw new Error(\n `Component '${name}' is a ${c.file.category}, not a language — a ':${version}' suffix has no meaning here.`,\n );\n }\n out.push({ component: c, ...(version !== undefined ? { version } : {}) });\n }\n if (unknown.length > 0) {\n const available = [...catalog.keys()].sort();\n throw new Error(\n `Unknown component${unknown.length > 1 ? 's' : ''}: ${unknown.join(', ')}.\\n` +\n `Available: ${available.join(', ')}.`,\n );\n }\n return out;\n}\n","import type { Component, ResolvedComponent } from './components.js';\nimport { mergeComponents } from './components.js';\nimport type { FeatureManifestSummary } from './manifest.js';\n\n/**\n * Renderer for the container yml that `monoceros init` produces.\n *\n * Two modes:\n *\n * - **Composed** (`monoceros init <name> --with=node,…`): all\n * listed components are active. The output is a clean,\n * immediately-applyable yml. Per-feature option hints\n * (auth/credentials from the feature manifest) are appended as\n * commented lines beneath the active options block, so a builder\n * reading the yml can see at a glance which keys exist without\n * leaving the file.\n *\n * - **Documented** (`monoceros init <name>` without `--with`):\n * every section is commented out. The output is a self-explaining\n * reference; the builder un-comments what they need and runs\n * `monoceros apply`.\n *\n * Both modes share the per-feature block rendering. The only\n * difference is whether the section is commented out at the top\n * level or not.\n *\n * We hand-render the yml as a string instead of going through the\n * yaml library's AST. The shape is narrow enough that the explicit\n * line-by-line approach is shorter and easier to reason about than\n * juggling Document + Pair + Scalar nodes with attached comments.\n */\n\nexport type ManifestLookup = (\n ref: string,\n) => FeatureManifestSummary | undefined;\n\nconst SCHEMA_HEADER = [\n '# Monoceros solution-config. Edit freely, then run',\n '# `monoceros apply <name>` to materialize a dev-container.',\n '#',\n '# Schema reference: see the workbench `templates/components/README.md`',\n '# and `docs/konzept.md` for what each section does. Each feature',\n '# under `features:` also accepts options not shown here — check',\n \"# the feature's `devcontainer-feature.json` for the full list.\",\n] as const;\n\n/**\n * Render the active-mode yml for the given components.\n */\nexport function generateComposedYml(\n name: string,\n components: ResolvedComponent[],\n lookupManifest: ManifestLookup,\n repoUrls: readonly string[] = [],\n): string {\n const merged = mergeComponents(components);\n const lines: string[] = [];\n for (const h of SCHEMA_HEADER) lines.push(h);\n lines.push('');\n lines.push('schemaVersion: 1');\n lines.push(`name: ${name}`);\n lines.push('');\n\n if (merged.languages.length > 0) {\n lines.push('languages:');\n for (const lang of merged.languages) lines.push(` - ${lang}`);\n lines.push('');\n }\n if (merged.services.length > 0) {\n lines.push('services:');\n for (const svc of merged.services) lines.push(` - ${svc}`);\n lines.push('');\n }\n if (merged.features.length > 0) {\n lines.push('features:');\n for (const f of merged.features) {\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ false,\n );\n }\n lines.push('');\n }\n // Composed mode only emits a repos block when --with-repo provided\n // URLs. The \"show all available options\" requirement applies on a\n // per-entry basis there: each active repo entry carries commented\n // hints for its optional fields. With no URLs, composed mode stays\n // schlank — repos are surfaced via `monoceros add-repo` post-init.\n if (repoUrls.length > 0) {\n renderReposBlock(lines, repoUrls, /* commented */ false);\n }\n\n return ensureTrailingNewline(lines.join('\\n'));\n}\n\n/**\n * Render the documented-default yml: every component listed but\n * commented out, with section headers carrying short prose so a\n * fresh builder can read the file and figure out what to enable.\n */\nexport function generateDocumentedYml(\n name: string,\n catalog: Map<string, Component>,\n lookupManifest: ManifestLookup,\n repoUrls: readonly string[] = [],\n): string {\n const byCategory = groupByCategory(catalog);\n const lines: string[] = [];\n for (const h of SCHEMA_HEADER) lines.push(h);\n lines.push('#');\n lines.push('# Below is the full set of components shipped with this');\n lines.push('# workbench, every one commented out. Un-comment the lines');\n lines.push('# you want active. The same effect (and a cleaner yml) is');\n lines.push('# achievable by running `monoceros init <name> --with=…`');\n lines.push('# with a comma-separated list of component names.');\n lines.push('');\n lines.push('schemaVersion: 1');\n lines.push(`name: ${name}`);\n lines.push('');\n\n if (byCategory.language.length > 0) {\n const items = byCategory.language.flatMap((c) =>\n (c.file.contributes.languages ?? []).map((lang) => ({\n value: lang,\n label: c.file.displayName,\n })),\n );\n const width = Math.max(...items.map((i) => i.value.length)) + 2;\n lines.push('# Languages — runtime toolchains.');\n lines.push('# languages:');\n for (const item of items) {\n const pad = ' '.repeat(width - item.value.length);\n lines.push(`# - ${item.value}${pad}# ${item.label}`);\n }\n lines.push('');\n }\n if (byCategory.service.length > 0) {\n const items = byCategory.service.flatMap((c) =>\n (c.file.contributes.services ?? []).map((svc) => ({\n value: svc,\n label: c.file.displayName,\n })),\n );\n const width = Math.max(...items.map((i) => i.value.length)) + 2;\n lines.push('# Services — compose-mode siblings of the workspace');\n lines.push('# container (compose mode kicks in as soon as at least');\n lines.push('# one service is active).');\n lines.push('# services:');\n for (const item of items) {\n const pad = ' '.repeat(width - item.value.length);\n lines.push(`# - ${item.value}${pad}# ${item.label}`);\n }\n lines.push('');\n }\n if (byCategory.feature.length > 0) {\n lines.push('# Features — devcontainer features installed inside the');\n lines.push('# container. Each entry has an OCI-style `ref` plus an');\n lines.push('# optional `options` map. Credentials/auth keys appear');\n lines.push('# as commented hints; set them here per container, or');\n lines.push('# globally in monoceros-config.yml under');\n lines.push('# `defaults.features.<ref>`.');\n lines.push('#');\n lines.push('# Catalog:');\n lines.push('#');\n const nameColumnWidth =\n Math.max(...byCategory.feature.map((c) => c.name.length)) + 2;\n for (const c of byCategory.feature) {\n const pad = ' '.repeat(nameColumnWidth - c.name.length);\n lines.push(`# ${c.name}${pad}${c.file.displayName}`);\n }\n lines.push('#');\n lines.push('# Below: one block per feature ref. Un-comment what');\n lines.push(\"# you want active. Sub-components share their parent's\");\n lines.push('# block — pick the parent for the full preset, swap to');\n lines.push('# a sub-component name for a partial install.');\n lines.push('#');\n lines.push('# features:');\n\n // Render one feature block per unique ref. Prefer the top-level\n // component (e.g. `atlassian` over `atlassian/twg`) as the source\n // of the rendered options, since the top-level carries the\n // \"everything on\" default users typically want first.\n const renderedRefs = new Set<string>();\n const topLevel = byCategory.feature.filter((c) => !c.name.includes('/'));\n\n for (const c of topLevel) {\n for (const f of c.file.contributes.features ?? []) {\n if (renderedRefs.has(f.ref)) continue;\n renderedRefs.add(f.ref);\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ true,\n );\n }\n }\n // Any feature ref only mentioned through a sub-component (no\n // top-level component for the same ref) — render it from the\n // first sub-component.\n for (const c of byCategory.feature) {\n if (!c.name.includes('/')) continue;\n for (const f of c.file.contributes.features ?? []) {\n if (renderedRefs.has(f.ref)) continue;\n renderedRefs.add(f.ref);\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ true,\n );\n }\n }\n lines.push('');\n }\n // Repos section — always rendered in documented mode (commented out\n // when no --with-repo URLs were provided, active when they were).\n // The block shows the full set of per-entry options so a builder\n // reading the yml sees what's possible without leaving the file —\n // same \"all available options are visible\" rule that drives the\n // features block above.\n renderReposBlock(lines, repoUrls, /* commented */ repoUrls.length === 0);\n\n return ensureTrailingNewline(lines.join('\\n'));\n}\n\ninterface RenderableFeature {\n ref: string;\n options?: Record<string, string | number | boolean>;\n}\n\n// Target column width for rendered comment lines. Description text\n// and usage notes get word-wrapped against this width so the output\n// stays readable in a standard editor without horizontal scrolling.\nconst COMMENT_WIDTH = 72;\n\nfunction renderFeatureBlock(\n out: string[],\n feature: RenderableFeature,\n summary: FeatureManifestSummary | undefined,\n commented: boolean,\n): void {\n const c = commented ? '# ' : ' ';\n const optionHints = summary?.optionHints ?? [];\n const optionDescriptions = summary?.optionDescriptions ?? {};\n const usageNotes = summary?.usageNotes ?? [];\n\n // Per-feature usage notes — rendered as a wrapped comment block\n // right before the `- ref:` line. Multiple notes are separated\n // by an empty comment line so they read as distinct paragraphs.\n for (let i = 0; i < usageNotes.length; i++) {\n if (i > 0) out.push(`${c}#`);\n for (const line of wrapToComment(\n usageNotes[i]!,\n COMMENT_WIDTH - c.length,\n )) {\n out.push(`${c}# ${line}`);\n }\n }\n\n out.push(`${c}- ref: ${feature.ref}`);\n const options = feature.options ?? {};\n const activeOptions = Object.entries(options);\n const remainingHints = optionHints.filter((h) => !(h in options));\n\n // When there are active options, emit a real `options:` block.\n // When there are only hints (no active options), skip `options:`\n // entirely and emit the hints as plain comments at the same depth\n // — yaml parsers see `options:` with no content under it as\n // `null`, which fails our schema.\n if (activeOptions.length > 0) {\n out.push(`${c} options:`);\n for (const [key, value] of activeOptions) {\n out.push(`${c} ${key}: ${renderScalarValue(value)}`);\n }\n if (remainingHints.length > 0) {\n out.push(\n `${c} # Optional — override monoceros-config.yml defaults.features:`,\n );\n for (const hint of remainingHints) {\n emitHint(out, hint, optionDescriptions[hint], `${c} `);\n }\n }\n } else if (remainingHints.length > 0) {\n out.push(\n `${c} # Optional — override monoceros-config.yml defaults.features:`,\n );\n out.push(`${c} # options:`);\n for (const hint of remainingHints) {\n emitHint(out, hint, optionDescriptions[hint], `${c} # `);\n }\n }\n}\n\n/**\n * Emit a single option-hint line, optionally preceded by its\n * description as wrapped comment lines. `linePrefix` is the full\n * prefix (indent + any commented-out `# ` chars) that every\n * emitted line should start with; the hint itself is suffixed\n * with `: ` so the user can fill in a value.\n */\nfunction emitHint(\n out: string[],\n hint: string,\n description: string | undefined,\n linePrefix: string,\n): void {\n if (description) {\n for (const line of wrapToComment(\n description,\n COMMENT_WIDTH - linePrefix.length,\n )) {\n out.push(`${linePrefix}# ${line}`);\n }\n }\n out.push(`${linePrefix}${hint}:`);\n}\n\n/**\n * Render the `repos:` section. When `commented` is true the entire\n * block is prefixed with `# ` (documented mode, no --with-repo).\n * When false the `url:` lines are active but the optional fields\n * (path, provider, git.user) stay commented per entry so the\n * builder sees what else can go there.\n *\n * The \"all available options visible\" rule: every per-entry field\n * the schema accepts appears on every rendered entry, either active\n * (rare — only `url` is required) or as a commented hint.\n */\nfunction renderReposBlock(\n out: string[],\n urls: readonly string[],\n commented: boolean,\n): void {\n // Prose intro — always a comment block, regardless of whether the\n // section below is active or fully commented.\n out.push('# Repos — git repositories cloned into projects/ during');\n out.push('# post-create. HTTPS-only (ADR 0006). Provider auto-detected');\n out.push('# for github.com, gitlab.com, bitbucket.org; for any other host');\n out.push('# (self-hosted GitLab, Bitbucket Data Center, Gitea/Forgejo,');\n out.push('# GitHub Enterprise, …) declare provider explicitly.');\n out.push('#');\n\n if (commented) {\n // Pure documented mode (no --with-repo): a single example entry,\n // everything commented. Shows the full per-entry surface.\n out.push('# repos:');\n out.push('# - url: https://github.com/<org>/<repo>.git');\n out.push(\n '# # path: <folder> # subfolder under projects/; default: URL-derived',\n );\n out.push(\n '# # provider: github # github | gitlab | bitbucket | gitea',\n );\n out.push(\n '# # git: # per-repo committer identity override',\n );\n out.push('# # user:');\n out.push('# # name: Your Name');\n out.push('# # email: you@example.com');\n out.push('');\n return;\n }\n\n // Active entries from --with-repo. Each entry repeats the\n // commented hints for the optional fields so they're discoverable\n // without docs.\n out.push('repos:');\n for (const url of urls) {\n const derivedPath = deriveDefaultPath(url);\n out.push(` - url: ${url}`);\n out.push(\n ` # path: ${derivedPath} # subfolder under projects/; default: URL-derived (${derivedPath})`,\n );\n out.push(\n ' # provider: github # github | gitlab | bitbucket | gitea',\n );\n out.push(\n ' # git: # per-repo committer identity override',\n );\n out.push(' # user:');\n out.push(' # name: Your Name');\n out.push(' # email: you@example.com');\n }\n out.push('');\n}\n\n/**\n * URL-derived default for the `path:` hint. Mirrors what\n * `deriveRepoName` in scaffold.ts does at apply time — inlined\n * here to keep the generator self-contained (no cross-module\n * dependency from init/ → create/).\n */\nfunction deriveDefaultPath(url: string): string {\n let last = url;\n const slash = url.lastIndexOf('/');\n if (slash >= 0) last = url.slice(slash + 1);\n if (last.endsWith('.git')) last = last.slice(0, -4);\n return last || 'repo';\n}\n\n/**\n * Word-wrap a single paragraph of plain text to `width` columns.\n * The returned strings do NOT include any prefix — the caller is\n * expected to prepend a comment marker (`# `) and any indent.\n * Long words that exceed `width` are emitted on their own line\n * rather than split mid-word.\n */\nfunction wrapToComment(text: string, width: number): string[] {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n if (words.length === 0) return [''];\n const usable = Math.max(width, 20);\n const lines: string[] = [];\n let current = '';\n for (const w of words) {\n if (current.length === 0) {\n current = w;\n continue;\n }\n if (current.length + 1 + w.length <= usable) {\n current += ' ' + w;\n } else {\n lines.push(current);\n current = w;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\nfunction renderScalarValue(value: string | number | boolean): string {\n if (typeof value === 'string') {\n // Quote anything that could be ambiguous to a yaml parser\n // (leading numerics, special chars). Quoting strings is always\n // safe; we only avoid it for booleans/numbers so they keep\n // their types.\n return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value)\n ? value\n : JSON.stringify(value);\n }\n return String(value);\n}\n\nfunction groupByCategory(catalog: Map<string, Component>): {\n language: Component[];\n service: Component[];\n feature: Component[];\n} {\n const out: ReturnType<typeof groupByCategory> = {\n language: [],\n service: [],\n feature: [],\n };\n // Stable sort by component name for deterministic output.\n const sorted = [...catalog.values()].sort((a, b) =>\n a.name.localeCompare(b.name),\n );\n for (const c of sorted) {\n out[c.file.category].push(c);\n }\n return out;\n}\n\nfunction ensureTrailingNewline(s: string): string {\n return s.endsWith('\\n') ? s : s + '\\n';\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { bundledFeaturesDir, workbenchCheckoutRoot } from '../config/paths.js';\nimport { matchMonocerosFeature } from '../util/ref.js';\n\n/**\n * Loader for the parts of a Monoceros devcontainer-feature manifest\n * that init's yml-generator wants to surface as inline guidance:\n *\n * - `optionHints` — names of feature options to render as\n * commented-out lines below the active options block, so a\n * builder reading the yml sees at a glance which keys exist\n * without going to the feature's docs.\n * - `optionDescriptions` — the `description` string from each\n * option in the manifest, keyed by option name. Init prints\n * these as a wrapped comment block above the matching hint\n * line so the builder knows what the option does without\n * opening the feature docs.\n * - `usageNotes` — free-text per-feature paragraphs from\n * `x-monoceros.usageNotes`. Init renders them as a comment\n * block right above the `- ref:` line. Use for things that\n * aren't tied to one option — e.g. \"alternative auth flow X\n * works inside the running container\".\n *\n * Only Monoceros-owned refs\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`) are\n * resolved — for third-party features we don't have the manifest on\n * disk at init time. The fallback is \"no hints\", which is right:\n * we don't speculate about other people's feature options.\n *\n * Manifest lookup order:\n * 1. Workbench checkout — `<checkoutRoot>/images/features/<name>/`.\n * Dev edits to the source-of-truth manifest are visible\n * immediately, no rebuild step required.\n * 2. CLI bundle — `<workbenchRoot>/features/<name>/`. Populated\n * by `pnpm manifests:sync` (runs as `prebuild`), shipped in\n * the npm tarball. Production fallback for builders without a\n * workbench checkout.\n *\n * Both paths missing → `undefined` and init renders without hints.\n * Never throws.\n */\n\nexport interface FeatureManifestSummary {\n /** Names of options to render as commented hints in the init output. */\n optionHints: string[];\n /** `description` from each option in the manifest, keyed by name. */\n optionDescriptions: Record<string, string>;\n /** Free-text per-feature notes rendered above the `- ref:` line. */\n usageNotes: string[];\n}\n\ninterface RawManifest {\n options?: Record<string, { description?: string }>;\n 'x-monoceros'?: {\n optionHints?: unknown;\n usageNotes?: unknown;\n };\n}\n\nfunction resolveManifestPath(\n name: string,\n checkoutRoot: string | null,\n): string | null {\n if (checkoutRoot) {\n const checkoutPath = path.join(\n checkoutRoot,\n 'images',\n 'features',\n name,\n 'devcontainer-feature.json',\n );\n if (existsSync(checkoutPath)) return checkoutPath;\n }\n const bundlePath = path.join(\n bundledFeaturesDir(),\n name,\n 'devcontainer-feature.json',\n );\n if (existsSync(bundlePath)) return bundlePath;\n return null;\n}\n\nexport function loadFeatureManifestSummary(\n ref: string,\n checkoutRoot: string | null = workbenchCheckoutRoot(),\n): FeatureManifestSummary | undefined {\n const match = matchMonocerosFeature(ref);\n if (!match) return undefined;\n const manifestPath = resolveManifestPath(match.name, checkoutRoot);\n if (!manifestPath) return undefined;\n try {\n const text = readFileSync(manifestPath, 'utf8');\n const parsed = JSON.parse(text) as RawManifest;\n\n const rawHints = parsed['x-monoceros']?.optionHints;\n const optionHints = Array.isArray(rawHints)\n ? rawHints.filter(\n (x): x is string => typeof x === 'string' && x.length > 0,\n )\n : [];\n\n const rawNotes = parsed['x-monoceros']?.usageNotes;\n const usageNotes = Array.isArray(rawNotes)\n ? rawNotes.filter(\n (x): x is string => typeof x === 'string' && x.length > 0,\n )\n : [];\n\n const optionDescriptions: Record<string, string> = {};\n if (parsed.options) {\n for (const [key, opt] of Object.entries(parsed.options)) {\n if (\n opt &&\n typeof opt === 'object' &&\n typeof opt.description === 'string' &&\n opt.description.length > 0\n ) {\n optionDescriptions[key] = opt.description;\n }\n }\n }\n\n return { optionHints, optionDescriptions, usageNotes };\n } catch {\n return undefined;\n }\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { loadComponentCatalog } from '../init/components.js';\nimport { colorsFor } from '../util/format.js';\n\n// Category-key → human-readable section label. Same order is used\n// for rendering — languages first (most common), services next,\n// features last.\nconst CATEGORY_LABELS = {\n language: 'Languages',\n service: 'Services',\n feature: 'Features',\n} as const;\nconst CATEGORY_ORDER: ReadonlyArray<keyof typeof CATEGORY_LABELS> = [\n 'language',\n 'service',\n 'feature',\n];\n\nexport const listComponentsCommand = defineCommand({\n meta: {\n name: 'list-components',\n group: 'discovery',\n description:\n 'Print the components catalog used by `monoceros init --with=…`, grouped by category (Languages, Services, Features). Component names render in cyan, descriptions in default colour; when piped, the formatting drops out and lines become `name<TAB>description` for grep/awk-friendly consumption.',\n },\n args: {},\n async run() {\n try {\n const catalog = await loadComponentCatalog();\n if (catalog.size === 0) {\n consola.warn(\n 'No components found. The workbench checkout looks incomplete.',\n );\n process.exit(0);\n }\n\n const fmt = colorsFor(process.stdout);\n const isTty = process.stdout.isTTY ?? false;\n\n // Group entries by category for sectioned rendering.\n const byCategory = new Map<\n string,\n Array<{ name: string; desc: string }>\n >();\n for (const c of catalog.values()) {\n const list = byCategory.get(c.file.category) ?? [];\n list.push({ name: c.name, desc: c.file.displayName });\n byCategory.set(c.file.category, list);\n }\n for (const list of byCategory.values()) {\n list.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n // Piped (non-TTY) output: stay machine-friendly with the\n // historical `name<TAB>description` shape, one category at a\n // time. No ANSI, no alignment padding — grep/awk consumers\n // want predictable columns.\n if (!isTty) {\n let first = true;\n for (const cat of CATEGORY_ORDER) {\n const items = byCategory.get(cat);\n if (!items || items.length === 0) continue;\n if (!first) process.stdout.write('\\n');\n first = false;\n process.stdout.write(`# ${cat}\\n`);\n for (const { name, desc } of items) {\n process.stdout.write(`${name}\\t${desc}\\n`);\n }\n }\n process.exit(0);\n }\n\n // Interactive (TTY) output: section headers + aligned\n // columns, same visual vocabulary as the help renderer and\n // the apply/install structured output. Cyan name column\n // padded to the widest entry in its section so the\n // description column lines up.\n let first = true;\n for (const cat of CATEGORY_ORDER) {\n const items = byCategory.get(cat);\n if (!items || items.length === 0) continue;\n if (!first) process.stdout.write('\\n');\n first = false;\n process.stdout.write(`${fmt.sectionLine(CATEGORY_LABELS[cat])}\\n\\n`);\n const nameWidth = Math.max(...items.map((i) => i.name.length));\n const gutter = 2;\n for (const { name, desc } of items) {\n const pad = ' '.repeat(nameWidth - name.length + gutter);\n process.stdout.write(` ${fmt.cyan(name)}${pad}${desc}\\n`);\n }\n }\n process.exit(0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runLogs } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const logsCommand = defineCommand({\n meta: {\n name: 'logs',\n group: 'run',\n description:\n 'Tail logs from the compose services of the named dev-container. Pass --no-follow for a one-shot dump.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n follow: {\n type: 'boolean',\n description:\n 'Follow log output (default: true). Use --no-follow to disable.',\n alias: ['f'],\n default: true,\n },\n },\n run({ args }) {\n return dispatch(() =>\n runLogs({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n follow: args.follow,\n }),\n );\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runRemoveAptPackages } from '../modify/index.js';\n\nexport const removeAptPackagesCommand = defineCommand({\n meta: {\n name: 'remove-apt-packages',\n group: 'edit',\n description:\n 'Remove apt packages from the container config. Pass package names after `--` (e.g. `monoceros remove-apt-packages sandbox -- make jq`). Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n const packages = [...getInnerArgs()];\n if (packages.length === 0) {\n consola.error(\n 'No package names given. Usage: `monoceros remove-apt-packages <containername> [--yes] -- <pkg> [<pkg> …]`.',\n );\n process.exit(1);\n }\n try {\n const result = await runRemoveAptPackages({\n name: args.name,\n packages,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveFeature } from '../modify/index.js';\n\nexport const removeFeatureCommand = defineCommand({\n meta: {\n name: 'remove-feature',\n group: 'edit',\n description:\n 'Remove a devcontainer feature from the container config (by its OCI ref). Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n ref: {\n type: 'positional',\n description:\n 'Feature ref (e.g. ghcr.io/devcontainers/features/docker-in-docker:2).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveFeature({\n name: args.name,\n ref: args.ref,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { createInterface } from 'node:readline/promises';\nimport { runRemove } from '../remove/index.js';\n\nexport const removeCommand = defineCommand({\n meta: {\n name: 'remove',\n group: 'lifecycle',\n description:\n 'Wipe everything belonging to a container: stop and remove the docker objects, back up the container-configs yml + container directory (incl. home/, projects/, data/), then delete them from disk. Shared docker images stay. By default the destructive step is confirmed interactively; pass -y to skip.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n backup: {\n type: 'boolean',\n // citty turns a default-true boolean automatically into a\n // `--no-X` flag for negation, so the builder gets the natural\n // `monoceros remove <name> --no-backup` form without us\n // needing to special-case the parsing. Defining the arg as\n // `no-backup` directly conflicts with citty's prefix logic\n // and silently fails to bind, so we always go through the\n // positive form.\n description:\n 'Write a backup of <container-dir> and the yml under container-backups/ before deleting. Default on; use `--no-backup` to skip.',\n default: true,\n },\n yes: {\n type: 'boolean',\n alias: 'y',\n description:\n 'Skip the interactive confirmation prompt. Useful in scripts.',\n default: false,\n },\n },\n async run({ args }) {\n try {\n const noBackup = args.backup === false;\n const skipPrompt = args.yes === true;\n\n if (!skipPrompt) {\n const warning = noBackup\n ? `About to remove '${args.name}' WITHOUT a backup. Docker objects, container-configs entry, and container directory will all be deleted.`\n : `About to remove '${args.name}'. A backup will be written to container-backups/ first, then docker objects, container-configs entry, and container directory will all be deleted.`;\n consola.warn(warning);\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n const answer = await rl.question('Continue? [y/N] ');\n rl.close();\n if (!/^y(es)?$/i.test(answer.trim())) {\n consola.info('Aborted. Nothing changed.');\n process.exit(0);\n }\n }\n\n await runRemove({\n name: args.name,\n ...(noBackup ? { noBackup: true } : {}),\n });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\nimport { REGEX } from '../config/schema.js';\nimport {\n composeProjectName,\n spawnBash,\n type ComposeSpawn,\n} from '../devcontainer/compose.js';\n\n/**\n * `monoceros remove <name>` — wipe everything belonging to one\n * container.\n *\n * What \"everything\" means in practice (in this order):\n *\n * 1. Stop and remove docker objects scoped to the container:\n * - compose containers (label `com.docker.compose.project=<project>`)\n * - any image-mode container matching `vsc-<name>-*`\n * - the project network `<project>_default`\n * Named docker volumes are no longer used as of fea2b3f (DB data\n * is bind-mounted onto `<container-dir>/data/<svc>/`), so they\n * go away with the directory delete below.\n *\n * 2. Optionally back up the host-side state:\n * - `container-configs/<name>.yml`\n * - `container/<name>/` (entire scaffold incl. `home/`,\n * `projects/`, `.monoceros/`, `data/`)\n * Lands at `container-backups/<name>-<timestamp>/`. Plain\n * directory copy — readable with normal filesystem tools.\n *\n * 3. Delete the host-side state.\n *\n * Shared docker images (`monoceros-runtime:dev`, feature build\n * images, postgres/mysql/redis base images) are NOT removed — they\n * are shared across containers and pruning them is a separate\n * operation the builder can do with `docker image prune` when they\n * actually want to free that disk.\n */\n\nexport interface RunRemoveOptions {\n name: string;\n /** When true, skip the backup step. */\n noBackup?: boolean;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n /** Override the timestamp embedded in the backup directory name. */\n now?: Date;\n /** Bash spawn for the docker cleanup script. Tests inject a stub. */\n dockerSpawn?: ComposeSpawn;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n warn?: (msg: string) => void;\n };\n}\n\nexport interface RunRemoveResult {\n /** Path the yml was at before deletion, or `null` if it didn't exist. */\n configPath: string | null;\n /** Path the container scaffold was at before deletion, or `null`. */\n containerPath: string | null;\n /** Directory of the backup, or `null` when --no-backup was passed. */\n backupPath: string | null;\n /** Exit code of the docker cleanup step (0 on success). */\n dockerExitCode: number;\n}\n\nexport async function runRemove(\n opts: RunRemoveOptions,\n): Promise<RunRemoveResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n warn: (msg) => consola.warn(msg),\n };\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const ymlPath = containerConfigPath(opts.name, home);\n const containerPath = containerDir(opts.name, home);\n const hasYml = existsSync(ymlPath);\n const hasContainer = existsSync(containerPath);\n\n if (!hasYml && !hasContainer) {\n throw new Error(\n `Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`,\n );\n }\n\n // ── Step 1: stop + remove docker objects ────────────────────────\n const projectName = composeProjectName(containerPath);\n const dockerSpawn = opts.dockerSpawn ?? spawnBash;\n const script = [\n `set -u`,\n `echo \"[remove] tearing down docker project ${projectName}…\"`,\n // Compose-mode containers, identified by the compose project label.\n `by_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n // Devcontainer-cli containers (image-mode workspace + feature-\n // build intermediates) all carry this label, value = the absolute\n // container-dir path. Most reliable anchor we have, because\n // @devcontainers/cli lets Docker assign random names like\n // 'kind_cerf' — neither the project-name nor the vsc-<name>-\n // prefix filters below catch those.\n `by_dc_label=$(docker ps -aq --filter \"label=devcontainer.local_folder=${containerPath}\" 2>/dev/null || true)`,\n // Container-name prefix fallback (catches half-broken state).\n `by_compose_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n // Image-mode devcontainer-cli name fallback (only kicks in when\n // the cli used a deterministic name — modern versions don't).\n `by_image_name=$(docker ps -aq --filter \"name=^vsc-${opts.name}-\" 2>/dev/null || true)`,\n `to_remove=$(printf \"%s\\\\n%s\\\\n%s\\\\n%s\\\\n\" \"$by_label\" \"$by_dc_label\" \"$by_compose_name\" \"$by_image_name\" | sort -u | grep -v \"^$\" || true)`,\n `if [ -n \"$to_remove\" ]; then echo \"[remove] removing containers: $(echo $to_remove | tr \"\\\\n\" \" \")\"; docker rm -f $to_remove >/dev/null || true; else echo \"[remove] no containers found\"; fi`,\n `docker network rm ${projectName}_default 2>/dev/null && echo \"[remove] network ${projectName}_default removed\" || true`,\n `echo \"[remove] docker cleanup done\"`,\n ].join('; ');\n const dockerExitCode = await dockerSpawn(['-c', script], home);\n\n // ── Step 2: optional backup ────────────────────────────────────\n let backupPath: string | null = null;\n if (!opts.noBackup && (hasYml || hasContainer)) {\n const ts = (opts.now ?? new Date()).toISOString().replace(/[:.]/g, '-');\n backupPath = path.join(home, 'container-backups', `${opts.name}-${ts}`);\n await fs.mkdir(backupPath, { recursive: true });\n if (hasYml) {\n await fs.copyFile(ymlPath, path.join(backupPath, `${opts.name}.yml`));\n }\n if (hasContainer) {\n await fs.cp(containerPath, path.join(backupPath, 'container'), {\n recursive: true,\n });\n }\n logger.info(`Backup written to ${prettyPath(backupPath)}.`);\n }\n\n // ── Step 3: delete host-side state ─────────────────────────────\n if (hasYml) {\n await fs.rm(ymlPath, { force: true });\n }\n if (hasContainer) {\n await fs.rm(containerPath, { recursive: true, force: true });\n }\n\n logger.success(\n `Removed '${opts.name}': docker objects gone, container-configs entry deleted, container directory deleted.`,\n );\n if (!backupPath) {\n logger.warn?.(\n 'No backup created (--no-backup). The host-side state is gone for good.',\n );\n }\n\n return {\n configPath: hasYml ? ymlPath : null,\n containerPath: hasContainer ? containerPath : null,\n backupPath,\n dockerExitCode,\n };\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRestore } from '../restore/index.js';\n\nexport const restoreCommand = defineCommand({\n meta: {\n name: 'restore',\n group: 'lifecycle',\n description:\n \"Restore a container's host-side state from a backup written by `monoceros remove`. Copies the yml and the container directory back into $MONOCEROS_HOME. Refuses to overwrite an existing config or container — remove the in-place container first if you need to clobber. Run `monoceros apply <name>` afterwards to bring it back up.\",\n },\n args: {\n 'backup-path': {\n type: 'positional',\n description:\n 'Path to a backup directory (typically `<MONOCEROS_HOME>/container-backups/<name>-<timestamp>/`).',\n required: true,\n },\n },\n async run({ args }) {\n try {\n await runRestore({ backupPath: args['backup-path'] });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerConfigsDir,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\n\n/**\n * `monoceros restore <backup-path>` — re-instantiate the host-side\n * state of a previously-removed container from a backup written by\n * `monoceros remove`.\n *\n * Backup layout (produced by runRemove):\n *\n * <backup>/<name>.yml ← container-configs source\n * <backup>/container/ ← full container scaffold\n * (home/, projects/, data/, .monoceros/, …)\n *\n * Restore copies both back into `$MONOCEROS_HOME`:\n *\n * $MONOCEROS_HOME/container-configs/<name>.yml\n * $MONOCEROS_HOME/container/<name>/\n *\n * Refuses to clobber an existing config or container dir — the\n * builder must remove the in-place container first (or pick a\n * different target name).\n *\n * Restore does NOT recreate the docker objects: builder runs\n * `monoceros apply <name>` afterwards. That keeps restore a\n * pure filesystem operation, safe to dry-run, with no side-\n * effects on the docker daemon.\n */\n\nexport interface RunRestoreOptions {\n /** Path to a `<MONOCEROS_HOME>/container-backups/<name>-<ts>/` dir. */\n backupPath: string;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n };\n}\n\nexport interface RunRestoreResult {\n /** Container name detected from the backup contents. */\n name: string;\n /** Where the yml was restored to. */\n configPath: string;\n /** Where the container directory was restored to (or `null` when\n * the backup didn't carry one — e.g. a remove that ran before any\n * apply had materialized the container dir). */\n containerPath: string | null;\n}\n\nexport async function runRestore(\n opts: RunRestoreOptions,\n): Promise<RunRestoreResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n };\n\n const backup = path.resolve(opts.backupPath);\n if (!existsSync(backup)) {\n throw new Error(`Backup not found: ${backup}.`);\n }\n const stat = await fs.stat(backup);\n if (!stat.isDirectory()) {\n throw new Error(`Backup path is not a directory: ${backup}.`);\n }\n\n // Detect the container name from the single `.yml` file in the\n // backup root. runRemove writes `<name>.yml`; we don't depend on\n // the backup-directory name (`<name>-<timestamp>`) because the\n // builder might have renamed/moved the backup folder.\n const entries = await fs.readdir(backup);\n const ymlFiles = entries.filter((f) => f.endsWith('.yml'));\n if (ymlFiles.length === 0) {\n throw new Error(\n `Backup at ${backup} doesn't contain a *.yml — expected a single config file at the root.`,\n );\n }\n if (ymlFiles.length > 1) {\n throw new Error(\n `Backup at ${backup} contains multiple .yml files (${ymlFiles.join(', ')}). Expected exactly one.`,\n );\n }\n const ymlFile = ymlFiles[0]!;\n const name = ymlFile.replace(/\\.yml$/, '');\n\n const containerInBackup = path.join(backup, 'container');\n const hasContainer = existsSync(containerInBackup);\n\n // Refuse to overwrite live state.\n const destYml = containerConfigPath(name, home);\n const destContainer = containerDir(name, home);\n if (existsSync(destYml)) {\n throw new Error(\n `Refusing to restore: ${destYml} already exists. Remove the current container first (\\`monoceros remove ${name}\\`) or rename the existing config.`,\n );\n }\n if (hasContainer && existsSync(destContainer)) {\n throw new Error(\n `Refusing to restore: ${destContainer} already exists. Remove the current container first (\\`monoceros remove ${name}\\`).`,\n );\n }\n\n // Copy back into place.\n await fs.mkdir(containerConfigsDir(home), { recursive: true });\n await fs.copyFile(path.join(backup, ymlFile), destYml);\n if (hasContainer) {\n await fs.cp(containerInBackup, destContainer, { recursive: true });\n }\n\n logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);\n logger.info(\n `Run \\`monoceros apply ${name}\\` to bring the container back up.`,\n );\n\n return {\n name,\n configPath: destYml,\n containerPath: hasContainer ? destContainer : null,\n };\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveFromUrl } from '../modify/index.js';\n\nexport const removeFromUrlCommand = defineCommand({\n meta: {\n name: 'remove-from-url',\n group: 'edit',\n description:\n 'Remove a previously-added install URL from the container config. Idempotent, prints a diff before writing. The URL is dropped from post-create.sh on the next `monoceros apply`.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description: 'Install URL to remove (must match the original exactly).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveFromUrl({\n name: args.name,\n url: args.url,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveLanguage } from '../modify/index.js';\n\nexport const removeLanguageCommand = defineCommand({\n meta: {\n name: 'remove-language',\n group: 'edit',\n description:\n 'Remove a language toolchain from the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n language: {\n type: 'positional',\n description: 'Language identifier (e.g. python, java, rust).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveLanguage({\n name: args.name,\n language: args.language,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveRepo } from '../modify/index.js';\n\nexport const removeRepoCommand = defineCommand({\n meta: {\n name: 'remove-repo',\n group: 'edit',\n description:\n 'Remove a repo from the container config (matches by URL or by its projects/<folder> name). Does NOT delete the existing projects/<folder> directory — local edits are preserved; clean it up manually.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n target: {\n type: 'positional',\n description: 'Repo URL or its projects/<folder> name. Either works.',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveRepo({\n name: args.name,\n target: args.target,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveService } from '../modify/index.js';\n\nexport const removeServiceCommand = defineCommand({\n meta: {\n name: 'remove-service',\n group: 'edit',\n description:\n 'Remove a compose service from the container config. Idempotent, prints a diff before writing. Note: data volumes (e.g. postgres-data) are NOT cleaned up automatically.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'positional',\n description: 'Service identifier (e.g. postgres, redis).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveService({\n name: args.name,\n service: args.service,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { containerDir } from '../config/paths.js';\nimport { runInContainer } from '../devcontainer/run.js';\nimport { getInnerArgs } from '../inner-args.js';\n\nexport const runCommand = defineCommand({\n meta: {\n name: 'run',\n group: 'run',\n description:\n 'Run a one-off command inside the named dev-container. Use `--` to separate monoceros flags from the inner command.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n async run({ args }) {\n const command = [...getInnerArgs()];\n if (command.length === 0) {\n consola.error(\n 'No command provided. Usage: `monoceros run <containername> -- <cmd> [args…]`.',\n );\n process.exit(1);\n }\n try {\n const exitCode = await runInContainer({\n root: containerDir(args.name),\n command,\n });\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\n\nexport interface ShellLogger {\n info: (message: string) => void;\n}\n\nexport interface RunShellOptions {\n /** Container root: `<MONOCEROS_HOME>/container/<name>/`. */\n root: string;\n spawn?: DevcontainerSpawn;\n}\n\nexport async function runShell(opts: RunShellOptions): Promise<number> {\n assertContainerExists(opts.root);\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n\n const upCode = await spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n { quiet: true },\n );\n if (upCode !== 0) return upCode;\n\n return spawnFn(\n [\n 'exec',\n '--workspace-folder',\n opts.root,\n '--mount-workspace-git-root=false',\n 'bash',\n ],\n opts.root,\n { interactive: true },\n );\n}\n\nexport function assertContainerExists(root: string): void {\n if (!existsSync(path.join(root, '.devcontainer'))) {\n throw new Error(\n `No .devcontainer/ at ${root}. Run \\`monoceros apply <name>\\` first.`,\n );\n }\n}\n","import { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\nimport { assertContainerExists } from './shell.js';\n\nexport interface RunInContainerOptions {\n /** Container root: `<MONOCEROS_HOME>/container/<name>/`. */\n root: string;\n command: string[];\n spawn?: DevcontainerSpawn;\n}\n\n// Run a one-off command inside the named container. Brings the\n// container up if needed (silently — only the inner command's stdio is\n// passed through), then forwards the command verbatim to\n// `devcontainer exec`. The inner command's exit code is propagated.\nexport async function runInContainer(\n opts: RunInContainerOptions,\n): Promise<number> {\n if (opts.command.length === 0) {\n throw new Error(\n 'No command provided. Usage: `monoceros run <containername> -- <cmd> [args…]`.',\n );\n }\n assertContainerExists(opts.root);\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n\n const upCode = await spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n { quiet: true },\n );\n if (upCode !== 0) return upCode;\n\n return spawnFn(\n [\n 'exec',\n '--workspace-folder',\n opts.root,\n '--mount-workspace-git-root=false',\n ...opts.command,\n ],\n opts.root,\n { interactive: true },\n );\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { containerDir } from '../config/paths.js';\nimport { runShell } from '../devcontainer/shell.js';\n\nexport const shellCommand = defineCommand({\n meta: {\n name: 'shell',\n group: 'run',\n description:\n 'Open an interactive bash session inside the named dev-container.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n async run({ args }) {\n try {\n const exitCode = await runShell({ root: containerDir(args.name) });\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStart } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const startCommand = defineCommand({\n meta: {\n name: 'start',\n group: 'run',\n description:\n 'Bring the named dev-container up via `devcontainer up` (workspace + runServices, postCreate, features).',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n run({ args }) {\n return dispatch(() => runStart({ root: containerDir(args.name) }));\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStatus } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const statusCommand = defineCommand({\n meta: {\n name: 'status',\n group: 'run',\n description:\n 'Show whether the compose services for the named dev-container are running.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n },\n run({ args }) {\n return dispatch(() =>\n runStatus({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n }),\n );\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStop } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const stopCommand = defineCommand({\n meta: {\n name: 'stop',\n group: 'run',\n description:\n 'Stop the compose services for the named dev-container. Volumes are preserved.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n },\n run({ args }) {\n return dispatch(() =>\n runStop({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n }),\n );\n },\n});\n"],"mappings":";;;AACA,SAAS,eAAe;;;AC8BxB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,aAAa;AAEnB,SAAS,QAAiB;AACxB,SAAO,QAAQ,OAAO,SAAS;AACjC;AAEA,SAAS,MAAM,SAAiB,OAAyB;AACvD,MAAI,CAAC,MAAM,EAAG,QAAO;AACrB,SAAO,MAAM,KAAK,EAAE,IAAI,OAAO;AACjC;AAEA,IAAM,OAAO,CAAC,MAAc,MAAM,GAAG,SAAS;AAC9C,IAAM,YAAY,CAAC,MAAc,MAAM,GAAG,cAAc;AACxD,IAAM,OAAO,CAAC,MAAc,MAAM,GAAG,SAAS;AAC9C,IAAM,OAAO,CAAC,MAAc,MAAM,GAAG,SAAS;AAQ9C,IAAM,SAAwD;AAAA,EAC5D,EAAE,KAAK,aAAa,OAAO,sBAAsB;AAAA,EACjD,EAAE,KAAK,OAAO,OAAO,gBAAgB;AAAA,EACrC,EAAE,KAAK,QAAQ,OAAO,qBAAqB;AAAA,EAC3C,EAAE,KAAK,aAAa,OAAO,YAAY;AAAA,EACvC,EAAE,KAAK,WAAW,OAAO,UAAU;AACrC;AAEA,SAAS,YACP,SACe;AACf,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,MAAqB,CAAC;AAC5B,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AACpD,UAAM,MAAO,UAAU,CAAC;AACxB,QAAI,KAAK;AAAA,MACP;AAAA,MACA,MAAO,IAAI,QAAgC;AAAA,MAC3C,UAAU,IAAI;AAAA,MACd,aAAa,IAAI;AAAA,MACjB,SAAS,IAAI;AAAA,MACb,OAAO,IAAI;AAAA,MACX,WAAW,IAAI;AAAA,IACjB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,KAA0B;AACjD,MAAI,IAAI,SAAS,UAAW,QAAO;AACnC,QAAM,OAAO,IAAI,aAAa,IAAI;AAClC,SAAO,KAAK,IAAI;AAClB;AAEA,SAAS,qBAAqB,KAAkB,YAA6B;AAC3E,QAAM,QAAkB,CAAC;AACzB,MAAI,IAAI,YAAa,OAAM,KAAK,IAAI,WAAW;AAC/C,MAAI,WAAY,OAAM,KAAK,KAAK,YAAY,CAAC;AAC7C,MAAI,IAAI,YAAY,UAAa,IAAI,SAAS,WAAW;AACvD,UAAM,KAAK,KAAK,aAAa,KAAK,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC;AAAA,EAC9D;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAKA,IAAM,UAAU;AAEhB,SAAS,WAAW,GAAmB;AACrC,SAAO,EAAE,QAAQ,SAAS,EAAE,EAAE;AAChC;AAEA,SAAS,gBAAwB;AAC/B,SAAO,QAAQ,OAAO,WAAW,QAAQ,OAAO,UAAU,KACtD,QAAQ,OAAO,UACf;AACN;AAQA,SAAS,SACP,MACA,OACA,oBACQ;AACR,MAAI,WAAW,IAAI,KAAK,MAAO,QAAO;AAKtC,QAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,aAAW,KAAK,OAAO;AACrB,QAAI,WAAW,OAAO,IAAI,WAAW,CAAC,KAAK,OAAO;AAChD,iBAAW;AACX;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAC9D,cAAU,EAAE,QAAQ,QAAQ,EAAE;AAAA,EAChC;AACA,MAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,QAAQ,QAAQ,QAAQ,EAAE,CAAC;AAC9D,SAAO,MAAM,IAAI,CAAC,GAAG,MAAO,MAAM,IAAI,IAAI,qBAAqB,CAAE,EAAE,KAAK,IAAI;AAC9E;AAOA,SAAS,WAAW,MAA+B,QAAwB;AACzE,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,MAAM,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;AAChE,QAAM,SAAS;AACf,QAAM,YACJ,cAAc,IAAI,OAAO,SAAS,aAAa,OAAO;AACxD,QAAM,qBAAqB,IAAI;AAAA,IAC7B,OAAO,SAAS,aAAa,OAAO;AAAA,EACtC;AACA,SAAO,KACJ,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;AACtB,UAAM,MAAM,IAAI,OAAO,aAAa,WAAW,IAAI,CAAC;AACpD,UAAM,UAAU,SAAS,OAAO,WAAW,kBAAkB;AAC7D,WAAO,GAAG,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,MAAM,GAAG,OAAO;AAAA,EAClD,CAAC,EACA,KAAK,IAAI;AACd;AAQA,SAAS,mBAAmB,KAAoC;AAC9D,QAAM,OAAQ,IAAI,eAAe,CAAC;AAClC,QAAM,MAAyB,CAAC;AAChC,aAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC9C,UAAM,OAAQ,KAAK,QAAQ,CAAC;AAK5B,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK;AAAA,MACP;AAAA,MACA,aAAa,KAAK,eAAe;AAAA,MACjC,OAAO,KAAK,SAAS;AAAA,IACvB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,SAAsC;AACjE,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,UAAU,KAAK,UAAU,CAAC,CAAC;AAKtC,QAAM,UAAU,oBAAI,IAA+B;AACnD,aAAWA,UAAS,SAAS;AAC3B,UAAM,MAAM,QAAQ,IAAIA,OAAM,KAAK,KAAK,CAAC;AACzC,QAAI,KAAKA,MAAK;AACd,YAAQ,IAAIA,OAAM,OAAO,GAAG;AAAA,EAC9B;AAEA,QAAM,gBAAgB,CAAC,OAAe,UAA6B;AACjE,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,KAAK,EAAE;AAOb,UAAM,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC;AACjC,UAAM,KAAK,EAAE;AACb,UAAM,OAAgC,MAAM,IAAI,CAAC,MAAM;AAAA,MACrD,KAAK,EAAE,IAAI;AAAA,MACX,EAAE;AAAA,IACJ,CAAC;AACD,UAAM,KAAK,WAAW,MAAM,EAAE,CAAC;AAAA,EACjC;AAEA,aAAW,EAAE,KAAK,MAAM,KAAK,QAAQ;AACnC,kBAAc,OAAO,QAAQ,IAAI,GAAG,KAAK,CAAC,CAAC;AAC3C,YAAQ,OAAO,GAAG;AAAA,EACpB;AAGA,aAAW,CAAC,UAAU,KAAK,KAAK,SAAS;AACvC,UAAM,QAAQ,aAAa,UAAU,UAAU;AAC/C,kBAAc,OAAO,KAAK;AAAA,EAC5B;AAEA,QAAM,KAAK,EAAE;AACb,SAAO;AACT;AAEO,SAAS,iBACd,KACA,aACQ;AACR,QAAM,OAAQ,IAAI,QAAQ,CAAC;AAK3B,QAAM,OAAO,YAAa,IAAI,QAAQ,CAAC,CAA6B;AACpE,QAAM,oBAAoB,mBAAmB,GAAG;AAEhD,QAAM,WAAW,YAAY,KAAK,GAAG,KAAK,KAAK,QAAQ;AAEvD,QAAM,cAAc,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY;AAC9D,QAAM,QAAQ,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY;AAOxD,QAAM,cAAwB,CAAC;AAC/B,aAAW,KAAK,aAAa;AAC3B,UAAM,aAAa,EAAE,aAAa,SAAS,EAAE,YAAY;AACzD,UAAM,IAAI,EAAE,KAAK,YAAY;AAC7B,gBAAY,KAAK,aAAa,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG;AAAA,EACnD;AACA,MAAI,kBAAkB,SAAS,EAAG,aAAY,KAAK,WAAW;AAC9D,MAAI,MAAM,SAAS,EAAG,aAAY,KAAK,WAAW;AAElD,QAAM,QAAkB,CAAC;AACzB,QAAM,UAAU,KAAK;AACrB,QAAM,SAAS,GAAG,KAAK,eAAe,EAAE,KAAK,QAAQ,GAAG,UAAU,KAAK,OAAO,KAAK,EAAE;AACrF,QAAM,KAAK,KAAK,SAAS,QAAQ,cAAc,GAAG,EAAE,CAAC,CAAC;AACtD,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ,GAAG,UAAU,KAAK,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,UAAU,GAAG,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;AAAA,EAC3E;AACA,QAAM,KAAK,EAAE;AAEb,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,KAAK,UAAU,KAAK,WAAW,CAAC,CAAC;AACvC,UAAM,KAAK,EAAE;AACb,UAAM,OAAgC,YAAY,IAAI,CAAC,MAAM;AAC3D,YAAM,aAAa,EAAE,aAAa,SAAS,EAAE,YAAY;AACzD,aAAO,CAAC,KAAK,EAAE,KAAK,YAAY,CAAC,GAAG,qBAAqB,GAAG,UAAU,CAAC;AAAA,IACzE,CAAC;AACD,UAAM,KAAK,WAAW,MAAM,IAAI,CAAC;AACjC,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,KAAK,UAAU,KAAK,SAAS,CAAC,CAAC;AACrC,UAAM,KAAK,EAAE;AACb,UAAM,OAAgC,MAAM,IAAI,CAAC,MAAM;AACrD,YAAM,aAAa,EAAE,aAAa,QAAQ,EAAE,YAAY;AACxD,YAAM,WACJ,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC,GAC1D,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE;AACpB,YAAM,QAAQ,CAAC,GAAG,SAAS,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,IAAI,gBAAgB,CAAC;AACxE,aAAO,CAAC,KAAK,KAAK,GAAG,qBAAqB,GAAG,UAAU,CAAC;AAAA,IAC1D,CAAC;AACD,UAAM,KAAK,WAAW,MAAM,IAAI,CAAC;AACjC,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,kBAAkB,SAAS,GAAG;AAChC,eAAW,QAAQ,oBAAoB,iBAAiB,GAAG;AACzD,YAAM,KAAK,IAAI;AAAA,IACjB;AACA,UAAM;AAAA,MACJ,OAAO,KAAK,GAAG,QAAQ,mBAAmB,CAAC;AAAA,IAC7C;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AASO,SAAS,kBACd,MACAC,OAC4C;AAC5C,QAAM,UAAU,KAAK,UAAU,CAAC,MAAM,MAAM,YAAY,MAAM,IAAI;AAClE,QAAM,eAAe,KAAK,QAAQ,IAAI;AACtC,MAAI,YAAY,GAAI,QAAO;AAC3B,MAAI,iBAAiB,MAAM,eAAe,QAAS,QAAO;AAI1D,QAAMC,SAAiB,CAAC;AACxB,QAAM,SAAS,KAAK;AAAA,IAClB;AAAA,IACA,iBAAiB,KAAK,KAAK,SAAS;AAAA,EACtC;AACA,MAAI,SAAqBD;AACzB,QAAM,YAAaA,MAAK,QAAQ,CAAC,GAAyB,QAAQ;AAClE,EAAAC,OAAK,KAAK,QAAQ;AAClB,aAAW,OAAO,QAAQ;AACxB,QAAI,IAAI,WAAW,GAAG,EAAG;AACzB,UAAM,OAAQ,OAAO,eAAe,CAAC;AACrC,QAAI,OAAO,MAAM;AACf,eAAS,KAAK,GAAG;AACjB,MAAAA,OAAK,KAAK,GAAG;AACb;AAAA,IACF;AAIA;AAAA,EACF;AACA,SAAO,EAAE,MAAAA,QAAM,KAAK,OAAO;AAC7B;AAMA,eAAsB,gBACpB,MACAD,OACkB;AAClB,QAAM,MAAM,kBAAkB,MAAMA,KAAI;AACxC,MAAI,CAAC,IAAK,QAAO;AAIjB,UAAQ,OAAO,MAAM,iBAAiB,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI;AAC/D,SAAO;AACT;;;AC7WA,IAAI,YAA+B,CAAC;AAE7B,SAAS,eAAe,UAG7B;AACA,QAAM,UAAU,SAAS,QAAQ,IAAI;AACrC,MAAI,YAAY,IAAI;AAClB,WAAO,EAAE,WAAW,CAAC,GAAG,QAAQ,GAAG,WAAW,CAAC,EAAE;AAAA,EACnD;AACA,SAAO;AAAA,IACL,WAAW,SAAS,MAAM,GAAG,OAAO;AAAA,IACpC,WAAW,SAAS,MAAM,UAAU,CAAC;AAAA,EACvC;AACF;AAEO,SAAS,kCAAwC;AAEtD,QAAM,WAAW,QAAQ,KAAK,MAAM,CAAC;AACrC,QAAM,QAAQ,eAAe,QAAQ;AACrC,UAAQ,OAAO,CAAC,GAAG,QAAQ,KAAK,MAAM,GAAG,CAAC,GAAG,GAAG,MAAM,SAAS;AAC/D,cAAY,MAAM;AACpB;AAEO,SAAS,eAAkC;AAChD,SAAO;AACT;;;ACzCA,SAAS,iBAAAE,uBAAqB;;;ACA9B,SAAS,qBAAqB;AAC9B,SAAS,WAAAC,gBAAe;;;ACDxB,SAAS,YAAYC,WAAU;AAC/B,SAAS,eAAe;AACxB,SAAS,mBAAmB;;;ACF5B,SAAS,YAAY,UAAU;AAC/B,SAAS,UAAU,qBAAqB;;;ACDxC,SAAS,SAAS;AAyBlB,IAAM,mBAAmB;AACzB,IAAM,sBAAsB;AAK5B,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAMvB,IAAM,cAAc;AASpB,IAAM,eAAe;AACrB,IAAM,kBAAkB;AAEjB,IAAM,QAAQ;AAAA,EACnB,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,UAAU;AAAA,EACV,aAAa;AACf;AAgBO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,IAAM,uBAA+D;AAAA,EAC1E,cAAc;AAAA,EACd,cAAc;AAAA,EACd,iBAAiB;AACnB;AAGO,IAAM,wBAAwB;AAE9B,IAAM,2BAA2B,EAAE,MAAM;AAAA,EAC9C,EAAE,OAAO;AAAA,EACT,EAAE,OAAO;AAAA,EACT,EAAE,QAAQ;AACZ,CAAC;AAEM,IAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,KAAK,EACF,OAAO,EACP;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAAA,EACF,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,wBAAwB,EAAE,SAAS;AACnE,CAAC;AAEM,IAAM,gBAAgB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACtB,OAAO,EACJ,OAAO,EACP,IAAI,CAAC,EACL,MAAM,8BAA8B,eAAe;AACxD,CAAC;AAEM,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,KAAK,EACF,OAAO,EACP;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAAA,EACF,MAAM,EACH,OAAO,EACP;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,GAAG;AAAA,IAC9D;AAAA,EACF,EACC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMZ,KAAK,EACF,OAAO;AAAA,IACN,MAAM,cAAc,SAAS;AAAA,EAC/B,CAAC,EACA,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASZ,UAAU,EAAE,KAAK,eAAe,EAAE,SAAS;AAC7C,CAAC;AAEM,IAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,UAAU,EACP,OAAO,EACP;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,SAAS;AACd,CAAC;AAEM,IAAM,uBAAuB,EAAE,OAAO;AAAA,EAC3C,eAAe,EAAE,QAAQ,qBAAqB;AAAA,EAC9C,MAAM,EACH,OAAO,EACP;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAAA,EACF,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,aAAa,EACV;AAAA,IACC,EACG,OAAO,EACP;AAAA,MACC;AAAA,MACA;AAAA,IACF;AAAA,EACJ,EACC,QAAQ,CAAC,CAAC;AAAA,EACb,UAAU,EAAE,MAAM,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAAA,EAChD,aAAa,EACV;AAAA,IACC,EACG,OAAO,EACP;AAAA,MACC;AAAA,MACA;AAAA,IACF;AAAA,EACJ,EACC,QAAQ,CAAC,CAAC;AAAA,EACb,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC/C,OAAO,EAAE,MAAM,eAAe,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC1C,kBAAkB,uBAAuB,QAAQ,CAAC,CAAC;AAAA,EACnD,KAAK,EACF,OAAO;AAAA,IACN,MAAM,cAAc,SAAS;AAAA,EAC/B,CAAC,EACA,SAAS;AACd,CAAC;AAcM,SAAS,eAAe,OAAgC;AAC7D,QAAM,SAAS,qBAAqB,UAAU,KAAK;AACnD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,UAAU;AACd,YAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG,IAAI;AAC7D,aAAO,OAAO,KAAK,KAAK,MAAM,OAAO;AAAA,IACvC,CAAC,EACA,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM;AAAA,EAA6B,MAAM,EAAE;AAAA,EACvD;AACA,SAAO,OAAO;AAChB;;;ADlNO,SAAS,YACd,UACA,SAAS,YACK;AACd,QAAM,MAAM,cAAc,UAAU,EAAE,cAAc,KAAK,CAAC;AAC1D,MAAI,IAAI,OAAO,SAAS,GAAG;AACzB,UAAM,QAAQ,IAAI,OAAO,CAAC;AAC1B,UAAM,IAAI,MAAM,uBAAuB,MAAM,KAAK,MAAM,OAAO,EAAE;AAAA,EACnE;AACA,QAAM,SAAS,eAAe,IAAI,KAAK,CAAC;AACxC,SAAO,EAAE,QAAQ,KAAK,OAAO;AAC/B;AAEA,eAAsB,WAAW,UAAyC;AACxE,QAAM,OAAO,MAAM,GAAG,SAAS,UAAU,MAAM;AAC/C,SAAO,YAAY,MAAM,QAAQ;AACnC;AAGO,SAAS,gBAAgB,KAAuB;AACrD,SAAO,OAAO,GAAG;AACnB;;;AE7CA,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AA6B9B,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB,KAAK,KAAK,aAAa,cAAc,WAAW;AACzE,IAAM,kBAAkB;AAExB,IAAI,sBAAqC;AACzC,IAAI,sBAAqC;AACzC,IAAI,qBAAgD;AAU7C,SAAS,gBAAwB;AACtC,MAAI,oBAAqB,QAAO;AAChC,MAAI,MAAM,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AACrD,SAAO,MAAM;AACX,QAAI,WAAW,KAAK,KAAK,KAAK,gBAAgB,CAAC,GAAG;AAChD,4BAAsB;AACtB,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,KAAK;AAClB,YAAM,IAAI;AAAA,QACR,yDAAyD,gBAAgB;AAAA,MAC3E;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAeO,SAAS,cAAc,OAA4B,CAAC,GAAW;AACpE,MAAI,CAAC,KAAK,SAAS,oBAAqB,QAAO;AAE/C,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,0BAAsB,KAAK,QAAQ,OAAO;AAC1C,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AACrD,SAAO,MAAM;AACX,UAAM,YAAY,KAAK,KAAK,KAAK,QAAQ;AACzC,QAAI,WAAW,KAAK,KAAK,WAAW,qBAAqB,CAAC,GAAG;AAC3D,4BAAsB;AACtB,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AAEA,wBAAsB,KAAK,KAAK,GAAG,QAAQ,GAAG,YAAY;AAC1D,SAAO;AACT;AAiBO,SAAS,wBAAuC;AACrD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,MAAI,MAAM,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AACrD,SAAO,MAAM;AACX,QAAI,WAAW,KAAK,KAAK,KAAK,eAAe,CAAC,GAAG;AAC/C,2BAAqB;AACrB,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,QAAI,WAAW,KAAK;AAClB,2BAAqB;AACrB,aAAO;AAAA,IACT;AACA,UAAM;AAAA,EACR;AACF;AAiBO,SAAS,cAAc,OAAe,cAAc,GAAW;AACpE,SAAO,KAAK,KAAK,MAAM,aAAa,YAAY;AAClD;AAUO,SAAS,mBAAmB,OAAe,cAAc,GAAW;AACzE,SAAO,KAAK,KAAK,MAAM,UAAU;AACnC;AAIO,SAAS,oBAAoB,OAAe,cAAc,GAAW;AAC1E,SAAO,KAAK,KAAK,MAAM,mBAAmB;AAC5C;AAEO,SAAS,oBACd,MACA,OAAe,cAAc,GACrB;AACR,SAAO,KAAK,KAAK,oBAAoB,IAAI,GAAG,GAAG,IAAI,MAAM;AAC3D;AAEO,SAAS,cAAc,OAAe,cAAc,GAAW;AACpE,SAAO,KAAK,KAAK,MAAM,WAAW;AACpC;AAEO,SAAS,aACd,MACA,OAAe,cAAc,GACrB;AACR,SAAO,KAAK,KAAK,cAAc,IAAI,GAAG,IAAI;AAC5C;AAEO,SAAS,oBAAoB,OAAe,cAAc,GAAW;AAC1E,SAAO,KAAK,KAAK,MAAM,sBAAsB;AAC/C;AAgBO,SAAS,WAAW,GAAmB;AAC5C,QAAM,OAAO,GAAG,QAAQ;AACxB,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,MAAM,KAAM,QAAO;AACvB,QAAM,SAAS,KAAK,SAAS,KAAK,GAAG,IAAI,OAAO,OAAO,KAAK;AAC5D,MAAI,EAAE,WAAW,MAAM,GAAG;AACxB,WAAO,MAAM,KAAK,MAAM,EAAE,MAAM,OAAO,MAAM;AAAA,EAC/C;AACA,SAAO;AACT;;;AC3MA,IAAM,qBAAqB;AAC3B,IAAM,WAAW,QAAQ,IAAI,+BAA+B,KAAK;AAC1D,IAAM,aACX,YAAY,SAAS,SAAS,IAAI,WAAW;AAaxC,IAAM,oBAAoB,oBAAI,IAAI,CAAC,MAAM,CAAC;AAE1C,IAAM,mBAA4D;AAAA,EACvE,MAAM,EAAE,IAAI,QAAQ,SAAS,wCAAwC;AAAA,EACrE,QAAQ,EAAE,IAAI,UAAU,SAAS,0CAA0C;AAAA,EAC3E,MAAM,EAAE,IAAI,QAAQ,SAAS,wCAAwC;AAAA,EACrE,IAAI,EAAE,IAAI,MAAM,SAAS,sCAAsC;AAAA,EAC/D,MAAM,EAAE,IAAI,QAAQ,SAAS,wCAAwC;AAAA,EACrE,QAAQ,EAAE,IAAI,UAAU,SAAS,0CAA0C;AAC7E;AASO,IAAM,mBAAmB;AAYzB,SAAS,kBAAkB,MAAmC;AACnE,QAAM,IAAI,iBAAiB,KAAK,IAAI;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,MAAM,EAAE,CAAC,GAAI,GAAI,EAAE,CAAC,MAAM,SAAY,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC,EAAG;AACzE;AAmCO,IAAM,kBAA0D;AAAA,EACrE,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,MACH,eAAe;AAAA,MACf,mBAAmB;AAAA,MACnB,aAAa;AAAA,IACf;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,WAAW;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,MACH,qBAAqB;AAAA,MACrB,gBAAgB;AAAA,IAClB;AAAA,IACA,WAAW;AAAA,EACb;AAAA,EACA,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,WAAW;AAAA,EACb;AACF;AAEO,SAAS,iBAA2B;AACzC,SAAO,CAAC,GAAG,mBAAmB,GAAG,OAAO,KAAK,gBAAgB,CAAC,EAAE,KAAK;AACvE;AAEO,SAAS,gBAA0B;AACxC,SAAO,OAAO,KAAK,eAAe,EAAE,KAAK;AAC3C;;;ACzIA,SAAS,cAAAC,aAAY,cAAc,YAAYC,WAAU;AACzD,OAAOC,WAAU;;;ACcjB,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAErB,IAAM,uBAAuB,IAAI;AAAA,EACtC,+CAA+C,oBAAoB,KAAK,mBAAmB;AAC7F;AAEO,IAAM,kCAAkC,IAAI;AAAA,EACjD,kCAAkC,oBAAoB,MAAM,mBAAmB;AACjF;AAOO,SAAS,sBAAsB,KAAsC;AAC1E,QAAM,QAAQ,qBAAqB,KAAK,GAAG;AAC3C,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,MAAM,MAAM,CAAC,EAAG;AAC3B;AASO,SAAS,4BAA4B,KAA4B;AACtE,QAAM,QAAQ,gCAAgC,KAAK,GAAG;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MAAM,CAAC;AACpB,QAAM,MAAM,MAAM,CAAC;AACnB,SAAO,2CAA2C,IAAI,IAAI,GAAG;AAC/D;;;AD/BA,IAAMC,uBAAsB;AAM5B,IAAMC,kBAAiB;AAOvB,IAAMC,kBAAiB;AAIvB,IAAMC,eAAc;AAMpB,IAAMC,gBAAe;AAUd,SAAS,eAAe,KAAqB;AAClD,QAAM,UAAU,KAAK,IAAI,IAAI,YAAY,GAAG,GAAG,IAAI,YAAY,GAAG,CAAC;AACnE,QAAM,OAAO,IAAI,MAAM,UAAU,CAAC;AAClC,SAAO,KAAK,QAAQ,UAAU,EAAE;AAClC;AAEO,SAAS,gBAAgB,MAA2B;AACzD,MAAI,CAAC,KAAK,QAAQ,CAAC,oBAAoB,KAAK,KAAK,IAAI,GAAG;AACtD,UAAM,IAAI;AAAA,MACR,0BAA0B,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AACA,aAAW,YAAY,KAAK,WAAW;AACrC,UAAM,SAAS,kBAAkB,QAAQ;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,0BAA0B,KAAK,UAAU,QAAQ,CAAC;AAAA,MACpD;AAAA,IACF;AACA,QAAI,CAAC,kBAAkB,IAAI,OAAO,IAAI,KAAK,CAAC,iBAAiB,OAAO,IAAI,GAAG;AACzE,YAAM,IAAI;AAAA,QACR,qBAAqB,OAAO,IAAI,YAAY,eAAe,EAAE,KAAK,IAAI,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,KAAK,UAAU;AAC/B,QAAI,CAAC,gBAAgB,GAAG,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,oBAAoB,GAAG,YAAY,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,KAAK,eAAe,CAAC,GAAG;AACxC,QAAI,CAACJ,qBAAoB,KAAK,GAAG,GAAG;AAClC,YAAM,IAAI;AAAA,QACR,6BAA6B,KAAK,UAAU,GAAG,CAAC;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,OAAO,KAAK,KAAK,YAAY,CAAC,CAAC,GAAG;AAClD,QAAI,CAACC,gBAAe,KAAK,GAAG,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,qCAAqC,KAAK,UAAU,GAAG,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACA,aAAW,OAAO,KAAK,eAAe,CAAC,GAAG;AACxC,QAAI,CAACC,gBAAe,KAAK,GAAG,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,UAAU,GAAG,CAAC;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,QAAQ,KAAK,SAAS,CAAC,GAAG;AACnC,QAAI,CAACC,aAAY,KAAK,KAAK,GAAG,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,qBAAqB,KAAK,UAAU,KAAK,GAAG,CAAC;AAAA,MAC/C;AAAA,IACF;AACA,QAAI,CAACC,cAAa,KAAK,KAAK,IAAI,GAAG;AACjC,YAAM,IAAI;AAAA,QACR,sBAAsB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AACA,QAAI,KAAK,KAAK,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,GAAG,GAAG;AACnE,YAAM,IAAI;AAAA,QACR,sBAAsB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,MACjD;AAAA,IACF;AACA,QAAI,cAAc,IAAI,KAAK,IAAI,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,wBAAwB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,MACnD;AAAA,IACF;AACA,kBAAc,IAAI,KAAK,IAAI;AAAA,EAC7B;AACF;AAIO,SAAS,iBAAiB,MAAoC;AACnE,QAAM,YAAY,CAAC,GAAG,IAAI,IAAI,KAAK,SAAS,CAAC,EAAE,KAAK;AACpD,MAAI,WAAW,CAAC,GAAG,IAAI,IAAI,KAAK,QAAQ,CAAC,EAAE,KAAK;AAChD,MAAI,KAAK,aAAa;AACpB,eAAW,SAAS,OAAO,CAAC,MAAM,MAAM,UAAU;AAAA,EACpD;AACA,QAAM,cAAc,CAAC,GAAG,IAAI,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK;AAG9D,QAAM,WAAW,KAAK,WAClB,OAAO;AAAA,IACL,OAAO,QAAQ,KAAK,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAAA,EACrE,IACA;AAGJ,QAAM,cAAc,KAAK,cACrB,CAAC,GAAG,IAAI,IAAI,KAAK,WAAW,CAAC,IAC7B;AAKJ,QAAM,QAAQ,KAAK,QACf,MAAM;AAAA,IACJ,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO;AAAA,EACnE,IACA;AACJ,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX;AAAA,IACA;AAAA,IACA,aAAa,KAAK;AAAA,IAClB,GAAI,YAAY,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,IAChD,GAAI,YAAY,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,IACnE,GAAI,eAAe,YAAY,SAAS,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA,IAC/D,GAAI,SAAS,MAAM,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,EAC/C;AACF;AAEO,SAAS,aAAa,MAA8B;AACzD,SAAO,KAAK,SAAS,SAAS;AAChC;AAuHO,SAAS,gBAAgB,MAAwC;AACtE,QAAM,WAA8B,CAAC;AAErC,aAAW,YAAY,KAAK,WAAW;AACrC,UAAM,SAAS,kBAAkB,QAAQ;AACzC,QAAI,CAAC,OAAQ;AAIb,QAAI,kBAAkB,IAAI,OAAO,IAAI,KAAK,OAAO,YAAY,QAAW;AACtE;AAAA,IACF;AACA,UAAMC,SAAQ,iBAAiB,OAAO,IAAI;AAC1C,QAAI,CAACA,OAAO;AACZ,UAAM,UAAkC,CAAC;AACzC,QAAI,OAAO,YAAY,OAAW,SAAQ,UAAU,OAAO;AAC3D,aAAS,KAAK;AAAA,MACZ,iBAAiBA,OAAM;AAAA,MACvB;AAAA,MACA,qBAAqB,CAAC;AAAA,MACtB,qBAAqB,CAAC;AAAA,IACxB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,eAAe,KAAK,YAAY,SAAS,GAAG;AACnD,aAAS,KAAK;AAAA,MACZ,iBAAiB;AAAA,MACjB,SAAS,EAAE,UAAU,KAAK,YAAY,KAAK,GAAG,EAAE;AAAA,MAChD,qBAAqB,CAAC;AAAA,MACtB,qBAAqB,CAAC;AAAA,IACxB,CAAC;AAAA,EACH;AACA,MAAI,KAAK,UAAU;AACjB,eAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,GAAG;AAC7D,YAAM,QAAQ,sBAAsB,MAAM;AAC1C,UAAI,OAAO;AACT,cAAM,OAAO,MAAM;AAMnB,cAAM,WAAW,sBAAsB;AACvC,cAAM,iBAAiB,WACnBC,MAAK,KAAK,UAAU,UAAU,YAAY,IAAI,IAC9C;AACJ,YAAI,kBAAkBC,YAAW,cAAc,GAAG;AAChD,gBAAM,EAAE,OAAO,MAAM,IAAI,0BAA0B,cAAc;AACjE,mBAAS,KAAK;AAAA,YACZ,iBAAiB,cAAc,IAAI;AAAA,YACnC;AAAA,YACA;AAAA,YACA,WAAW;AAAA,YACX,qBAAqB;AAAA,YACrB,qBAAqB;AAAA,UACvB,CAAC;AACD;AAAA,QACF;AAAA,MACF;AACA,eAAS,KAAK;AAAA,QACZ,iBAAiB;AAAA,QACjB;AAAA,QACA,qBAAqB,CAAC;AAAA,QACtB,qBAAqB,CAAC;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAYA,SAAS,0BAA0B,gBAGjC;AACA,QAAM,eAAeD,MAAK,KAAK,gBAAgB,2BAA2B;AAC1E,MAAI;AACF,UAAM,OAAO,aAAa,cAAc,MAAM;AAC9C,UAAM,SAAS,KAAK,MAAM,IAAI;AAM9B,WAAO;AAAA,MACL,OAAO,eAAe,OAAO,aAAa,GAAG,mBAAmB;AAAA,MAChE,OAAO,kBAAkB,OAAO,aAAa,GAAG,mBAAmB;AAAA,IACrE;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,EAChC;AACF;AAEA,SAAS,eAAe,KAAwB;AAC9C,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,SAAO,IAAI;AAAA,IACT,CAAC,MACC,OAAO,MAAM,YACb,EAAE,SAAS,KACX,CAAC,EAAE,WAAW,GAAG,KACjB,CAAC,EAAE,SAAS,IAAI,KAChB,gBAAgB,KAAK,CAAC;AAAA,EAC1B;AACF;AASA,SAAS,kBAAkB,KAAoC;AAC7D,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,QAAM,SAA+B,CAAC;AACtC,aAAWD,UAAS,KAAK;AACvB,QAAI,OAAOA,WAAU,UAAU;AAC7B,UAAI,mBAAmBA,MAAK,GAAG;AAC7B,eAAO,KAAK,EAAE,MAAMA,QAAO,gBAAgB,GAAG,CAAC;AAAA,MACjD;AACA;AAAA,IACF;AACA,QACEA,WAAU,QACV,OAAOA,WAAU,YACjB,UAAUA,UACV,OAAQA,OAA4B,SAAS,UAC7C;AACA,YAAM,IAAIA;AACV,UAAI,CAAC,mBAAmB,EAAE,IAAI,EAAG;AACjC,YAAM,iBACJ,OAAO,EAAE,mBAAmB,WAAW,EAAE,iBAAiB;AAC5D,aAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,GAAoB;AAC9C,SACE,EAAE,SAAS,KACX,CAAC,EAAE,WAAW,GAAG,KACjB,CAAC,EAAE,SAAS,IAAI,KAChB,gBAAgB,KAAK,CAAC;AAE1B;AAKA,IAAM,kBAAkB;AAEjB,SAAS,sBACd,MACA,aAAyB,WACP;AAClB,QAAM,mBAAmB,gBAAgB,IAAI;AAC7C,QAAM,WAAoD,CAAC;AAC3D,aAAW,KAAK,kBAAkB;AAChC,aAAS,EAAE,eAAe,IAAI,EAAE;AAAA,EAClC;AAEA,QAAM,gBACJ,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,EAAE,SAAS,IAAI;AAWpD,QAAM,cAAc,eAAe,aAAa,WAAW;AAW3D,QAAM,aAAuB,CAAC;AAC9B,aAAW,KAAK,kBAAkB;AAChC,UAAM,UAAU;AAAA,MACd,GAAG,EAAE;AAAA,MACL,GAAG,EAAE,oBAAoB,IAAI,CAACA,WAAUA,OAAM,IAAI;AAAA,IACpD;AACA,eAAW,OAAO,SAAS;AACzB,iBAAW;AAAA,QACT,wCAAwC,GAAG,sBAAsB,GAAG,aAAa,WAAW;AAAA,MAC9F;AAAA,IACF;AAAA,EACF;AAMA,MAAI,aAAa,IAAI,GAAG;AAItB,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,mBAAmB;AAAA,MACnB,SAAS;AAAA,MACT,GAAI,KAAK,SAAS,SAAS,IAAI,EAAE,aAAa,KAAK,SAAS,IAAI,CAAC;AAAA,MACjE,iBAAiB,eAAe,KAAK,IAAI;AAAA,MACzC,YAAY;AAAA,MACZ,cAAc,CAAC,KAAM,GAAI;AAAA,MACzB,mBAAmB;AAAA,MACnB,GAAI,iBAAiB,CAAC;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,SAAmB,CAAC,GAAG,UAAU;AACvC,QAAM,cAAc,OAAO,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;AAOtD,QAAM,sBACJ,eAAe,aACX;AAAA,IACE,gBAAgB,sDAAsD,KAAK,IAAI,aAAa,WAAW;AAAA,EACzG,IACA,CAAC;AAEP,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,GAAG;AAAA,IACH,GAAG;AAAA,IACH,SAAS,CAAC,qBAAqB;AAAA,IAC/B,cAAc,CAAC,KAAM,GAAI;AAAA,IACzB,mBAAmB;AAAA,IACnB,GAAI,iBAAiB,CAAC;AAAA,EACxB;AACF;AAYO,SAAS,iBACd,MACA,aAAyB,WACjB;AACR,QAAM,QAAkB,CAAC,WAAW;AACpC,QAAM,aAAa,eAAe;AAElC,QAAM,KAAK,cAAc;AACzB,QAAM,KAAK,cAAc,UAAU,EAAE;AACrC,QAAM,KAAK,+BAA+B;AAK1C,QAAM,KAAK,cAAc;AACzB,QAAM,KAAK,mBAAmB;AAC9B,QAAM,KAAK,cAAc;AACzB,MAAI,YAAY;AAId,UAAM,KAAK,oBAAoB;AAC/B,UAAM,KAAK,oBAAoB;AAC/B,UAAM,KAAK,+BAA+B,KAAK,IAAI,EAAE;AACrD,UAAM,KAAK,eAAe;AAC1B,UAAM,KAAK,kCAAkC;AAC7C,UAAM,KAAK,uBAAuB;AAAA,EACpC,OAAO;AACL,UAAM,KAAK,0BAA0B,KAAK,IAAI,SAAS;AAAA,EACzD;AAMA,QAAM,mBAAmB,gBAAgB,IAAI;AAC7C,aAAW,KAAK,kBAAkB;AAChC,UAAM,UAAU;AAAA,MACd,GAAG,EAAE;AAAA,MACL,GAAG,EAAE,oBAAoB,IAAI,CAACA,WAAUA,OAAM,IAAI;AAAA,IACpD;AACA,eAAW,OAAO,SAAS;AACzB,UAAI,YAAY;AACd,cAAM,KAAK,oBAAoB;AAC/B,cAAM,KAAK,2BAA2B,GAAG,EAAE;AAC3C,cAAM,KAAK,8BAA8B,GAAG,EAAE;AAC9C,cAAM,KAAK,eAAe;AAC1B,cAAM,KAAK,kCAAkC;AAC7C,cAAM,KAAK,uBAAuB;AAAA,MACpC,OAAO;AACL,cAAM,KAAK,mBAAmB,GAAG,eAAe,GAAG,EAAE;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AACA,aAAW,SAAS,KAAK,UAAU;AACjC,UAAM,MAAM,gBAAgB,KAAK;AACjC,QAAI,CAAC,IAAK;AACV,UAAM,KAAK,KAAK,IAAI,EAAE,GAAG;AACzB,UAAM,KAAK,cAAc,IAAI,KAAK,EAAE;AACpC,QAAI,IAAI,KAAK;AACX,YAAM,KAAK,kBAAkB;AAC7B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG,GAAG;AAC5C,cAAM,KAAK,SAAS,CAAC,KAAK,CAAC,EAAE;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,IAAI,WAAW;AAKjB,YAAM,KAAK,cAAc;AACzB,YAAM,KAAK,mBAAmB,IAAI,EAAE,IAAI,IAAI,SAAS,EAAE;AAAA,IACzD;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI;AAC5B;AAkBO,SAAS,uBAAuB,MAAwC;AAC7E,QAAM,UAAiC,CAAC,EAAE,MAAM,IAAI,CAAC;AAIrD,QAAM,cAAc,CAAC,GAAI,KAAK,SAAS,CAAC,CAAE,EAAE;AAAA,IAAK,CAAC,GAAG,MACnD,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EAC7B;AACA,aAAW,QAAQ,aAAa;AAK9B,UAAM,QAAQ,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK,KAAK;AACjD,YAAQ,KAAK,EAAE,MAAM,YAAY,KAAK,IAAI,IAAI,MAAM,MAAM,CAAC;AAAA,EAC7D;AACA,SAAO,EAAE,QAAQ;AACnB;AAQO,SAAS,sBAAsB,MAA6B;AACjE,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iDAAiD,KAAK,IAAI;AAAA,IAC1D;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,KAAK,eAAe,KAAK,YAAY,SAAS,GAAG;AACnD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,wBAAmB,KAAK,YAAY,MAAM;AAAA,IAC5C;AACA,eAAW,OAAO,KAAK,aAAa;AAClC,YAAM,KAAK,gBAAW,GAAG,KAAK,eAAe,GAAG,QAAQ;AAAA,IAC1D;AAAA,EACF;AAEA,MAAI,KAAK,SAAS,KAAK,MAAM,SAAS,GAAG;AACvC,UAAM,eAAe,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,IAAI,WAAW,UAAU,CAAC;AACxE,QAAI,cAAc;AAChB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,mEAAmE,KAAK,IAAI;AAAA,MAC9E;AAAA,IACF;AACA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,QAAQ,KAAK,OAAO;AAI7B,YAAM,SAAS,KAAK,KAAK,SAAS,GAAG,IACjC,KAAK,KAAK,MAAM,GAAG,KAAK,KAAK,YAAY,GAAG,CAAC,IAC7C;AACJ,UAAI,QAAQ;AACV,cAAM,KAAK,sBAAsB,MAAM,GAAG;AAAA,MAC5C;AACA,YAAM;AAAA,QACJ,uBAAuB,KAAK,IAAI;AAAA,QAChC,0BAAqB,KAAK,IAAI,SAAS,KAAK,GAAG;AAAA,QAC/C,gBAAgB,KAAK,GAAG,eAAe,KAAK,IAAI;AAAA,QAChD;AAAA,QACA,2BAAsB,KAAK,IAAI;AAAA,QAC/B;AAAA,MACF;AAQA,UAAI,KAAK,SAAS;AAChB,cAAM,WAAW,KAAK,QAAQ,KAAK,QAAQ,MAAM,KAAK;AACtD,cAAM,YAAY,KAAK,QAAQ,MAAM,QAAQ,MAAM,KAAK;AACxD,cAAM;AAAA,UACJ,oBAAoB,KAAK,IAAI,uBAAuB,QAAQ;AAAA,UAC5D,oBAAoB,KAAK,IAAI,wBAAwB,SAAS;AAAA,QAChE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI;AAC5B;AAEA,eAAsB,sBACpB,iBACA,MACe;AACf,QAAM,OAAOC,MAAK,KAAK,iBAAiB,gBAAgB;AACxD,QAAME,IAAG,UAAU,MAAM,sBAAsB,IAAI,CAAC;AACpD,QAAMA,IAAG,MAAM,MAAM,GAAK;AAC5B;AAsBA,eAAsB,cACpB,MACA,WACA,eAA4C,CAAC,GAC9B;AACf,QAAM,aAAyB,aAAa,cAAc;AAC1D,QAAM,kBAAkBF,MAAK,KAAK,WAAW,eAAe;AAC5D,QAAM,eAAeA,MAAK,KAAK,WAAW,YAAY;AACtD,QAAM,cAAcA,MAAK,KAAK,WAAW,UAAU;AACnD,QAAM,UAAUA,MAAK,KAAK,WAAW,MAAM;AAC3C,QAAM,UAAUA,MAAK,KAAK,WAAW,MAAM;AAC3C,QAAME,IAAG,MAAM,iBAAiB,EAAE,WAAW,KAAK,CAAC;AACnD,QAAMA,IAAG,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAChD,QAAMA,IAAG,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC/C,QAAMA,IAAG,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC3C,MAAI,aAAa,IAAI,GAAG;AACtB,UAAMA,IAAG,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAI3C,eAAW,SAAS,KAAK,UAAU;AACjC,YAAM,MAAM,gBAAgB,KAAK;AACjC,UAAI,KAAK,WAAW;AAClB,cAAMA,IAAG,MAAMF,MAAK,KAAK,SAAS,IAAI,EAAE,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAeA,QAAM,qBAAqBA,MAAK,KAAK,WAAW,YAAY;AAC5D,QAAME,IAAG,UAAU,oBAAoB,gCAAgC;AAIvE,QAAM,UAAUF,MAAK,KAAK,aAAa,UAAU;AACjD,MAAI,CAACC,YAAW,OAAO,GAAG;AACxB,UAAMC,IAAG,UAAU,SAAS,EAAE;AAAA,EAChC;AAIA,QAAMA,IAAG;AAAA,IACPF,MAAK,KAAK,cAAc,YAAY;AAAA,IACpC;AAAA,EACF;AAEA,QAAM,mBAAmB,sBAAsB,MAAM,UAAU;AAC/D,QAAME,IAAG;AAAA,IACPF,MAAK,KAAK,iBAAiB,mBAAmB;AAAA,IAC9C,KAAK,UAAU,kBAAkB,MAAM,CAAC,IAAI;AAAA,EAC9C;AAYA,QAAM,cAAcA,MAAK,KAAK,iBAAiB,UAAU;AACzD,MAAIC,YAAW,WAAW,GAAG;AAC3B,UAAMC,IAAG,GAAG,aAAa,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC3D;AACA,QAAM,mBAAmB,gBAAgB,IAAI;AAC7C,aAAW,KAAK,kBAAkB;AAChC,QAAI,CAAC,EAAE,kBAAkB,CAAC,EAAE,UAAW;AACvC,UAAM,OAAOF,MAAK,KAAK,aAAa,EAAE,SAAS;AAC/C,UAAME,IAAG,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;AACxC,UAAMA,IAAG,GAAG,EAAE,gBAAgB,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA,EACzD;AASA,aAAW,KAAK,kBAAkB;AAChC,eAAW,OAAO,EAAE,qBAAqB;AACvC,YAAMA,IAAG,MAAMF,MAAK,KAAK,SAAS,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7D;AACA,eAAWD,UAAS,EAAE,qBAAqB;AACzC,YAAM,WAAWC,MAAK,KAAK,SAASD,OAAM,IAAI;AAC9C,YAAMG,IAAG,MAAMF,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,UAAI,CAACC,YAAW,QAAQ,GAAG;AAIzB,cAAMC,IAAG,UAAU,UAAUH,OAAM,cAAc;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,sBAAsB,iBAAiB,IAAI;AAEjD,QAAM,cAAcC,MAAK,KAAK,iBAAiB,cAAc;AAC7D,MAAI,aAAa,IAAI,GAAG;AACtB,UAAME,IAAG,UAAU,aAAa,iBAAiB,MAAM,UAAU,CAAC;AAAA,EACpE,WAAWD,YAAW,WAAW,GAAG;AAGlC,UAAMC,IAAG,GAAG,WAAW;AAAA,EACzB;AAEA,QAAMA,IAAG;AAAA,IACPF,MAAK,KAAK,WAAW,GAAG,KAAK,IAAI,iBAAiB;AAAA,IAClD,KAAK,UAAU,uBAAuB,IAAI,GAAG,MAAM,CAAC,IAAI;AAAA,EAC1D;AACF;;;AE/6BA,SAAwB,OAAO,UAAU,OAAO,SAAS,eAAe;AAsBxE,SAAS,UAAU,KAAe,KAAsB;AACtD,QAAM,WAAW,IAAI,IAAI,KAAK,IAAI;AAClC,MAAI,YAAY,MAAM,QAAQ,EAAG,QAAO;AACxC,QAAM,MAAM,IAAI,QAAQ;AACxB,MAAI,IAAI,KAAK,GAAG;AAChB,SAAO;AACT;AAGA,SAAS,cAAc,KAAe,KAAmB;AACvD,QAAM,OAAO,IAAI,IAAI,KAAK,IAAI;AAC9B,MAAI,QAAQ,MAAM,IAAI,KAAK,KAAK,MAAM,WAAW,GAAG;AAClD,QAAI,OAAO,GAAG;AAAA,EAChB;AACF;AAGA,SAAS,YAAY,MAAwB;AAC3C,SAAO,SAAS,IAAI,IAAI,KAAK,QAAQ;AACvC;AAEO,SAAS,iBAAiB,KAAe,MAAuB;AACrE,QAAM,MAAM,UAAU,KAAK,WAAW;AACtC,MAAI,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,IAAI,EAAG,QAAO;AAC3D,MAAI,IAAI,IAAI;AACZ,SAAO;AACT;AAEO,SAAS,gBAAgB,KAAe,SAA0B;AACvE,QAAM,MAAM,UAAU,KAAK,UAAU;AACrC,MAAI,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO,EAAG,QAAO;AAC9D,MAAI,IAAI,OAAO;AACf,SAAO;AACT;AAEO,SAAS,oBACd,KACA,UACS;AACT,QAAM,MAAM,UAAU,KAAK,aAAa;AACxC,MAAI,UAAU;AACd,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,GAAG,EAAG;AACnD,QAAI,IAAI,GAAG;AACX,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,KAAe,KAAsB;AACtE,QAAM,MAAM,UAAU,KAAK,aAAa;AACxC,MAAI,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,GAAG,EAAG,QAAO;AAC1D,MAAI,IAAI,GAAG;AACX,SAAO;AACT;AAQO,SAAS,gBACd,KACA,KACA,UAA0B,CAAC,GAClB;AACT,QAAM,MAAM,UAAU,KAAK,UAAU;AACrC,aAAW,QAAQ,IAAI,OAAO;AAC5B,QAAI,CAAC,MAAM,IAAI,EAAG;AAClB,UAAM,UAAU,KAAK,IAAI,KAAK;AAC9B,QAAI,YAAY,IAAK;AAIrB,UAAM,SAAS,KAAK,KAAK,GAAG;AAC5B,UAAM,aAAa,OAAO,WAAW,CAAC;AACtC,QAAI,KAAK,UAAU,UAAU,MAAM,KAAK,UAAU,OAAO,GAAG;AAC1D,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR,WAAW,GAAG,8FAA8F,GAAG;AAAA,IACjH;AAAA,EACF;AACA,QAAMG,SAAQ,IAAI,QAAQ;AAC1B,EAAAA,OAAM,IAAI,OAAO,GAAG;AACpB,MAAI,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AACnC,IAAAA,OAAM,IAAI,WAAW,OAAO;AAAA,EAC9B;AACA,MAAI,IAAIA,MAAK;AACb,SAAO;AACT;AAmBO,SAAS,aAAa,KAAe,MAA0B;AACpE,QAAM,MAAM,UAAU,KAAK,OAAO;AAClC,aAAW,QAAQ,IAAI,OAAO;AAC5B,QAAI,CAAC,MAAM,IAAI,EAAG;AAClB,UAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,QAAI,QAAQ,KAAK,IAAK;AACtB,UAAM,eAAe,KAAK,IAAI,MAAM;AACpC,UAAM,gBACJ,OAAO,iBAAiB,WACpB,eACA,eAAe,GAAa;AAClC,QAAI,kBAAkB,KAAK,KAAM;AAIjC,UAAM,cAAc,KAAK,IAAI,OAAO,IAAI;AACxC,UAAM,eACJ,eAAe,MAAM,WAAW,IAAI,YAAY,IAAI,QAAQ,IAAI,IAAI;AACtE,UAAM,eACJ,gBAAgB,MAAM,YAAY,IAAI,aAAa,IAAI,MAAM,IAAI;AACnE,UAAM,gBACJ,gBAAgB,MAAM,YAAY,IAAI,aAAa,IAAI,OAAO,IAAI;AACpE,UAAM,kBACJ,OAAO,iBAAiB,YAAY,OAAO,kBAAkB,WACzD,EAAE,MAAM,cAAc,OAAO,cAAc,IAC3C;AACN,UAAM,eACH,iBAAiB,QAAQ,WAAW,KAAK,SAAS,QAAQ,UAC1D,iBAAiB,SAAS,WAAW,KAAK,SAAS,SAAS;AAC/D,UAAM,mBAAmB,KAAK,IAAI,UAAU;AAC5C,UAAM,gBACH,OAAO,qBAAqB,WAAW,mBAAmB,WAC1D,KAAK,YAAY;AACpB,QAAI,eAAe,cAAc;AAC/B,aAAO;AAAA,IACT;AAIA,QAAI,KAAK,SAAS;AAChB,YAAM,SAAS,IAAI,QAAQ;AAC3B,YAAM,UAAU,IAAI,QAAQ;AAC5B,cAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI;AACrC,cAAQ,IAAI,SAAS,KAAK,QAAQ,KAAK;AACvC,aAAO,IAAI,QAAQ,OAAO;AAC1B,WAAK,IAAI,OAAO,MAAM;AAAA,IACxB,OAAO;AACL,WAAK,OAAO,KAAK;AAAA,IACnB;AACA,QAAI,KAAK,UAAU;AACjB,WAAK,IAAI,YAAY,KAAK,QAAQ;AAAA,IACpC,OAAO;AACL,WAAK,OAAO,UAAU;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AACA,QAAMA,SAAQ,IAAI,QAAQ;AAC1B,EAAAA,OAAM,IAAI,OAAO,KAAK,GAAG;AAGzB,MAAI,KAAK,SAAS,eAAe,KAAK,GAAG,GAAG;AAC1C,IAAAA,OAAM,IAAI,QAAQ,KAAK,IAAI;AAAA,EAC7B;AACA,MAAI,KAAK,SAAS;AAChB,UAAM,SAAS,IAAI,QAAQ;AAC3B,UAAM,UAAU,IAAI,QAAQ;AAC5B,YAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI;AACrC,YAAQ,IAAI,SAAS,KAAK,QAAQ,KAAK;AACvC,WAAO,IAAI,QAAQ,OAAO;AAC1B,IAAAA,OAAM,IAAI,OAAO,MAAM;AAAA,EACzB;AACA,MAAI,KAAK,UAAU;AACjB,IAAAA,OAAM,IAAI,YAAY,KAAK,QAAQ;AAAA,EACrC;AACA,MAAI,IAAIA,MAAK;AACb,SAAO;AACT;AAOO,SAAS,sBAAsB,KAAe,MAAuB;AAC1E,SAAO,oBAAoB,KAAK,aAAa,IAAI;AACnD;AAEO,SAAS,qBAAqB,KAAe,SAA0B;AAC5E,SAAO,oBAAoB,KAAK,YAAY,OAAO;AACrD;AAEO,SAAS,wBAAwB,KAAe,KAAsB;AAC3E,SAAO,oBAAoB,KAAK,eAAe,GAAG;AACpD;AAGO,SAAS,yBACd,KACA,UACS;AACT,MAAI,UAAU;AACd,aAAW,OAAO,UAAU;AAC1B,QAAI,wBAAwB,KAAK,GAAG,EAAG,WAAU;AAAA,EACnD;AACA,SAAO;AACT;AAEO,SAAS,wBAAwB,KAAe,KAAsB;AAC3E,SAAO,oBAAoB,KAAK,eAAe,GAAG;AACpD;AAEO,SAAS,qBAAqB,KAAe,KAAsB;AACxE,QAAM,MAAM,IAAI,IAAI,YAAY,IAAI;AACpC,MAAI,CAAC,OAAO,CAAC,MAAM,GAAG,EAAG,QAAO;AAChC,QAAM,MAAM,IAAI,MAAM,UAAU,CAAC,MAAM,MAAM,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,GAAG;AACvE,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,OAAO,KAAK,CAAC;AACvB,gBAAc,KAAK,UAAU;AAC7B,SAAO;AACT;AAQO,SAAS,kBAAkB,KAAe,WAA4B;AAC3E,QAAM,MAAM,IAAI,IAAI,SAAS,IAAI;AACjC,MAAI,CAAC,OAAO,CAAC,MAAM,GAAG,EAAG,QAAO;AAChC,QAAM,MAAM,IAAI,MAAM,UAAU,CAAC,SAAS;AACxC,QAAI,CAAC,MAAM,IAAI,EAAG,QAAO;AACzB,UAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,QAAI,QAAQ,UAAW,QAAO;AAC9B,UAAMC,SAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,gBACJ,OAAOA,WAAS,WACZA,SACA,OAAO,QAAQ,WACb,eAAe,GAAG,IAClB;AACR,WAAO,kBAAkB;AAAA,EAC3B,CAAC;AACD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,OAAO,KAAK,CAAC;AACvB,gBAAc,KAAK,OAAO;AAC1B,SAAO;AACT;AAEA,SAAS,oBACP,KACA,KACA,OACS;AACT,QAAM,MAAM,IAAI,IAAI,KAAK,IAAI;AAC7B,MAAI,CAAC,OAAO,CAAC,MAAM,GAAG,EAAG,QAAO;AAChC,QAAM,MAAM,IAAI,MAAM,UAAU,CAAC,MAAM,YAAY,CAAC,MAAM,KAAK;AAC/D,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,OAAO,KAAK,CAAC;AACvB,gBAAc,KAAK,GAAG;AACtB,SAAO;AACT;;;AP3JO,SAAS,eAAe,OAAgD;AAC7E,MACE,CAAC,kBAAkB,IAAI,MAAM,QAAQ,KACrC,CAAC,iBAAiB,MAAM,QAAQ,GAChC;AACA,UAAM,IAAI;AAAA,MACR,qBAAqB,MAAM,QAAQ,YAAY,eAAe,EAAE,KAAK,IAAI,CAAC;AAAA,IAC5E;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,iBAAiB,KAAK,MAAM,QAAQ,CAAC;AACrE;AAEO,SAAS,cAAc,OAA+C;AAC3E,MAAI,CAAC,gBAAgB,MAAM,OAAO,GAAG;AACnC,UAAM,IAAI;AAAA,MACR,oBAAoB,MAAM,OAAO,YAAY,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,gBAAgB,KAAK,MAAM,OAAO,CAAC;AACnE;AAEO,SAAS,kBACd,OACuB;AACvB,MAAI,MAAM,SAAS,WAAW,GAAG;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,oBAAoB,KAAK,MAAM,QAAQ,CAAC;AACxE;AAEA,eAAsB,WAAW,OAA4C;AAC3E,QAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAMC,UAAQ,MAAM,QAAQ,eAAe,GAAG,GAAG,KAAK;AAGtD,QAAM,UACJ,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,KAAK,EAAE,SAAS;AACrE,QAAM,WACJ,OAAO,MAAM,aAAa,YAAY,MAAM,SAAS,KAAK,EAAE,SAAS;AACvE,MAAI,YAAY,UAAU;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAYA,QAAM,mBAAmB,kBAAkB,MAAM,QAAQ;AACzD,MAAI;AACJ,MAAI;AACF,WAAO,IAAI,WAAW,UAAU,IAAI,IAAI,IAAI,GAAG,EAAE,WAAW;AAAA,EAC9D,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,YAAY,OAAO,qBAAqB,KAAK,YAAY,CAAC,IAAI;AACpE,MAAI,QAAQ,CAAC,aAAa,CAAC,kBAAkB;AAC3C,UAAM,IAAI;AAAA,MACR,SAAS,IAAI;AAAA,IACf;AAAA,EACF;AACA,MAAI,aAAa,oBAAoB,qBAAqB,WAAW;AACnE,UAAM,IAAI;AAAA,MACR,cAAc,gBAAgB,sBAAsB,IAAI,uBAAuB,SAAS;AAAA,IAC1F;AAAA,EACF;AAKA,QAAM,kBACJ,CAAC,aAAa,mBAAmB,mBAAmB;AACtD,QAAMC,SAAmB;AAAA,IACvB;AAAA,IACA,MAAAD;AAAA,IACA,GAAI,WAAW,WACX;AAAA,MACE,SAAS;AAAA,QACP,MAAM,MAAM,QAAS,KAAK;AAAA,QAC1B,OAAO,MAAM,SAAU,KAAK;AAAA,MAC9B;AAAA,IACF,IACA,CAAC;AAAA,IACL,GAAI,kBAAkB,EAAE,UAAU,gBAAgB,IAAI,CAAC;AAAA,EACzD;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,aAAa,KAAKC,MAAK,CAAC;AACxD;AAEA,SAAS,kBAAkB,KAAmD;AAC5E,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,UAAU,QAAQ,YAAY;AACpC,MAAI,CAAE,gBAAsC,SAAS,OAAO,GAAG;AAC7D,UAAM,IAAI;AAAA,MACR,6BAA6B,KAAK,UAAU,GAAG,CAAC,cAAc,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAC1F;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,cAAc,OAA+C;AAC3E,QAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,mBAAmB,KAAK,GAAG,CAAC;AAC5D;AAEO,SAAS,cAAc,OAA+C;AAC3E,QAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,gBAAgB,KAAK,KAAK,MAAM,WAAW,CAAC,CAAC,CAAC;AAC9E;AAIO,SAAS,kBACd,OACuB;AACvB,SAAO,OAAO,OAAO,CAAC,QAAQ,sBAAsB,KAAK,MAAM,QAAQ,CAAC;AAC1E;AAEO,SAAS,iBACd,OACuB;AACvB,SAAO,OAAO,OAAO,CAAC,QAAQ,qBAAqB,KAAK,MAAM,OAAO,CAAC;AACxE;AAEO,SAAS,qBACd,OACuB;AACvB,MAAI,MAAM,SAAS,WAAW,GAAG;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,yBAAyB,KAAK,MAAM,QAAQ,CAAC;AAC7E;AAEO,SAAS,iBACd,OACuB;AACvB,QAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,qBAAqB,KAAK,GAAG,CAAC;AAC9D;AAEO,SAAS,iBACd,OACuB;AACvB,QAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,wBAAwB,KAAK,GAAG,CAAC;AACjE;AAEO,SAAS,cAAc,OAA+C;AAC3E,QAAM,SAAS,MAAM,OAAO,KAAK;AACjC,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,CAAC,QAAQ,kBAAkB,KAAK,MAAM,CAAC;AAC9D;AAIA,eAAe,OACb,MACA,OACuB;AACvB,MAAI,CAAC,MAAM,aAAa,KAAK,KAAK,IAAI,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,2BAA2B,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACtD;AAAA,EACF;AACA,QAAM,OAAO,KAAK,iBAAiB,cAAqB;AACxD,QAAM,UAAU,oBAAoB,KAAK,MAAM,IAAI;AACnD,QAAM,SAAS,KAAK,UAAU,cAAc;AAE5C,MAAI;AACJ,MAAI;AACF,cAAU,MAAMC,IAAG,SAAS,SAAS,MAAM;AAAA,EAC7C,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,mBAAmB,OAAO,qCAAqC,KAAK,IAAI;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,SAAS,YAAY,SAAS,OAAO;AAC3C,QAAM,UAAU,MAAM,OAAO,GAAG;AAEhC,MAAI,CAAC,SAAS;AACZ,WAAO,KAAK,wDAAmD;AAC/D,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAKA,QAAM,UAAU,gBAAgB,OAAO,GAAG;AAC1C,cAAY,SAAS,OAAO;AAE5B,QAAM,MAAM,KAAK,WAAW,CAAC,SAAS,QAAQ,OAAO,MAAM,OAAO,IAAI;AACtE,MAAI,YAAY,SAAS,SAAS,SAAS,UAAU,OAAO,CAAC;AAE7D,MAAI,CAAC,KAAK,KAAK;AACb,UAAM,UAAU,KAAK,WAAW;AAChC,UAAM,KAAK,MAAM,QAAQ,iCAAiC;AAC1D,QAAI,CAAC,IAAI;AACP,aAAO,KAAK,4CAA4C;AACxD,aAAO,EAAE,QAAQ,UAAU;AAAA,IAC7B;AAAA,EACF;AAEA,QAAMA,IAAG,UAAU,SAAS,SAAS,MAAM;AAC3C,SAAO,QAAQ,WAAW,OAAO,GAAG;AACpC,SAAO;AAAA,IACL,yBAAyB,KAAK,IAAI;AAAA,EACpC;AACA,SAAO,EAAE,QAAQ,WAAW,cAAc,CAAC,OAAO,EAAE;AACtD;AAEA,SAAS,gBAA8B;AACrC,SAAO;AAAA,IACL,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC;AAAA,IAC3B,SAAS,CAAC,MAAM,QAAQ,QAAQ,CAAC;AAAA,IACjC,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC;AAAA,EAC7B;AACF;AAEA,IAAM,iBAA4B,OAAO,YAAY;AACnD,QAAM,SAAS,MAAM,QAAQ,OAAO,SAAS;AAAA,IAC3C,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AACD,SAAO,WAAW;AACpB;;;AD/YO,IAAM,wBAAwB,cAAc;AAAA,EACjD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,UAAM,WAAW,CAAC,GAAG,aAAa,CAAC;AACnC,QAAI,SAAS,WAAW,GAAG;AACzB,MAAAC,SAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI;AACF,YAAM,SAAS,MAAM,kBAAkB;AAAA,QACrC,MAAM,KAAK;AAAA,QACX;AAAA,QACA,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAA,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AS9CD,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AAKjB,IAAM,oBAAoBC,eAAc;AAAA,EAC7C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACJ,QAAI;AACF,gBAAU,wBAAwB,aAAa,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV;AAAA,QACA,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAA,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;AAOD,SAAS,wBAAwB,QAA2C;AAC1E,QAAM,SAAyB,CAAC;AAChC,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,QAAQ,GAAG;AAC/B,QAAI,SAAS,GAAG;AACd,YAAM,IAAI;AAAA,QACR,mBAAmB,KAAK,UAAU,KAAK,CAAC;AAAA,MAC1C;AAAA,IACF;AACA,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK;AAChC,UAAM,MAAM,MAAM,MAAM,QAAQ,CAAC;AACjC,WAAO,GAAG,IAAI,OAAO,GAAG;AAAA,EAC1B;AACA,SAAO;AACT;AAEA,SAAS,OAAO,OAA0C;AACxD,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,QAAS,QAAO;AAC9B,MAAI,UAAU,KAAK,KAAK,GAAG;AACzB,UAAM,IAAI,OAAO,KAAK;AACtB,QAAI,OAAO,cAAc,CAAC,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;;;ACrFA,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AAGjB,IAAM,oBAAoBC,eAAc;AAAA,EAC7C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aACE;AAAA,MACF,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI,CAAC,KAAK,KAAK;AACb,2BAAqB,KAAK,GAAG;AAAA,IAC/B;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;AAED,SAAS,qBAAqB,KAAmB;AAC/C,QAAM,IAAI,CAAC,SAAiB,QAAQ,OAAO,MAAM,OAAO,IAAI;AAC5D,IAAE,EAAE;AACJ,IAAE,gEAAiD;AACnD,IAAE,EAAE;AACJ,IAAE,UAAU,GAAG,EAAE;AACjB,IAAE,EAAE;AACJ,IAAE,wEAAwE;AAC1E;AAAA,IACE;AAAA,EACF;AACA;AAAA,IACE;AAAA,EACF;AACA,IAAE,uCAAuC;AACzC,IAAE,EAAE;AACJ,IAAE,4BAA4B;AAC9B,IAAE,8DAA8D;AAChE;AAAA,IACE;AAAA,EACF;AACA,IAAE,mEAAmE;AACrE;AAAA,IACE;AAAA,EACF;AACA,IAAE,EAAE;AACN;;;AC5EA,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AAGjB,IAAM,iBAAiBC,eAAc;AAAA,EAC1C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,WAAW;AAAA,QAC9B,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,GAAI,OAAO,KAAK,SAAS,WAAW,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,QAC3D,GAAI,OAAO,KAAK,UAAU,MAAM,WAC5B,EAAE,SAAS,KAAK,UAAU,EAAE,IAC5B,CAAC;AAAA,QACL,GAAI,OAAO,KAAK,WAAW,MAAM,WAC7B,EAAE,UAAU,KAAK,WAAW,EAAE,IAC9B,CAAC;AAAA,QACL,GAAI,OAAO,KAAK,aAAa,WACzB,EAAE,UAAU,KAAK,SAAS,IAC1B,CAAC;AAAA,QACL,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC1ED,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AAGjB,IAAM,qBAAqBC,eAAc;AAAA,EAC9C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,eAAe;AAAA,QAClC,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC5CD,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,gBAAe;AAGjB,IAAM,oBAAoBC,eAAc;AAAA,EAC7C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,SAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC3CD,SAAS,iBAAAC,sBAAqB;;;ACA9B,SAAS,cAAAC,aAAY,YAAYC,WAAU;AAC3C,SAAS,WAAAC,iBAAe;;;ACDxB,SAAS,YAAYC,WAAU;AAC/B,SAAS,KAAAC,UAAS;AAClB,SAAS,iBAAAC,sBAAqB;AAiB9B,IAAM,iBAAiB;AAWhB,IAAM,wBAAwBC,GAAE,OAAO;AAAA,EAC5C,eAAeA,GAAE,QAAQ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,UAAUA,GACP,OAAO;AAAA,IACN,KAAKA,GACF,OAAO;AAAA,MACN,MAAM,cAAc,SAAS;AAAA,IAC/B,CAAC,EACA,SAAS;AAAA,IACZ,UAAUA,GACP;AAAA,MACCA,GACG,OAAO,EACP;AAAA,QACC,MAAM;AAAA,QACN;AAAA,MACF;AAAA,MACFA,GAAE,OAAOA,GAAE,OAAO,GAAG,wBAAwB;AAAA,IAC/C,EACC,SAAS;AAAA,EACd,CAAC,EACA,QAAQ;AACb,CAAC;AAeD,eAAsB,oBACpB,OAAmC,CAAC,GACE;AACtC,QAAM,OAAO,KAAK,iBAAiB,cAAc;AACjD,QAAM,WAAW,oBAAoB,IAAI;AACzC,MAAI;AACJ,MAAI;AACF,WAAO,MAAMC,IAAG,SAAS,UAAU,MAAM;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,MAAMC,eAAc,MAAM,EAAE,cAAc,KAAK,CAAC;AACtD,MAAI,IAAI,OAAO,SAAS,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,uBAAuB,QAAQ,KAAK,IAAI,OAAO,CAAC,EAAG,OAAO;AAAA,IAC5D;AAAA,EACF;AACA,QAAM,SAAS,sBAAsB,UAAU,IAAI,KAAK,CAAC;AACzD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,UAAU;AACd,YAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG,IAAI;AAC7D,aAAO,OAAO,KAAK,KAAK,MAAM,OAAO;AAAA,IACvC,CAAC,EACA,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,WAAW,QAAQ;AAAA,EAAM,MAAM;AAAA;AAAA,MAAW,SAAS;AAAA,QACjD;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,OAAO;AAChB;;;ACzGA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AA2BV,SAAS,eAAe,MAIjB;AACZ,SAAO;AAAA,IACL,eAAe;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,qBAAqB,KAAK;AAAA,IAC1B,iBAAiB,KAAK,OAAO,oBAAI,KAAK,GAAG,YAAY;AAAA,EACvD;AACF;AAEO,SAAS,cAAc,WAA2B;AACvD,SAAOC,MAAK,KAAK,WAAW,cAAc,YAAY;AACxD;AAEA,eAAsB,cACpB,WACgC;AAChC,MAAI;AACF,UAAM,UAAU,MAAMC,IAAG,SAAS,cAAc,SAAS,GAAG,MAAM;AAClE,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,eACpB,WACA,OACe;AACf,QAAM,eAAeD,MAAK,KAAK,WAAW,YAAY;AACtD,QAAMC,IAAG,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAChD,QAAMA,IAAG;AAAA,IACP,cAAc,SAAS;AAAA,IACvB,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI;AAAA,EACnC;AACF;;;ACvCO,SAAS,8BACd,QACA,kBAAkD,CAAC,GACpC;AACf,QAAM,gBAAgD,CAAC;AACvD,aAAWC,UAAS,OAAO,UAAU;AACnC,UAAM,WAAW,gBAAgBA,OAAM,GAAG,KAAK,CAAC;AAChD,kBAAcA,OAAM,GAAG,IAAI,EAAE,GAAG,UAAU,GAAIA,OAAM,WAAW,CAAC,EAAG;AAAA,EACrE;AAEA,QAAM,SAAwB;AAAA,IAC5B,MAAM,OAAO;AAAA,IACb,WAAW,CAAC,GAAG,OAAO,SAAS;AAAA,IAC/B,UAAU,CAAC,GAAG,OAAO,QAAQ;AAAA,EAC/B;AAEA,MAAI,OAAO,iBAAiB,aAAa,QAAW;AAClD,WAAO,cAAc,OAAO,iBAAiB;AAAA,EAC/C;AACA,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,WAAO,cAAc,CAAC,GAAG,OAAO,WAAW;AAAA,EAC7C;AACA,MAAI,OAAO,KAAK,aAAa,EAAE,SAAS,GAAG;AACzC,WAAO,WAAW;AAAA,EACpB;AACA,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,WAAO,cAAc,CAAC,GAAG,OAAO,WAAW;AAAA,EAC7C;AACA,MAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,WAAO,QAAQ,OAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MACtC,KAAK,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA,MAKP,MAAM,EAAE,QAAQ,eAAe,EAAE,GAAG;AAAA,MACpC,GAAI,EAAE,KAAK,OACP,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,KAAK,MAAM,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,IAC9D,CAAC;AAAA,MACL,GAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/C,EAAE;AAAA,EACJ;AACA,SAAO;AACT;;;ACpDA,IAAM,MAAM;AACZ,IAAMC,aAAY,GAAG,GAAG;AACxB,IAAMC,kBAAiB,GAAG,GAAG;AAC7B,IAAMC,aAAY,GAAG,GAAG;AACxB,IAAMC,aAAY,GAAG,GAAG;AACxB,IAAMC,cAAa,GAAG,GAAG;AAyBzB,SAAS,SAASC,QAA2D;AAC3E,SAAO,CAAC,MAAM,UAAWA,SAAQ,MAAM,KAAK,EAAE,IAAI,IAAIC,cAAa;AACrE;AAEA,SAAS,YAAYD,QAAyB;AAC5C,QAAM,OAAO,SAASA,MAAK;AAC3B,SAAO;AAAA,IACL,MAAM,CAAC,MAAM,KAAK,GAAGE,UAAS;AAAA,IAC9B,WAAW,CAAC,MAAM,KAAK,GAAGC,eAAc;AAAA,IACxC,MAAM,CAAC,MAAM,KAAK,GAAGC,UAAS;AAAA,IAC9B,KAAK,CAAC,MAAM,KAAK,GAAGC,UAAS;AAAA,IAC7B,aAAa,CAAC,UAAU,KAAK,UAAK,KAAK,IAAIH,YAAWC,eAAc;AAAA,EACtE;AACF;AAQO,SAAS,UAAU,QAAqC;AAC7D,SAAO,YAAY,OAAO,SAAS,KAAK;AAC1C;AAKA,IAAM,gBAAgB,YAAY,QAAQ,OAAO,SAAS,KAAK;AACxD,IAAMG,QAAO,cAAc;AAC3B,IAAMC,aAAY,cAAc;AAChC,IAAMC,QAAO,cAAc;AAC3B,IAAM,MAAM,cAAc;AAC1B,IAAM,cAAc,cAAc;;;ACjFzC,SAAS,SAAAC,cAAa;AACtB,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,WAAU;AACjB,SAAS,WAAAC,gBAAe;;;ACHxB,SAAS,iBAAyC;AAuClD,IAAM,WAA4B;AAAA;AAAA;AAAA;AAAA,EAIhC,EAAE,MAAM,iBAAiB,IAAI,kCAAkC;AAAA;AAAA,EAE/D,EAAE,MAAM,iBAAiB,IAAI,6BAA6B;AAAA;AAAA;AAAA,EAG1D,EAAE,MAAM,gBAAgB,IAAI,4BAA4B;AAAA;AAAA,EAExD,EAAE,MAAM,cAAc,IAAI,gCAAgC;AAAA;AAAA,EAE1D,EAAE,MAAM,iBAAiB,IAAI,6BAA6B;AAC5D;AAOO,SAAS,YAAY,MAAsB;AAChD,MAAI,SAAS;AACb,aAAW,EAAE,GAAG,KAAK,UAAU;AAC7B,aAAS,OAAO,QAAQ,IAAI,OAAO;AAAA,EACrC;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,OAAuB;AACtC,MAAI,MAAM,UAAU,GAAI,QAAO;AAC/B,SAAO,GAAG,MAAM,MAAM,GAAG,CAAC,CAAC,SAAI,MAAM,MAAM,EAAE,CAAC;AAChD;AAYO,SAAS,yBAAoC;AAClD,MAAI,SAAS;AACb,SAAO,IAAI,UAAU;AAAA,IACnB,eAAe;AAAA,IACf,UAAU,OAAwB,MAAM,IAA6B;AACnE,YAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,MAAM,SAAS,MAAM;AACtE,gBAAU;AACV,YAAM,cAAc,OAAO,YAAY,IAAI;AAC3C,UAAI,gBAAgB,IAAI;AAGtB,WAAG,IAAI;AACP;AAAA,MACF;AACA,YAAM,YAAY,OAAO,MAAM,GAAG,cAAc,CAAC;AACjD,eAAS,OAAO,MAAM,cAAc,CAAC;AACrC,SAAG,MAAM,YAAY,SAAS,CAAC;AAAA,IACjC;AAAA,IACA,MAAM,IAA6B;AACjC,UAAI,OAAO,SAAS,GAAG;AACrB,cAAM,OAAO,YAAY,MAAM;AAC/B,iBAAS;AACT,WAAG,MAAM,IAAI;AACb;AAAA,MACF;AACA,SAAG,IAAI;AAAA,IACT;AAAA,EACF,CAAC;AACH;;;AC/GA,SAAS,aAAa;AACtB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,OAAOC,WAAU;AAGjB,IAAM,WAAW,cAAc,YAAY,GAAG;AAE9C,IAAI,mBAAkC;AAK/B,SAAS,sBAA8B;AAC5C,MAAI,iBAAkB,QAAO;AAC7B,QAAM,cAAc,SAAS,QAAQ,iCAAiC;AACtE,QAAM,MAAM,KAAK,MAAMC,cAAa,aAAa,MAAM,CAAC;AAGxD,QAAM,WACJ,OAAO,IAAI,QAAQ,WAAW,IAAI,MAAO,IAAI,KAAK,gBAAgB;AACpE,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,qBAAmBC,MAAK,QAAQA,MAAK,QAAQ,WAAW,GAAG,QAAQ;AACnE,SAAO;AACT;AAkCO,IAAM,oBAAuC,CAClD,MACA,KACA,UAAU,CAAC,MACR;AACH,QAAM,UAAU,oBAAoB;AACpC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,aAAa;AAKvB,YAAMC,SAAQ,MAAM,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG;AAAA,QACxD;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AACD,MAAAA,OAAM,GAAG,SAAS,MAAM;AACxB,MAAAA,OAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAC7C;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG;AAAA,MACxD;AAAA,MACA,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC,CAAC;AACD,QAAI,QAAQ,OAAO;AACjB,YAAM,eAAyB,CAAC;AAChC,YAAM,eAAyB,CAAC;AAChC,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB,aAAa,KAAK,KAAK,CAAC;AACpE,YAAM,QAAQ,GAAG,QAAQ,CAAC,UAAkB,aAAa,KAAK,KAAK,CAAC;AACpE,YAAM,GAAG,SAAS,MAAM;AACxB,YAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,cAAM,WAAW,QAAQ;AACzB,YAAI,aAAa,GAAG;AAClB,kBAAQ,OAAO;AAAA,YACb,YAAY,OAAO,OAAO,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,UAC1D;AACA,kBAAQ,OAAO;AAAA,YACb,YAAY,OAAO,OAAO,YAAY,EAAE,SAAS,MAAM,CAAC;AAAA,UAC1D;AAAA,QACF;AACA,gBAAQ,QAAQ;AAAA,MAClB,CAAC;AACD;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/C,CAAC;AACH;;;AF/FO,IAAM,qBAAmC,CAAC,MAAM,QAAQ;AAC7D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQC,OAAM,UAAU,CAAC,WAAW,GAAG,IAAI,GAAG;AAAA,MAClD;AAAA,MACA,OAAO,CAAC,WAAW,QAAQ,MAAM;AAAA,IACnC,CAAC;AACD,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/C,CAAC;AACH;AAOO,IAAM,YAA0B,CAAC,MAAM,QAAQ;AACpD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQA,OAAM,QAAQ,MAAM;AAAA,MAChC;AAAA,MACA,OAAO,CAAC,WAAW,QAAQ,MAAM;AAAA,IACnC,CAAC;AACD,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,QAAQ,KAAK,uBAAuB,CAAC,EAAE,KAAK,QAAQ,MAAM;AAChE,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAC/C,CAAC;AACH;AAaO,SAAS,mBAAmB,MAAsB;AACvD,SAAO,GAAGC,MAAK,SAAS,IAAI,CAAC;AAC/B;AASO,SAAS,eAAe,MAA+B;AAC5D,MAAI,CAACC,YAAWD,MAAK,KAAK,MAAM,eAAe,CAAC,GAAG;AACjD,UAAM,IAAI;AAAA,MACR,wBAAwB,IAAI;AAAA,IAC9B;AAAA,EACF;AACA,QAAM,cAAcA,MAAK,KAAK,MAAM,iBAAiB,cAAc;AACnE,MAAI,CAACC,YAAW,WAAW,GAAG;AAC5B,UAAM,IAAI;AAAA,MACR,sBAAsB,WAAW;AAAA,IACnC;AAAA,EACF;AACA,SAAO,EAAE,aAAa,aAAa,mBAAmB,IAAI,EAAE;AAC9D;AAQA,eAAe,iBACb,cACA,MACiB;AACjB,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,KAAK,IAAI;AAC7D,QAAM,UAAU,KAAK,SAAS;AAC9B,QAAM,UAAU,aAAa,KAAK,OAAO;AACzC,SAAO,QAAQ,CAAC,MAAM,aAAa,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,IAAI;AAC9E;AAkBA,eAAsB,SAAS,MAAqC;AAClE,iBAAe,KAAK,IAAI;AACxB,QAAM,SAAS,KAAK,UAAU,EAAE,MAAM,CAAC,QAAQC,SAAQ,KAAK,GAAG,EAAE;AACjE,QAAM,UAAU,KAAK,SAAS;AAC9B,SAAO,KAAK,+BAA+B,KAAK,IAAI,QAAG;AACvD,SAAO;AAAA,IACL,CAAC,MAAM,sBAAsB,KAAK,MAAM,kCAAkC;AAAA,IAC1E,KAAK;AAAA,EACP;AACF;AAgBA,eAAsB,kBACpB,MACA,MACiB;AACjB,QAAM,EAAE,YAAY,OAAO,IAAI;AAE/B,MAAI,YAAY;AACd,UAAM,cAAc,mBAAmB,IAAI;AAC3C,WAAO;AAAA,MACL,2BAA2B,WAAW;AAAA,IACxC;AACA,UAAM,eAAe,KAAK,gBAAgB;AAS1C,UAAM,SAAS;AAAA,MACb;AAAA,MACA,oCAAoC,WAAW;AAAA,MAC/C,uEAAuE,WAAW;AAAA,MAClF,2CAA2C,WAAW;AAAA,MACtD;AAAA,MACA;AAAA,MACA,qBAAqB,WAAW,mDAAmD,WAAW,gDAAgD,WAAW;AAAA,MACzJ,8EAA8E,WAAW;AAAA,MACzF,kDAAkD,WAAW;AAAA,MAC7D,qHAAqH,WAAW;AAAA,MAChI;AAAA,IACF,EAAE,KAAK,IAAI;AACX,UAAM,cAAc,MAAM,aAAa,CAAC,MAAM,MAAM,GAAG,IAAI;AAC3D,QAAI,gBAAgB,EAAG,QAAO;AAE9B,WAAO,SAAS;AAAA,MACd;AAAA,MACA,GAAI,KAAK,oBAAoB,EAAE,OAAO,KAAK,kBAAkB,IAAI,CAAC;AAAA,MAClE;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,KAAK,yCAAyC,IAAI,QAAG;AAC5D,QAAM,UAAU,KAAK,qBAAqB;AAC1C,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,MAA6C;AACnE,SAAO;AAAA,IACL,CAAC,YAAY,CAAC,QAAQ,GAAI,UAAU,CAAC,OAAO,IAAI,CAAC,CAAE;AAAA,IACnD;AAAA,EACF;AACF;AAEO,SAAS,UAAU,MAA6C;AACrE,SAAO;AAAA,IACL,CAAC,YAAY,CAAC,MAAM,GAAI,UAAU,CAAC,OAAO,IAAI,CAAC,CAAE;AAAA,IACjD;AAAA,EACF;AACF;AAMO,SAAS,QAAQ,MAAoC;AAC1D,QAAM,SAAS,KAAK,UAAU;AAC9B,SAAO;AAAA,IACL,CAAC,YAAY;AAAA,MACX;AAAA,MACA,GAAI,SAAS,CAAC,IAAI,IAAI,CAAC;AAAA,MACvB,GAAI,UAAU,CAAC,OAAO,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AACF;;;AGhOA,SAAS,SAAAC,cAAa;AACtB,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AAcjB,IAAM,wBAA0C,CAAC,UAAU;AACzD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAatC,UAAM,QAAQC,OAAM,OAAO,CAAC,cAAc,MAAM,GAAG;AAAA,MACjD,OAAO,CAAC,QAAQ,QAAQ,SAAS;AAAA,MACjC,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,qBAAqB;AAAA,QACrB,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,IACF,CAAC;AACD,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,EAAE,QAAQ,UAAU,QAAQ,EAAE,CAAC,CAAC;AACnE,UAAM,MAAM,MAAM,KAAK;AACvB,UAAM,MAAM,IAAI;AAAA,EAClB,CAAC;AACH;AAiBO,SAAS,gBACd,MACA,UACkB;AAClB,QAAM,YAAY,qBAAqB,KAAK,YAAY,CAAC;AACzD,MAAI,UAAW,QAAO;AACtB,SAAO,YAAY;AACrB;AAkBA,SAAS,iBAAiB,OAAiD;AACzE,QAAM,SAAS,oBAAI,IAA8B;AACjD,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,IAAI,WAAW,UAAU,EAAG;AACtC,QAAI;AACJ,QAAI;AACF,aAAO,IAAI,IAAI,KAAK,GAAG,EAAE;AAAA,IAC3B,QAAQ;AAIN;AAAA,IACF;AACA,QAAI,OAAO,IAAI,IAAI,EAAG;AACtB,WAAO,IAAI,MAAM,EAAE,MAAM,UAAU,gBAAgB,MAAM,KAAK,QAAQ,EAAE,CAAC;AAAA,EAC3E;AACA,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC5B;AAUA,SAAS,oBAAoB,MAYlB;AACT,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AACH,aAAOC,MAAK,KAAK,IAAI;AAAA,IACvB,KAAK;AACH,aAAOA,MAAK,KAAK,MAAM;AAAA,IACzB;AACE,UAAI,KAAK,UAAW,QAAOA,MAAK,KAAK,SAAS;AAC9C,aAAO,OAAO,KAAK,YAAY;AAAA,EACnC;AACF;AAYO,SAAS,kBACd,MACA,UAMA;AACA,MAAI,aAAa,UAAU;AAOzB,UAAM,SAAS,KAAK,YAAY,MAAM;AACtC,UAAM,UAAU,SAAS,KAAK,eAAe,IAAI;AACjD,UAAM,UAAU,oBAAoB;AAAA,MAClC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,cAAc;AAAA,IAChB,CAAC;AACD,WAAO;AAAA,MACL,OAAO,GAAG,IAAI;AAAA,MACd,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAA,MAAK,gBAAgB,OAAO,EAAE;AAAA,QAC9BA,MAAK,oBAAoB,OAAO,EAAE;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACA,MAAI,aAAa,UAAU;AAGzB,UAAM,SAAS,KAAK,YAAY,MAAM;AACtC,UAAM,UAAU,SAAS,KAAK,eAAe,IAAI;AAOjD,UAAM,UAAU,oBAAoB;AAAA,MAClC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,cAAc;AAAA,IAChB,CAAC;AACD,WAAO;AAAA,MACL,OAAO,GAAG,IAAI;AAAA,MACd,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAA,MAAK,kBAAkB,OAAO,EAAE;AAAA,QAChC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AACA,MAAI,aAAa,aAAa;AAO5B,UAAM,UAAU,KAAK,YAAY,MAAM;AACvC,QAAI,SAAS;AACX,aAAO;AAAA,QACL,OAAO,GAAG,IAAI;AAAA,QACd,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACAA;AAAA,YACE,sDAAsD,IAAI;AAAA,UAC5D;AAAA,QACF,EAAE,KAAK,IAAI;AAAA,MACb;AAAA,IACF;AACA,WAAO;AAAA,MACL,OAAO,GAAG,IAAI;AAAA,MACd,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,6DAA6D,IAAI;AAAA,QACjE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACAA;AAAA,UACE,sDAAsD,IAAI;AAAA,QAC5D;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,EACF;AAQA,SAAO;AAAA,IACL,OAAO,GAAG,IAAI;AAAA,IACd,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA,yDAAyD,IAAI;AAAA,MAC7D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACAA;AAAA,QACE,sDAAsD,IAAI;AAAA,MAC5D;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAOA,SAAS,0BAA0B,QAA6B;AAC9D,QAAM,SAAsB,CAAC;AAC7B,aAAW,QAAQ,OAAO,MAAM,IAAI,GAAG;AACrC,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,SAAS,EAAG;AAChB,UAAM,MAAM,KAAK,MAAM,GAAG,KAAK;AAC/B,UAAM,QAAQ,KAAK,MAAM,QAAQ,CAAC;AAClC,QAAI,QAAQ,WAAY,QAAO,WAAW;AAC1C,QAAI,QAAQ,WAAY,QAAO,WAAW;AAAA,EAC5C;AACA,SAAO;AACT;AAEA,SAAS,qBACP,MACA,UACA,UACQ;AAGR,QAAM,UAAU,mBAAmB,QAAQ;AAC3C,QAAM,UAAU,mBAAmB,QAAQ;AAC3C,SAAO,WAAW,OAAO,IAAI,OAAO,IAAI,IAAI;AAC9C;AAsDA,eAAsB,sBACpB,kBACA,OACA,UAAqC,CAAC,GACH;AACnC,QAAM,WAAWC,MAAK,KAAK,kBAAkB,YAAY;AACzD,QAAM,kBAAkBA,MAAK,KAAK,UAAU,iBAAiB;AAE7D,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,SAAS,QAAQ,UAAU,EAAE,MAAM,MAAM;AAAA,EAAC,GAAG,MAAM,MAAM;AAAA,EAAC,EAAE;AAMlE,QAAM,QAAkB,CAAC;AACzB,QAAM,UAAkC,CAAC;AACzC,aAAW,EAAE,MAAM,SAAS,KAAK,OAAO;AACtC,QAAI,aAAa,WAAW;AAI1B,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,UAAU;AAAA;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,WAAO,KAAK,4BAA4B,IAAI,sBAAiB;AAC7D,UAAM,QAAQ;AAAA,OAAwB,IAAI;AAAA;AAAA;AAC1C,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,QAAQ,KAAK;AAAA,IAC9B,SAAS,KAAK;AAKZ,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,cAAQ,KAAK,EAAE,MAAM,UAAU,QAAQ,eAAe,OAAO,CAAC;AAC9D;AAAA,IACF;AACA,QAAI,OAAO,aAAa,GAAG;AACzB,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,aAAa,OAAO,QAAQ;AAAA,MACtC,CAAC;AACD;AAAA,IACF;AACA,UAAM,EAAE,UAAU,SAAS,IAAI,0BAA0B,OAAO,MAAM;AACtE,QAAI,CAAC,YAAY,CAAC,UAAU;AAC1B,cAAQ,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,UAAM,KAAK,qBAAqB,MAAM,UAAU,QAAQ,CAAC;AACzD,YAAQ,KAAK,EAAE,MAAM,UAAU,QAAQ,MAAM,QAAQ,GAAG,CAAC;AAAA,EAC3D;AAEA,QAAMC,IAAG,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAMA,IAAG;AAAA,IACP;AAAA,IACA,MAAM,KAAK,IAAI,KAAK,MAAM,SAAS,IAAI,OAAO;AAAA,IAC9C;AAAA,MACE,MAAM;AAAA,IACR;AAAA,EACF;AAEA,SAAO;AAAA,IACL,cAAc,MAAM;AAAA,IACpB,cAAc,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,IAAI,EAAE;AAAA,IACvD;AAAA,IACA;AAAA,EACF;AACF;AAwBO,SAAS,8BACd,SACQ;AACR,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI,QAAQ,CAAC;AACnB,UAAM,OAAO,kBAAkB,EAAE,MAAM,EAAE,QAAQ;AACjD,WAAO;AAAA,MACL,4BAA4B,KAAK,KAAK;AAAA,MACtC;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,eAAeC,MAAK,iBAAiB,CAAC;AAAA,IACxC,EAAE,KAAK,IAAI;AAAA,EACb;AACA,QAAM,QAAkB;AAAA,IACtB,+BAA+B,QAAQ,MAAM;AAAA,IAC7C;AAAA,EACF;AACA,aAAW,KAAK,SAAS;AACvB,UAAM,OAAO,kBAAkB,EAAE,MAAM,EAAE,QAAQ;AACjD,UAAM,KAAK,KAAK,KAAK;AACrB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAK,IAAI;AACpB,UAAM,KAAK,EAAE;AAAA,EACf;AACA,QAAM,KAAK,eAAeA,MAAK,iBAAiB,CAAC,GAAG;AACpD,SAAO,MAAM,KAAK,IAAI;AACxB;AAQO,SAAS,2BAA2B,OAAkC;AAC3E,QAAM,SAAS,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,EAAE,KAAK;AACxC,QAAM,QAAkB;AAAA,IACtB,OAAO,WAAW,IACd,iCAAiC,OAAO,CAAC,CAAE,MAC3C,4BAA4B,OAAO,MAAM,WAAW,OAAO,KAAK,IAAI,CAAC;AAAA,IACzE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACAA,MAAK,UAAU;AAAA,IACfA,MAAK,sBAAsB,OAAO,CAAC,CAAE,SAAI;AAAA,IACzCA,MAAK,yDAAyD;AAAA,IAC9D;AAAA,IACA,kBAAkBA,MAAK,4EAA4E,CAAC;AAAA,EACtG;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC/gBA,SAAS,SAAAC,cAAa;AAoCtB,IAAM,kBAAqC,CAAC,QAAQ;AAClD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAItC,UAAM,QAAQC,OAAM,OAAO,CAAC,aAAa,WAAW,MAAM,GAAG,GAAG;AAAA,MAC9D,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAChC,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,qBAAqB;AAAA,QACrB,aAAa;AAAA,QACb,aAAa;AAAA,MACf;AAAA,IACF,CAAC;AACD,QAAI,SAAS;AACb,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AACD,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM;AAAA,MAAG;AAAA,MAAQ,CAAC,SAChB,QAAQ,EAAE,QAAQ,QAAQ,UAAU,QAAQ,EAAE,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AACH;AA+BA,SAAS,eAAe,QAAyC;AAC/D,QAAM,IAAI,OAAO,YAAY;AAC7B,MACE,EAAE,SAAS,wBAAwB,KACnC,EAAE,SAAS,2BAA2B,KACtC,EAAE,SAAS,sCAAsC,KACjD,EAAE,SAAS,qCAAqC,GAChD;AACA,WAAO;AAAA,EACT;AACA,MACE,EAAE,SAAS,sBAAsB,KACjC,EAAE,SAAS,qBAAqB,KAChC,EAAE,SAAS,kBAAkB,KAC7B,EAAE,SAAS,uBAAuB,KAClC,EAAE,SAAS,oBAAoB,KAC/B,EAAE,SAAS,uCAAuC,GAClD;AACA,WAAO;AAAA,EACT;AACA,MACE,EAAE,SAAS,uBAAuB,KAClC,EAAE,SAAS,yBAAyB,KACpC,EAAE,SAAS,gCAAgC,KAC3C,EAAE,SAAS,uCAAuC,KAClD,EAAE,SAAS,uCAAuC,GAClD;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASA,eAAsB,sBACpB,OACA,UAAyC,CAAC,GACP;AACnC,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,UAAoC,CAAC;AAC3C,aAAW,QAAQ,OAAO;AAIxB,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,QAAQ,KAAK,GAAG;AAAA,IACjC,SAAS,KAAK;AACZ,cAAQ,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACzD,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,aAAa,GAAG;AACzB,cAAQ,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,CAAC;AACpD;AAAA,IACF;AACA,YAAQ,KAAK;AAAA,MACX,KAAK,KAAK;AAAA,MACV,IAAI;AAAA,MACJ,MAAM,eAAe,OAAO,MAAM;AAAA,MAClC,QAAQ,OAAO,OAAO,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAuBO,SAAS,4BACd,UACQ;AACR,QAAM,SAAS,oBAAI,IAAuD;AAC1E,aAAW,KAAK,UAAU;AACxB,UAAM,OAAO,EAAE,QAAQ;AACvB,UAAM,OAAO,OAAO,IAAI,IAAI,KAAK,CAAC;AAClC,SAAK,KAAK,CAAC;AACX,WAAO,IAAI,MAAM,IAAI;AAAA,EACvB;AACA,QAAM,YAAY,SAAS;AAC3B,QAAM,QAAkB;AAAA,IACtB,cAAc,IACV,+BAA+B,SAAS,CAAC,EAAG,GAAG,KAC/C,gBAAgB,SAAS;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,eAA0C;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,aAAW,QAAQ,cAAc;AAC/B,UAAM,UAAU,OAAO,IAAI,IAAI;AAC/B,QAAI,CAAC,WAAW,QAAQ,WAAW,EAAG;AACtC,UAAM,KAAK,cAAc,IAAI,CAAC;AAC9B,eAAW,KAAK,SAAS;AACvB,YAAM,KAAK,YAAO,EAAE,GAAG,EAAE;AAAA,IAC3B;AACA,eAAW,UAAU,cAAc,IAAI,GAAG;AACxC,YAAM,KAAK,SAAS,MAAM,EAAE;AAAA,IAC9B;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,QAAM,KAAK,eAAeC,MAAK,iBAAiB,CAAC,GAAG;AACpD,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,cAAc,MAAuC;AAC5D,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAEA,SAAS,cAAc,MAAyC;AAC9D,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,QACA;AAAA,MACF;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL;AAAA,MACF;AAAA,EACJ;AACF;;;AC1QA,SAAS,SAAAC,cAAa;AAuCtB,IAAM,iBAAkC,MAAM;AAC5C,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAKtC,UAAM,QAAQA;AAAA,MACZ;AAAA,MACA,CAAC,QAAQ,YAAY,2BAA2B;AAAA,MAChD;AAAA,QACE,OAAO,CAAC,UAAU,QAAQ,SAAS;AAAA,MACrC;AAAA,IACF;AACA,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,QAAQ,CAAC,SAAS,QAAQ,EAAE,QAAQ,UAAU,QAAQ,EAAE,CAAC,CAAC;AAAA,EACrE,CAAC;AACH;AAQA,eAAsB,iBACpB,UAAuC,CAAC,GACnB;AACrB,QAAM,UAAU,QAAQ,SAAS;AACjC,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ;AAC7B,QAAI,OAAO,aAAa,EAAG,QAAO;AAIlC,WAAO,gBAAgB,KAAK,OAAO,MAAM,IAAI,aAAa;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACjFA,SAAS,SAAAC,cAAa;AACtB,SAAS,YAAYC,WAAU;AAC/B,OAAOC,WAAU;AACjB,SAAS,WAAAC,gBAAe;AAsBxB,IAAM,mBAAkC,CAAC,QAAQ;AAC/C,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQH,OAAM,OAAO,CAAC,UAAU,YAAY,SAAS,GAAG,GAAG;AAAA,MAC/D,OAAO,CAAC,UAAU,QAAQ,SAAS;AAAA,IACrC,CAAC;AACD,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM;AAAA,MAAG;AAAA,MAAQ,CAAC,SAChB,QAAQ,EAAE,OAAO,OAAO,KAAK,GAAG,UAAU,QAAQ,EAAE,CAAC;AAAA,IACvD;AAAA,EACF,CAAC;AACH;AAEA,IAAM,qBAAqC,OAAO,QAAQ;AAIxD,MAAI,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,OAAO,OAAO;AACjD,WAAO;AAAA,EACT;AACA,QAAM,QACJ,QAAQ,cACJ,qDACA;AACN,QAAM,QAAQ,MAAMG,SAAQ,OAAO,GAAG,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC;AAChE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAgDA,eAAsB,mBACpB,kBACA,UAAkC,CAAC,GACH;AAChC,QAAM,eAAeD,MAAK,KAAK,kBAAkB,YAAY;AAC7D,QAAM,gBAAgBA,MAAK,KAAK,cAAc,WAAW;AACzD,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,WAAW,QAAQ,UAAU;AACnC,QAAM,SAAS,QAAQ,UAAU,EAAE,MAAM,MAAM;AAAA,EAAC,GAAG,MAAM,MAAM;AAAA,EAAC,EAAE;AAElE,QAAM,WAAW,MAAM,sBAAsB,aAAa;AAQ1D,QAAM,OAAO,MAAM,WAAW,aAAa;AAAA,IACzC,UAAU,QAAQ,mBAAmB;AAAA,IACrC,cAAc,QAAQ,UAAU;AAAA,IAChC;AAAA,IACA,gBAAgB,SAAS;AAAA,IACzB;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,QAAQ,MAAM,WAAW,cAAc;AAAA,IAC3C,UAAU,QAAQ,mBAAmB;AAAA,IACrC,cAAc,QAAQ,UAAU;AAAA,IAChC;AAAA,IACA,gBAAgB,SAAS;AAAA,IACzB;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,QAAkB,CAAC,QAAQ;AACjC,MAAI,SAAS,OAAW,OAAM,KAAK,WAAY,IAAI,EAAE;AACrD,MAAI,UAAU,OAAW,OAAM,KAAK,YAAa,KAAK,EAAE;AAExD,QAAMD,IAAG,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAChD,QAAMA,IAAG,UAAU,eAAe,MAAM,KAAK,IAAI,IAAI,IAAI;AAEzD,SAAO;AAAA,IACL,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACvC;AAAA,EACF;AACF;AAWA,eAAe,WACb,KACA,MAC6B;AAC7B,MAAI,KAAK,aAAa,UAAa,KAAK,SAAS,SAAS,GAAG;AAC3D,WAAO,KAAK;AAAA,EACd;AACA,MAAI,KAAK,iBAAiB,UAAa,KAAK,aAAa,SAAS,GAAG;AACnE,WAAO,KAAK;AAAA,EACd;AACA,QAAM,YAAY,MAAM,gBAAgB,KAAK,SAAS,KAAK,KAAK,MAAM;AACtE,MAAI,cAAc,OAAW,QAAO;AACpC,MAAI,KAAK,mBAAmB,UAAa,KAAK,eAAe,SAAS,GAAG;AACvE,WAAO,KAAK;AAAA,EACd;AACA,QAAM,WAAW,MAAM,KAAK,SAAS,GAAG;AACxC,MAAI,aAAa,OAAW,QAAO;AACnC,OAAK,OAAO;AAAA,IACV,MAAM,GAAG,+JAA+J,GAAG;AAAA,EAC7K;AACA,SAAO;AACT;AAEA,eAAe,gBACb,SACA,KACA,QAC6B;AAC7B,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,GAAG;AAChC,QAAI,OAAO,aAAa,KAAK,OAAO,MAAM,SAAS,GAAG;AACpD,aAAO,OAAO;AAAA,IAChB;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,0BAA0B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAe,sBACb,UAC4C;AAC5C,MAAI;AACF,UAAM,UAAU,MAAMA,IAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAA4C,CAAC;AACnD,UAAM,YAAY,4BAA4B,KAAK,OAAO;AAC1D,UAAM,aAAa,6BAA6B,KAAK,OAAO;AAC5D,QAAI,YAAY,CAAC,EAAG,QAAO,OAAO,UAAU,CAAC;AAC7C,QAAI,aAAa,CAAC,EAAG,QAAO,QAAQ,WAAW,CAAC;AAChD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;;;AX5GA,eAAsB,SAAS,MAAgD;AAC7E,QAAM,OAAO,KAAK,iBAAiB,cAAqB;AACxD,QAAM,SAAS,KAAK,UAAU;AAAA,IAC5B,MAAM,CAAC,QAAQG,UAAQ,KAAK,GAAG;AAAA,IAC/B,SAAS,CAAC,QAAQA,UAAQ,QAAQ,GAAG;AAAA,IACrC,MAAM,CAAC,QAAQA,UAAQ,KAAK,GAAG;AAAA;AAAA;AAAA,IAG/B,SAAS,CAAC,UAAU,QAAQ,OAAO,MAAM;AAAA,EAAK,YAAY,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,EACxE;AACA,QAAM,UAAU,CAAC,UAAkB,OAAO,UAAU,KAAK;AAEzD,MAAI,CAAC,MAAM,aAAa,KAAK,KAAK,IAAI,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,wBAAwB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,KAAK,MAAM,IAAI;AACnD,MAAI,CAACC,YAAW,OAAO,GAAG;AACxB,UAAM,IAAI;AAAA,MACR,mBAAmB,OAAO,qCAAqC,KAAK,IAAI;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,YAAY,aAAa,KAAK,MAAM,IAAI;AAC9C,QAAM,oBAAoB,WAAW,KAAK,IAAI;AAG9C,UAAQ,eAAe;AAEvB,QAAM,SAAS,MAAM,WAAW,OAAO;AAKvC,QAAM,eAAe,MAAM,oBAAoB,EAAE,eAAe,KAAK,CAAC;AAStE,8BAA4B,OAAO,OAAO,UAAU,cAAc,MAAM;AAKxE,QAAM,aAAa;AAAA,IACjB;AAAA,MACE,OAAO;AAAA,MACP,cAAc,UAAU,YAAY,CAAC;AAAA,IACvC;AAAA,EACF;AACA,kBAAgB,UAAU;AAC1B,SAAO,QAAQ,iBAAiB,IAAI,IAAI,WAAW,OAAO,CAAC,GAAG,CAAC,EAAE;AAMjE,QAAM,WAAW;AAAA,IACf,MAAM,OAAO;AAAA,IACb,MAAM,OAAO,QAAQ,OAAO;AAAA,EAC9B;AACA,QAAM,mBAAmB,WAAW;AAAA,IAClC,GAAI,KAAK,gBAAgB,EAAE,OAAO,KAAK,cAAc,IAAI,CAAC;AAAA,IAC1D,GAAI,KAAK,iBAAiB,EAAE,QAAQ,KAAK,eAAe,IAAI,CAAC;AAAA,IAC7D,GAAI,OAAO,OAAO,KAAK,OACnB,EAAE,mBAAmB,OAAO,OAAO,IAAI,KAAK,IAC5C,CAAC;AAAA,IACL,GAAI,cAAc,UAAU,KAAK,OAC7B,EAAE,UAAU,aAAa,SAAS,IAAI,KAAK,IAC3C,CAAC;AAAA,IACL,QAAQ;AAAA,EACV,CAAC;AAaD,QAAM,eAAe,iBAAiB,WAAW,SAAS,CAAC,CAAC;AAC5D,QAAM,uBAAuB,aAC1B,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,EACtC,IAAI,CAAC,MAAM,EAAE,IAAI;AACpB,MAAI,qBAAqB,SAAS,GAAG;AACnC,UAAM,IAAI,MAAM,2BAA2B,oBAAoB,CAAC;AAAA,EAClE;AACA,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,aAAa,MAAM,sBAAsB,WAAW,cAAc;AAAA,MACtE,GAAI,KAAK,mBAAmB,EAAE,OAAO,KAAK,iBAAiB,IAAI,CAAC;AAAA,MAChE,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,UAAU,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,IAAI;AAClE,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,IAAI,MAAM,8BAA8B,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAQA,QAAM,gBAAgB,WAAW,SAAS,CAAC;AAC3C,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,eAAe,MAAM,sBAAsB,eAAe;AAAA,MAC9D,GAAI,KAAK,oBAAoB,EAAE,OAAO,KAAK,kBAAkB,IAAI,CAAC;AAAA,IACpE,CAAC;AACD,UAAM,cAAc,aAAa,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE;AACpD,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,IAAI,MAAM,4BAA4B,WAAW,CAAC;AAAA,IAC1D;AAAA,EACF;AAGA,UAAQ,UAAU;AAQlB,QAAM,aAAa,MAAM,iBAAiB;AAAA,IACxC,GAAI,KAAK,kBAAkB,EAAE,OAAO,KAAK,gBAAgB,IAAI,CAAC;AAAA,EAChE,CAAC;AAED,QAAMC,IAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,QAAM,cAAc,YAAY,WAAW,EAAE,WAAW,CAAC;AACzD,QAAM;AAAA,IACJ;AAAA,IACA,eAAe;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,MACjB,GAAI,KAAK,MAAM,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IACtC,CAAC;AAAA,EACH;AACA,SAAO,QAAQ,qBAAqB,WAAW,SAAS,CAAC,EAAE;AAG3D,UAAQ,WAAW;AAKnB,QAAM,cAAc,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,GAAG;AAC3D,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO,KAAK,aAAa,YAAY,IAAI,CAAC,MAAMC,MAAK,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EACvE;AAQA,SAAO;AAAA,IACL;AAAA,MACE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,kBAAkB,WAAW;AAAA,IAClD,YAAY,aAAa,UAAU;AAAA,IACnC,GAAI,KAAK,iBAAiB,SACtB,EAAE,cAAc,KAAK,aAAa,IAClC,CAAC;AAAA,IACL,GAAI,KAAK,sBAAsB,SAC3B,EAAE,mBAAmB,KAAK,kBAAkB,IAC5C,CAAC;AAAA,IACL;AAAA,EACF,CAAC;AAMD,MAAI,aAAa,GAAG;AAClB,YAAQ,YAAY;AACpB,WAAO,KAAK,KAAKA,MAAK,mBAAmB,KAAK,IAAI,EAAE,CAAC,EAAE;AAAA,EACzD;AAEA,SAAO,EAAE,WAAW,YAAY,SAAS,mBAAmB,SAAS;AACvE;AAkBA,eAAe,oBACb,WACA,gBACe;AACf,MAAI,CAACF,YAAW,SAAS,EAAG;AAC5B,QAAM,UAAU,MAAMC,IAAG,QAAQ,SAAS;AAC1C,MAAI,QAAQ,WAAW,EAAG;AAE1B,QAAM,QAAQ,MAAM,cAAc,SAAS;AAC3C,MAAI,OAAO;AACT,QAAI,MAAM,WAAW,gBAAgB;AACnC,YAAM,IAAI;AAAA,QACR,GAAG,SAAS,yCAAyC,MAAM,MAAM,WAAW,cAAc,kEAAkE,MAAM,MAAM;AAAA,MAC1K;AAAA,IACF;AACA;AAAA,EACF;AAOA,MAAI,QAAQ,WAAW,KAAK,QAAQ,CAAC,MAAM,cAAc;AACvD;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,oDAAoD,SAAS;AAAA,EAC/D;AACF;AAOA,SAAS,4BACP,mBACA,cACA,QACM;AACN,QAAM,OAAO,OAAO,QAAQ,OAAO;AACnC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,OAAO,CAAC,QAAgB,WAAmB;AAC/C,QAAI,KAAK,IAAI,MAAM,EAAG;AACtB,SAAK,IAAI,MAAM;AACf,UAAM,SAAS,4BAA4B,MAAM;AACjD,QAAI,CAAC,OAAQ;AACb;AAAA,MACE,6BAA6B,MAAM,MAAM,MAAM,oBAC5B,MAAM;AAAA,IAE3B;AAAA,EACF;AAEA,aAAWE,UAAS,mBAAmB;AACrC,SAAKA,OAAM,KAAK,eAAe;AAAA,EACjC;AACA,QAAM,iBAAiB,cAAc,UAAU;AAC/C,MAAI,gBAAgB;AAClB,eAAW,OAAO,OAAO,KAAK,cAAc,GAAG;AAC7C,WAAK,KAAK,sBAAsB;AAAA,IAClC;AAAA,EACF;AACF;;;AY/XO,IAAM,cAAc;;;ACJ3B,SAAS,WAAAC,iBAAe;AAIxB,eAAsB,SAAS,QAA+C;AAC5E,MAAI;AACF,UAAM,WAAW,MAAM,OAAO;AAC9B,YAAQ,KAAK,QAAQ;AAAA,EACvB,SAAS,KAAK;AACZ,IAAAA,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AdEO,IAAM,eAAeC,eAAc;AAAA,EACxC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,WAAO,SAAS,YAAY;AAC1B,YAAM,SAAS,MAAM,SAAS;AAAA,QAC5B,MAAM,KAAK;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AACD,aAAO,OAAO;AAAA,IAChB,CAAC;AAAA,EACH;AACF,CAAC;;;AetCD,SAAS,iBAAAC,sBAAqB;AAiC9B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,8BAA8B;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,SAAS,CAAC,QAAQ,OAAO,MAAM;AAG9B,SAAS,uBAAuB,OAAsB;AAC3D,QAAM,WAAW,aAAa,KAAK,GAAG;AACtC,QAAM,yBAAyB,4BAA4B,KAAK,GAAG;AAEnE,MAAI,UAAU,QAAQ;AACpB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iCAAiC,QAAQ;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,sBAAsB;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,qCAAqC,OAAO,KAAK,GAAG,CAAC;AAAA,MACrD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAEA,MAAI,UAAU,QAAQ;AACpB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,aAAa,IAAI,CAAC,MAAM,YAAY,CAAC,GAAG;AAAA,MAC3C;AAAA,MACA,oBAAoB,OAAO,KAAK,MAAM,CAAC;AAAA,MACvC;AAAA,MACA,GAAG,4BAA4B,IAAI,CAAC,MAAM,YAAY,CAAC,GAAG;AAAA,MAC1D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAGA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,aAAa,IAAI,CAAC,MAAM,QAAQ,CAAC,GAAG;AAAA,IACvC;AAAA,IACA,aAAa,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,sBAAsB;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEO,IAAM,oBAAoBA,eAAc;AAAA,EAC7C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,UAAM,QAAQ,KAAK;AACnB,QAAI,UAAU,UAAU,UAAU,SAAS,UAAU,QAAQ;AAC3D,cAAQ,OAAO;AAAA,QACb,kBAAkB,KAAK,UAAU,KAAK,CAAC,gBAAgB,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA,MAC1E;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,OAAO,MAAM,uBAAuB,KAAK,CAAC;AAAA,EACpD;AACF,CAAC;;;ACxPD,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,WAAAC,iBAAe;;;ACDxB,SAAS,cAAAC,aAAY,YAAYC,YAAU;AAC3C,SAAS,WAAAC,iBAAe;;;ACDxB,SAAS,cAAAC,aAAY,YAAYC,WAAU;AAC3C,OAAOC,WAAU;AACjB,SAAS,KAAAC,UAAS;AAClB,SAAS,SAAS,iBAAiB;AAwBnC,IAAM,iBAAiBC,GAAE,KAAK,CAAC,YAAY,WAAW,SAAS,CAAC;AAGhE,IAAM,4BAA4BA,GAAE,OAAO;AAAA,EACzC,KAAKA,GAAE,OAAO,EAAE,MAAM,MAAM,UAAU;AAAA,EACtC,SAASA,GAAE,OAAOA,GAAE,OAAO,GAAG,wBAAwB,EAAE,SAAS;AACnE,CAAC;AAOD,IAAM,sBAAsBA,GACzB,OAAO;AAAA,EACN,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,aAAaA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,UAAU;AAAA,EACV,aAAaA,GAAE,OAAO;AAAA,IACpB,WAAWA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,IAC/C,UAAUA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS;AAAA,IAC9C,UAAUA,GAAE,MAAM,yBAAyB,EAAE,SAAS;AAAA,EACxD,CAAC;AACH,CAAC,EACA,YAAY,CAAC,MAAM,QAAQ;AAC1B,QAAM,IAAI,KAAK;AACf,QAAM,SAAS;AAAA,IACb,EAAE,aAAa,EAAE,UAAU,SAAS,IAAI,cAAc;AAAA,IACtD,EAAE,YAAY,EAAE,SAAS,SAAS,IAAI,aAAa;AAAA,IACnD,EAAE,YAAY,EAAE,SAAS,SAAS,IAAI,aAAa;AAAA,EACrD,EAAE,OAAO,CAAC,MAAmB,MAAM,IAAI;AAEvC,MAAI,OAAO,WAAW,GAAG;AACvB,QAAI,SAAS;AAAA,MACX,MAAMA,GAAE,aAAa;AAAA,MACrB,SACE;AAAA,IACJ,CAAC;AACD;AAAA,EACF;AACA,MAAI,OAAO,SAAS,GAAG;AACrB,QAAI,SAAS;AAAA,MACX,MAAMA,GAAE,aAAa;AAAA,MACrB,SAAS,yEAAyE,OAAO,KAAK,IAAI,CAAC;AAAA,IACrG,CAAC;AACD;AAAA,EACF;AACA,QAAM,WACJ,KAAK,aAAa,aACd,cACA,KAAK,aAAa,YAChB,aACA;AACR,MAAI,OAAO,CAAC,MAAM,UAAU;AAC1B,QAAI,SAAS;AAAA,MACX,MAAMA,GAAE,aAAa;AAAA,MACrB,SAAS,aAAa,KAAK,QAAQ,0BAA0B,QAAQ,qBAAqB,OAAO,CAAC,CAAC;AAAA,IACrG,CAAC;AAAA,EACH;AACF,CAAC;AAoBH,eAAsB,qBACpB,UAAkB,cAAqB,GACN;AACjC,MAAI,CAACC,YAAW,OAAO,GAAG;AACxB,WAAO,oBAAI,IAAI;AAAA,EACjB;AACA,QAAM,MAAM,oBAAI,IAAuB;AACvC,QAAM,KAAK,SAAS,SAAS,GAAG;AAChC,SAAO;AACT;AAEA,eAAe,KACb,SACA,YACA,KACe;AACf,QAAM,UAAU,MAAMC,IAAG,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AACpE,aAAWC,UAAS,SAAS;AAC3B,UAAM,OAAOC,MAAK,KAAK,YAAYD,OAAM,IAAI;AAC7C,QAAIA,OAAM,YAAY,GAAG;AACvB,YAAM,KAAK,SAAS,MAAM,GAAG;AAC7B;AAAA,IACF;AACA,QAAI,CAACA,OAAM,OAAO,KAAK,CAACA,OAAM,KAAK,SAAS,MAAM,EAAG;AACrD,UAAM,WAAWC,MAAK,SAAS,SAAS,IAAI;AAC5C,UAAM,OAAO,SACV,QAAQ,UAAU,EAAE,EACpB,MAAMA,MAAK,GAAG,EACd,KAAK,GAAG;AACX,UAAM,OAAO,MAAMF,IAAG,SAAS,MAAM,MAAM;AAC3C,QAAI;AACJ,QAAI;AACF,YAAM,UAAU,IAAI;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,IAAI,KAAK,IAAI,MAAO,IAAc,OAAO;AAAA,MACxE;AAAA,IACF;AACA,UAAM,SAAS,oBAAoB,UAAU,GAAG;AAChD,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,UAAU;AACd,cAAM,QAAQ,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG,IAAI;AAC7D,eAAO,OAAO,KAAK,KAAK,MAAM,OAAO;AAAA,MACvC,CAAC,EACA,KAAK,IAAI;AACZ,YAAM,IAAI,MAAM,qBAAqB,IAAI,KAAK,IAAI;AAAA,EAAO,MAAM,EAAE;AAAA,IACnE;AACA,QAAI,IAAI,MAAM,EAAE,MAAM,YAAY,MAAM,MAAM,OAAO,KAAK,CAAC;AAAA,EAC7D;AACF;AAmDO,SAAS,gBACd,UACkB;AAClB,QAAM,YAAsB,CAAC;AAC7B,QAAM,WAAqB,CAAC;AAC5B,QAAM,eAAe,oBAAI,IAGvB;AAEF,aAAWC,UAAS,UAAU;AAC5B,UAAM,IAAI,oBAAoBA,MAAK,IAAIA,OAAM,YAAYA;AACzD,UAAM,UAAU,oBAAoBA,MAAK,IAAIA,OAAM,UAAU;AAC7D,UAAM,KAAK,EAAE,KAAK;AAClB,eAAW,QAAQ,GAAG,aAAa,CAAC,GAAG;AAKrC,YAAM,QAAQ,YAAY,SAAY,GAAG,IAAI,IAAI,OAAO,KAAK;AAC7D,UAAI,CAAC,UAAU,SAAS,KAAK,EAAG,WAAU,KAAK,KAAK;AAAA,IACtD;AACA,eAAW,OAAO,GAAG,YAAY,CAAC,GAAG;AACnC,UAAI,CAAC,SAAS,SAAS,GAAG,EAAG,UAAS,KAAK,GAAG;AAAA,IAChD;AACA,eAAW,KAAK,GAAG,YAAY,CAAC,GAAG;AACjC,YAAM,WAAW,aAAa,IAAI,EAAE,GAAG;AACvC,UAAI,CAAC,UAAU;AACb,qBAAa,IAAI,EAAE,KAAK;AAAA,UACtB,KAAK,EAAE;AAAA,UACP,SAAS,EAAE,GAAI,EAAE,WAAW,CAAC,EAAG;AAAA,QAClC,CAAC;AACD;AAAA,MACF;AACA,eAAS,UAAU,oBAAoB,SAAS,SAAS,EAAE,WAAW,CAAC,CAAC;AAAA,IAC1E;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,CAAC,GAAG,aAAa,OAAO,CAAC;AAAA,EACrC;AACF;AAEA,SAAS,oBACP,GACwB;AACxB,SAAO,eAAe;AACxB;AAEA,SAAS,oBACP,GACA,GAC2C;AAC3C,QAAM,SAAS,EAAE,GAAG,EAAE;AACtB,aAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,CAAC,GAAG;AAC7C,UAAM,SAAS,OAAO,GAAG;AACzB,QAAI,OAAO,WAAW,aAAa,OAAO,WAAW,WAAW;AAC9D,aAAO,GAAG,IAAI,UAAU;AACxB;AAAA,IACF;AACA,WAAO,GAAG,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAYO,SAAS,kBACd,SACA,OACqB;AACrB,QAAM,UAAoB,CAAC;AAC3B,QAAM,MAA2B,CAAC;AAClC,aAAW,OAAO,OAAO;AACvB,UAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,UAAM,OAAO,UAAU,KAAK,MAAM,IAAI,MAAM,GAAG,KAAK;AACpD,UAAM,UAAU,UAAU,KAAK,SAAY,IAAI,MAAM,QAAQ,CAAC;AAE9D,UAAM,IAAI,QAAQ,IAAI,IAAI;AAC1B,QAAI,CAAC,GAAG;AAGN,cAAQ,KAAK,GAAG;AAChB;AAAA,IACF;AACA,QAAI,YAAY,UAAa,EAAE,KAAK,aAAa,YAAY;AAC3D,YAAM,IAAI;AAAA,QACR,cAAc,IAAI,UAAU,EAAE,KAAK,QAAQ,+BAA0B,OAAO;AAAA,MAC9E;AAAA,IACF;AACA,QAAI,KAAK,EAAE,WAAW,GAAG,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC,EAAG,CAAC;AAAA,EAC1E;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,YAAY,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE,KAAK;AAC3C,UAAM,IAAI;AAAA,MACR,oBAAoB,QAAQ,SAAS,IAAI,MAAM,EAAE,KAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,aACxD,UAAU,KAAK,IAAI,CAAC;AAAA,IACtC;AAAA,EACF;AACA,SAAO;AACT;;;ACzRA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,SAAS,oBACd,MACA,YACA,gBACA,WAA8B,CAAC,GACvB;AACR,QAAM,SAAS,gBAAgB,UAAU;AACzC,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,cAAe,OAAM,KAAK,CAAC;AAC3C,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,kBAAkB;AAC7B,QAAM,KAAK,SAAS,IAAI,EAAE;AAC1B,QAAM,KAAK,EAAE;AAEb,MAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,UAAM,KAAK,YAAY;AACvB,eAAW,QAAQ,OAAO,UAAW,OAAM,KAAK,OAAO,IAAI,EAAE;AAC7D,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,UAAM,KAAK,WAAW;AACtB,eAAW,OAAO,OAAO,SAAU,OAAM,KAAK,OAAO,GAAG,EAAE;AAC1D,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,UAAM,KAAK,WAAW;AACtB,eAAW,KAAK,OAAO,UAAU;AAC/B;AAAA,QACE;AAAA,QACA;AAAA,QACA,eAAe,EAAE,GAAG;AAAA;AAAA,QACJ;AAAA,MAClB;AAAA,IACF;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAMA,MAAI,SAAS,SAAS,GAAG;AACvB;AAAA,MAAiB;AAAA,MAAO;AAAA;AAAA,MAA0B;AAAA,IAAK;AAAA,EACzD;AAEA,SAAO,sBAAsB,MAAM,KAAK,IAAI,CAAC;AAC/C;AAOO,SAAS,sBACd,MACA,SACA,gBACA,WAA8B,CAAC,GACvB;AACR,QAAM,aAAa,gBAAgB,OAAO;AAC1C,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,cAAe,OAAM,KAAK,CAAC;AAC3C,QAAM,KAAK,GAAG;AACd,QAAM,KAAK,yDAAyD;AACpE,QAAM,KAAK,4DAA4D;AACvE,QAAM,KAAK,2DAA2D;AACtE,QAAM,KAAK,+DAA0D;AACrE,QAAM,KAAK,mDAAmD;AAC9D,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,kBAAkB;AAC7B,QAAM,KAAK,SAAS,IAAI,EAAE;AAC1B,QAAM,KAAK,EAAE;AAEb,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,UAAM,QAAQ,WAAW,SAAS;AAAA,MAAQ,CAAC,OACxC,EAAE,KAAK,YAAY,aAAa,CAAC,GAAG,IAAI,CAAC,UAAU;AAAA,QAClD,OAAO;AAAA,QACP,OAAO,EAAE,KAAK;AAAA,MAChB,EAAE;AAAA,IACJ;AACA,UAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,IAAI;AAC9D,UAAM,KAAK,wCAAmC;AAC9C,UAAM,KAAK,cAAc;AACzB,eAAW,QAAQ,OAAO;AACxB,YAAM,MAAM,IAAI,OAAO,QAAQ,KAAK,MAAM,MAAM;AAChD,YAAM,KAAK,SAAS,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,KAAK,EAAE;AAAA,IACvD;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,UAAM,QAAQ,WAAW,QAAQ;AAAA,MAAQ,CAAC,OACvC,EAAE,KAAK,YAAY,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS;AAAA,QAChD,OAAO;AAAA,QACP,OAAO,EAAE,KAAK;AAAA,MAChB,EAAE;AAAA,IACJ;AACA,UAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,IAAI;AAC9D,UAAM,KAAK,0DAAqD;AAChE,UAAM,KAAK,wDAAwD;AACnE,UAAM,KAAK,2BAA2B;AACtC,UAAM,KAAK,aAAa;AACxB,eAAW,QAAQ,OAAO;AACxB,YAAM,MAAM,IAAI,OAAO,QAAQ,KAAK,MAAM,MAAM;AAChD,YAAM,KAAK,SAAS,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,KAAK,EAAE;AAAA,IACvD;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,MAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,UAAM,KAAK,8DAAyD;AACpE,UAAM,KAAK,wDAAwD;AACnE,UAAM,KAAK,wDAAwD;AACnE,UAAM,KAAK,uDAAuD;AAClE,UAAM,KAAK,0CAA0C;AACrD,UAAM,KAAK,8BAA8B;AACzC,UAAM,KAAK,GAAG;AACd,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,GAAG;AACd,UAAM,kBACJ,KAAK,IAAI,GAAG,WAAW,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC,IAAI;AAC9D,eAAW,KAAK,WAAW,SAAS;AAClC,YAAM,MAAM,IAAI,OAAO,kBAAkB,EAAE,KAAK,MAAM;AACtD,YAAM,KAAK,OAAO,EAAE,IAAI,GAAG,GAAG,GAAG,EAAE,KAAK,WAAW,EAAE;AAAA,IACvD;AACA,UAAM,KAAK,GAAG;AACd,UAAM,KAAK,qDAAqD;AAChE,UAAM,KAAK,wDAAwD;AACnE,UAAM,KAAK,6DAAwD;AACnE,UAAM,KAAK,+CAA+C;AAC1D,UAAM,KAAK,GAAG;AACd,UAAM,KAAK,aAAa;AAMxB,UAAM,eAAe,oBAAI,IAAY;AACrC,UAAM,WAAW,WAAW,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,SAAS,GAAG,CAAC;AAEvE,eAAW,KAAK,UAAU;AACxB,iBAAW,KAAK,EAAE,KAAK,YAAY,YAAY,CAAC,GAAG;AACjD,YAAI,aAAa,IAAI,EAAE,GAAG,EAAG;AAC7B,qBAAa,IAAI,EAAE,GAAG;AACtB;AAAA,UACE;AAAA,UACA;AAAA,UACA,eAAe,EAAE,GAAG;AAAA;AAAA,UACJ;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAIA,eAAW,KAAK,WAAW,SAAS;AAClC,UAAI,CAAC,EAAE,KAAK,SAAS,GAAG,EAAG;AAC3B,iBAAW,KAAK,EAAE,KAAK,YAAY,YAAY,CAAC,GAAG;AACjD,YAAI,aAAa,IAAI,EAAE,GAAG,EAAG;AAC7B,qBAAa,IAAI,EAAE,GAAG;AACtB;AAAA,UACE;AAAA,UACA;AAAA,UACA,eAAe,EAAE,GAAG;AAAA;AAAA,UACJ;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAOA;AAAA,IAAiB;AAAA,IAAO;AAAA;AAAA,IAA0B,SAAS,WAAW;AAAA,EAAC;AAEvE,SAAO,sBAAsB,MAAM,KAAK,IAAI,CAAC;AAC/C;AAUA,IAAM,gBAAgB;AAEtB,SAAS,mBACP,KACA,SACA,SACA,WACM;AACN,QAAM,IAAI,YAAY,SAAS;AAC/B,QAAM,cAAc,SAAS,eAAe,CAAC;AAC7C,QAAM,qBAAqB,SAAS,sBAAsB,CAAC;AAC3D,QAAM,aAAa,SAAS,cAAc,CAAC;AAK3C,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,QAAI,IAAI,EAAG,KAAI,KAAK,GAAG,CAAC,GAAG;AAC3B,eAAW,QAAQ;AAAA,MACjB,WAAW,CAAC;AAAA,MACZ,gBAAgB,EAAE;AAAA,IACpB,GAAG;AACD,UAAI,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,KAAK,GAAG,CAAC,UAAU,QAAQ,GAAG,EAAE;AACpC,QAAM,UAAU,QAAQ,WAAW,CAAC;AACpC,QAAM,gBAAgB,OAAO,QAAQ,OAAO;AAC5C,QAAM,iBAAiB,YAAY,OAAO,CAAC,MAAM,EAAE,KAAK,QAAQ;AAOhE,MAAI,cAAc,SAAS,GAAG;AAC5B,QAAI,KAAK,GAAG,CAAC,YAAY;AACzB,eAAW,CAAC,KAAK,KAAK,KAAK,eAAe;AACxC,UAAI,KAAK,GAAG,CAAC,OAAO,GAAG,KAAK,kBAAkB,KAAK,CAAC,EAAE;AAAA,IACxD;AACA,QAAI,eAAe,SAAS,GAAG;AAC7B,UAAI;AAAA,QACF,GAAG,CAAC;AAAA,MACN;AACA,iBAAW,QAAQ,gBAAgB;AACjC,iBAAS,KAAK,MAAM,mBAAmB,IAAI,GAAG,GAAG,CAAC,MAAM;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,WAAW,eAAe,SAAS,GAAG;AACpC,QAAI;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AACA,QAAI,KAAK,GAAG,CAAC,cAAc;AAC3B,eAAW,QAAQ,gBAAgB;AACjC,eAAS,KAAK,MAAM,mBAAmB,IAAI,GAAG,GAAG,CAAC,QAAQ;AAAA,IAC5D;AAAA,EACF;AACF;AASA,SAAS,SACP,KACA,MACA,aACA,YACM;AACN,MAAI,aAAa;AACf,eAAW,QAAQ;AAAA,MACjB;AAAA,MACA,gBAAgB,WAAW;AAAA,IAC7B,GAAG;AACD,UAAI,KAAK,GAAG,UAAU,KAAK,IAAI,EAAE;AAAA,IACnC;AAAA,EACF;AACA,MAAI,KAAK,GAAG,UAAU,GAAG,IAAI,GAAG;AAClC;AAaA,SAAS,iBACP,KACA,MACA,WACM;AAGN,MAAI,KAAK,8DAAyD;AAClE,MAAI,KAAK,8DAA8D;AACvE,MAAI,KAAK,iEAAiE;AAC1E,MAAI,KAAK,8DAA8D;AACvE,MAAI,KAAK,2DAAsD;AAC/D,MAAI,KAAK,GAAG;AAEZ,MAAI,WAAW;AAGb,QAAI,KAAK,UAAU;AACnB,QAAI,KAAK,gDAAgD;AACzD,QAAI;AAAA,MACF;AAAA,IACF;AACA,QAAI;AAAA,MACF;AAAA,IACF;AACA,QAAI;AAAA,MACF;AAAA,IACF;AACA,QAAI,KAAK,iBAAiB;AAC1B,QAAI,KAAK,6BAA6B;AACtC,QAAI,KAAK,oCAAoC;AAC7C,QAAI,KAAK,EAAE;AACX;AAAA,EACF;AAKA,MAAI,KAAK,QAAQ;AACjB,aAAW,OAAO,MAAM;AACtB,UAAM,cAAc,kBAAkB,GAAG;AACzC,QAAI,KAAK,YAAY,GAAG,EAAE;AAC1B,QAAI;AAAA,MACF,eAAe,WAAW,kEAAkE,WAAW;AAAA,IACzG;AACA,QAAI;AAAA,MACF;AAAA,IACF;AACA,QAAI;AAAA,MACF;AAAA,IACF;AACA,QAAI,KAAK,eAAe;AACxB,QAAI,KAAK,2BAA2B;AACpC,QAAI,KAAK,kCAAkC;AAAA,EAC7C;AACA,MAAI,KAAK,EAAE;AACb;AAQA,SAAS,kBAAkB,KAAqB;AAC9C,MAAI,OAAO;AACX,QAAM,QAAQ,IAAI,YAAY,GAAG;AACjC,MAAI,SAAS,EAAG,QAAO,IAAI,MAAM,QAAQ,CAAC;AAC1C,MAAI,KAAK,SAAS,MAAM,EAAG,QAAO,KAAK,MAAM,GAAG,EAAE;AAClD,SAAO,QAAQ;AACjB;AASA,SAAS,cAAc,MAAc,OAAyB;AAC5D,QAAM,QAAQ,KAAK,MAAM,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC1D,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC,EAAE;AAClC,QAAM,SAAS,KAAK,IAAI,OAAO,EAAE;AACjC,QAAM,QAAkB,CAAC;AACzB,MAAI,UAAU;AACd,aAAW,KAAK,OAAO;AACrB,QAAI,QAAQ,WAAW,GAAG;AACxB,gBAAU;AACV;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,IAAI,EAAE,UAAU,QAAQ;AAC3C,iBAAW,MAAM;AAAA,IACnB,OAAO;AACL,YAAM,KAAK,OAAO;AAClB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,OAAO;AAC1C,SAAO;AACT;AAEA,SAAS,kBAAkB,OAA0C;AACnE,MAAI,OAAO,UAAU,UAAU;AAK7B,WAAO,6BAA6B,KAAK,KAAK,IAC1C,QACA,KAAK,UAAU,KAAK;AAAA,EAC1B;AACA,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,gBAAgB,SAIvB;AACA,QAAM,MAA0C;AAAA,IAC9C,UAAU,CAAC;AAAA,IACX,SAAS,CAAC;AAAA,IACV,SAAS,CAAC;AAAA,EACZ;AAEA,QAAM,SAAS,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE;AAAA,IAAK,CAAC,GAAG,MAC5C,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EAC7B;AACA,aAAW,KAAK,QAAQ;AACtB,QAAI,EAAE,KAAK,QAAQ,EAAE,KAAK,CAAC;AAAA,EAC7B;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,GAAmB;AAChD,SAAO,EAAE,SAAS,IAAI,IAAI,IAAI,IAAI;AACpC;;;ACndA,SAAS,cAAAE,aAAY,gBAAAC,qBAAoB;AACzC,OAAOC,WAAU;AA2DjB,SAAS,oBACP,MACA,cACe;AACf,MAAI,cAAc;AAChB,UAAM,eAAeC,MAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAIC,YAAW,YAAY,EAAG,QAAO;AAAA,EACvC;AACA,QAAM,aAAaD,MAAK;AAAA,IACtB,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,MAAIC,YAAW,UAAU,EAAG,QAAO;AACnC,SAAO;AACT;AAEO,SAAS,2BACd,KACA,eAA8B,sBAAsB,GAChB;AACpC,QAAM,QAAQ,sBAAsB,GAAG;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,eAAe,oBAAoB,MAAM,MAAM,YAAY;AACjE,MAAI,CAAC,aAAc,QAAO;AAC1B,MAAI;AACF,UAAM,OAAOC,cAAa,cAAc,MAAM;AAC9C,UAAM,SAAS,KAAK,MAAM,IAAI;AAE9B,UAAM,WAAW,OAAO,aAAa,GAAG;AACxC,UAAM,cAAc,MAAM,QAAQ,QAAQ,IACtC,SAAS;AAAA,MACP,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS;AAAA,IAC1D,IACA,CAAC;AAEL,UAAM,WAAW,OAAO,aAAa,GAAG;AACxC,UAAM,aAAa,MAAM,QAAQ,QAAQ,IACrC,SAAS;AAAA,MACP,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS;AAAA,IAC1D,IACA,CAAC;AAEL,UAAM,qBAA6C,CAAC;AACpD,QAAI,OAAO,SAAS;AAClB,iBAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,OAAO,OAAO,GAAG;AACvD,YACE,OACA,OAAO,QAAQ,YACf,OAAO,IAAI,gBAAgB,YAC3B,IAAI,YAAY,SAAS,GACzB;AACA,6BAAmB,GAAG,IAAI,IAAI;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,aAAa,oBAAoB,WAAW;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AHnDA,eAAsB,QAAQ,MAA8C;AAC1E,QAAM,YAAY,KAAK,iBAAiB,cAAqB;AAC7D,QAAM,OAAO,KAAK,iBAAiB,cAAqB;AACxD,QAAM,SAAS,KAAK,UAAU;AAAA,IAC5B,SAAS,CAAC,QAAQC,UAAQ,QAAQ,GAAG;AAAA,IACrC,MAAM,CAAC,QAAQA,UAAQ,KAAK,GAAG;AAAA,EACjC;AAEA,MAAI,CAAC,MAAM,aAAa,KAAK,KAAK,IAAI,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,wBAAwB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,OAAO,oBAAoB,KAAK,MAAM,IAAI;AAChD,MAAIC,YAAW,IAAI,GAAG;AACpB,UAAM,IAAI;AAAA,MACR,0BAA0B,IAAI;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,qBAAqB,cAAqB,SAAS,CAAC;AAC1E,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,6BAA6B,cAAqB,SAAS,CAAC;AAAA,IAC9D;AAAA,EACF;AASA,QAAM,eAAe,KAAK,iBAAiB,sBAAsB;AACjE,QAAM,SAAS,CAAC,QAAgB,2BAA2B,KAAK,YAAY;AAU5E,QAAM,YAAY,KAAK,YAAY,CAAC,GACjC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,eAAe,oBAAI,IAAY;AACrC,aAAW,OAAO,UAAU;AAC1B,QAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,iBAAa,IAAI,GAAG;AACpB,UAAM,KAAK,GAAG;AAAA,EAChB;AACA,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,YAAsB,CAAC;AAC7B,eAAW,OAAO,OAAO;AACvB,UAAI;AACJ,UAAI;AACF,eAAO,IAAI,WAAW,UAAU,IAAI,IAAI,IAAI,GAAG,EAAE,WAAW;AAAA,MAC9D,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,CAAC,QAAQ,CAAC,qBAAqB,KAAK,YAAY,CAAC,GAAG;AACtD,kBAAU,KAAK,GAAG;AAAA,MACpB;AAAA,IACF;AACA,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,IAAI;AAAA,QACR;AAAA,UACE;AAAA,UACA,4BAA4B,UAAU,KAAK,IAAI,CAAC;AAAA,UAChD;AAAA,UACA;AAAA,QACF,EAAE,KAAK,IAAI;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAQA,MAAI;AACJ,QAAM,YAAY,KAAK,QAAQ,CAAC;AAChC,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,sBAAsB,KAAK,MAAM,SAAS,QAAQ,KAAK;AAAA,EAChE,OAAO;AACL,UAAM,aAAa,kBAAkB,SAAS,SAAS;AACvD,WAAO,oBAAoB,KAAK,MAAM,YAAY,QAAQ,KAAK;AAAA,EACjE;AAEA,QAAMC,KAAG,MAAM,oBAAoB,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC7D,QAAMA,KAAG,UAAU,MAAM,MAAM,MAAM;AAErC,QAAM,aAAa,UAAU,WAAW;AACxC,QAAM,cAAc,WAAW,IAAI;AACnC,MAAI,YAAY;AACd,WAAO;AAAA,MACL,+BAA+B,WAAW,sDAAsD,KAAK,IAAI;AAAA,IAC3G;AAAA,EACF,OAAO;AACL,WAAO;AAAA,MACL,YAAY,UAAU,MAAM,sBAAsB,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACxF;AACA,WAAO;AAAA,MACL,8DAA8D,KAAK,IAAI;AAAA,IACzE;AAAA,EACF;AAEA,SAAO,EAAE,YAAY,MAAM,WAAW;AACxC;;;AD3LO,IAAM,cAAcC,eAAc;AAAA,EACvC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,MAAM,QAAQ,GAAG;AAC3B,QAAI;AACF,YAAM,WAAW,gBAAgB,KAAK,MAAM,OAAO;AACnD,YAAM,eAAe,oBAAoB,OAAO;AAChD,YAAM,QAAQ;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,GAAI,WAAW,EAAE,MAAM,SAAS,IAAI,CAAC;AAAA,QACrC,GAAI,aAAa,SAAS,IAAI,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,MAC9D,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;AAQD,SAAS,oBAAoB,SAA6B;AACxD,QAAM,OAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,IAAI,QAAQ,CAAC;AACnB,QAAI,MAAM,eAAe;AACvB,YAAM,OAAO,QAAQ,IAAI,CAAC;AAC1B,UAAI,OAAO,SAAS,YAAY,CAAC,KAAK,WAAW,GAAG,GAAG;AACrD,aAAK,KAAK,IAAI;AACd,aAAK;AAAA,MACP;AAAA,IACF,WAAW,EAAE,WAAW,cAAc,GAAG;AACvC,WAAK,KAAK,EAAE,MAAM,eAAe,MAAM,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAkBA,SAAS,gBACP,SACA,SACsB;AACtB,MAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,EAAE,WAAW,GAAG;AAC9D,WAAO;AAAA,EACT;AACA,MAAI,WAAW,QAAQ,KAAK;AAK5B,QAAM,WAAW,QAAQ;AAAA,IACvB,CAAC,MAAM,MAAM,YAAY,EAAE,WAAW,SAAS;AAAA,EACjD;AACA,MAAI,YAAY,GAAG;AAGjB,QAAI,WAAW,WAAW;AAC1B,QAAI,QAAQ,QAAQ,MAAM,SAAU,aAAY;AAChD,aAAS,IAAI,UAAU,IAAI,QAAQ,QAAQ,KAAK,GAAG;AACjD,YAAM,IAAI,QAAQ,CAAC;AACnB,UAAI,EAAE,WAAW,IAAI,KAAK,MAAM,QAAQ,MAAM,SAAU;AAGxD,YAAM,MAAM,SAAS,SAAS,GAAG,IAAI,KAAK;AAC1C,kBAAY,MAAM;AAAA,IACpB;AAAA,EACF;AACA,QAAM,SAAS,SACZ,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,SAAO,OAAO,SAAS,IAAI,SAAS;AACtC;;;AKxHA,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAOxB,IAAM,kBAAkB;AAAA,EACtB,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AACX;AACA,IAAM,iBAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,wBAAwBC,gBAAc;AAAA,EACjD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM,CAAC;AAAA,EACP,MAAM,MAAM;AACV,QAAI;AACF,YAAM,UAAU,MAAM,qBAAqB;AAC3C,UAAI,QAAQ,SAAS,GAAG;AACtB,QAAAC,UAAQ;AAAA,UACN;AAAA,QACF;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,YAAM,MAAM,UAAU,QAAQ,MAAM;AACpC,YAAMC,SAAQ,QAAQ,OAAO,SAAS;AAGtC,YAAM,aAAa,oBAAI,IAGrB;AACF,iBAAW,KAAK,QAAQ,OAAO,GAAG;AAChC,cAAM,OAAO,WAAW,IAAI,EAAE,KAAK,QAAQ,KAAK,CAAC;AACjD,aAAK,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,YAAY,CAAC;AACpD,mBAAW,IAAI,EAAE,KAAK,UAAU,IAAI;AAAA,MACtC;AACA,iBAAW,QAAQ,WAAW,OAAO,GAAG;AACtC,aAAK,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,MAClD;AAMA,UAAI,CAACA,QAAO;AACV,YAAIC,SAAQ;AACZ,mBAAW,OAAO,gBAAgB;AAChC,gBAAM,QAAQ,WAAW,IAAI,GAAG;AAChC,cAAI,CAAC,SAAS,MAAM,WAAW,EAAG;AAClC,cAAI,CAACA,OAAO,SAAQ,OAAO,MAAM,IAAI;AACrC,UAAAA,SAAQ;AACR,kBAAQ,OAAO,MAAM,KAAK,GAAG;AAAA,CAAI;AACjC,qBAAW,EAAE,MAAM,KAAK,KAAK,OAAO;AAClC,oBAAQ,OAAO,MAAM,GAAG,IAAI,IAAK,IAAI;AAAA,CAAI;AAAA,UAC3C;AAAA,QACF;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAOA,UAAI,QAAQ;AACZ,iBAAW,OAAO,gBAAgB;AAChC,cAAM,QAAQ,WAAW,IAAI,GAAG;AAChC,YAAI,CAAC,SAAS,MAAM,WAAW,EAAG;AAClC,YAAI,CAAC,MAAO,SAAQ,OAAO,MAAM,IAAI;AACrC,gBAAQ;AACR,gBAAQ,OAAO,MAAM,GAAG,IAAI,YAAY,gBAAgB,GAAG,CAAC,CAAC;AAAA;AAAA,CAAM;AACnE,cAAM,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC;AAC7D,cAAM,SAAS;AACf,mBAAW,EAAE,MAAM,KAAK,KAAK,OAAO;AAClC,gBAAM,MAAM,IAAI,OAAO,YAAY,KAAK,SAAS,MAAM;AACvD,kBAAQ,OAAO,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI;AAAA,CAAI;AAAA,QAC3D;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,KAAK;AACZ,MAAAF,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AClGD,SAAS,iBAAAG,uBAAqB;AAKvB,IAAM,cAAcC,gBAAc;AAAA,EACvC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aACE;AAAA,MACF,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,WAAO;AAAA,MAAS,MACd,QAAQ;AAAA,QACN,MAAM,aAAa,KAAK,IAAI;AAAA,QAC5B,GAAI,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,QACpE,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;ACzCD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAIjB,IAAM,2BAA2BC,gBAAc;AAAA,EACpD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,UAAM,WAAW,CAAC,GAAG,aAAa,CAAC;AACnC,QAAI,SAAS,WAAW,GAAG;AACzB,MAAAC,UAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI;AACF,YAAM,SAAS,MAAM,qBAAqB;AAAA,QACxC,MAAM,KAAK;AAAA,QACX;AAAA,QACA,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAA,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC9CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAGjB,IAAM,uBAAuBC,gBAAc;AAAA,EAChD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,iBAAiB;AAAA,QACpC,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC5CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AACxB,SAAS,uBAAuB;;;ACFhC,SAAS,cAAAC,aAAY,YAAYC,YAAU;AAC3C,OAAOC,YAAU;AACjB,SAAS,WAAAC,iBAAe;AAwExB,eAAsB,UACpB,MAC0B;AAC1B,QAAM,OAAO,KAAK,iBAAiB,cAAqB;AACxD,QAAM,SAAS,KAAK,UAAU;AAAA,IAC5B,MAAM,CAAC,QAAQC,UAAQ,KAAK,GAAG;AAAA,IAC/B,SAAS,CAAC,QAAQA,UAAQ,QAAQ,GAAG;AAAA,IACrC,MAAM,CAAC,QAAQA,UAAQ,KAAK,GAAG;AAAA,EACjC;AAEA,MAAI,CAAC,MAAM,aAAa,KAAK,KAAK,IAAI,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,wBAAwB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,KAAK,MAAM,IAAI;AACnD,QAAM,gBAAgB,aAAa,KAAK,MAAM,IAAI;AAClD,QAAM,SAASC,YAAW,OAAO;AACjC,QAAM,eAAeA,YAAW,aAAa;AAE7C,MAAI,CAAC,UAAU,CAAC,cAAc;AAC5B,UAAM,IAAI;AAAA,MACR,0BAA0B,KAAK,IAAI,cAAc,OAAO,QAAQ,aAAa;AAAA,IAC/E;AAAA,EACF;AAGA,QAAM,cAAc,mBAAmB,aAAa;AACpD,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,SAAS;AAAA,IACb;AAAA,IACA,8CAA8C,WAAW;AAAA;AAAA,IAEzD,uEAAuE,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOlF,yEAAyE,aAAa;AAAA;AAAA,IAEtF,mDAAmD,WAAW;AAAA;AAAA;AAAA,IAG9D,qDAAqD,KAAK,IAAI;AAAA,IAC9D;AAAA,IACA;AAAA,IACA,qBAAqB,WAAW,kDAAkD,WAAW;AAAA,IAC7F;AAAA,EACF,EAAE,KAAK,IAAI;AACX,QAAM,iBAAiB,MAAM,YAAY,CAAC,MAAM,MAAM,GAAG,IAAI;AAG7D,MAAI,aAA4B;AAChC,MAAI,CAAC,KAAK,aAAa,UAAU,eAAe;AAC9C,UAAM,MAAM,KAAK,OAAO,oBAAI,KAAK,GAAG,YAAY,EAAE,QAAQ,SAAS,GAAG;AACtE,iBAAaC,OAAK,KAAK,MAAM,qBAAqB,GAAG,KAAK,IAAI,IAAI,EAAE,EAAE;AACtE,UAAMC,KAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAI,QAAQ;AACV,YAAMA,KAAG,SAAS,SAASD,OAAK,KAAK,YAAY,GAAG,KAAK,IAAI,MAAM,CAAC;AAAA,IACtE;AACA,QAAI,cAAc;AAChB,YAAMC,KAAG,GAAG,eAAeD,OAAK,KAAK,YAAY,WAAW,GAAG;AAAA,QAC7D,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AACA,WAAO,KAAK,qBAAqB,WAAW,UAAU,CAAC,GAAG;AAAA,EAC5D;AAGA,MAAI,QAAQ;AACV,UAAMC,KAAG,GAAG,SAAS,EAAE,OAAO,KAAK,CAAC;AAAA,EACtC;AACA,MAAI,cAAc;AAChB,UAAMA,KAAG,GAAG,eAAe,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAC7D;AAEA,SAAO;AAAA,IACL,YAAY,KAAK,IAAI;AAAA,EACvB;AACA,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY,SAAS,UAAU;AAAA,IAC/B,eAAe,eAAe,gBAAgB;AAAA,IAC9C;AAAA,IACA;AAAA,EACF;AACF;;;ADnKO,IAAM,gBAAgBC,gBAAc;AAAA,EACzC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQN,aACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,WAAW,KAAK,WAAW;AACjC,YAAM,aAAa,KAAK,QAAQ;AAEhC,UAAI,CAAC,YAAY;AACf,cAAM,UAAU,WACZ,oBAAoB,KAAK,IAAI,8GAC7B,oBAAoB,KAAK,IAAI;AACjC,QAAAC,UAAQ,KAAK,OAAO;AACpB,cAAM,KAAK,gBAAgB;AAAA,UACzB,OAAO,QAAQ;AAAA,UACf,QAAQ,QAAQ;AAAA,QAClB,CAAC;AACD,cAAM,SAAS,MAAM,GAAG,SAAS,kBAAkB;AACnD,WAAG,MAAM;AACT,YAAI,CAAC,YAAY,KAAK,OAAO,KAAK,CAAC,GAAG;AACpC,UAAAA,UAAQ,KAAK,2BAA2B;AACxC,kBAAQ,KAAK,CAAC;AAAA,QAChB;AAAA,MACF;AAEA,YAAM,UAAU;AAAA,QACd,MAAM,KAAK;AAAA,QACX,GAAI,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;AAAA,MACvC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,MAAAA,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AEvED,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;;;ACDxB,SAAS,cAAAC,aAAY,YAAYC,YAAU;AAC3C,OAAOC,YAAU;AACjB,SAAS,WAAAC,iBAAe;AAyDxB,eAAsB,WACpB,MAC2B;AAC3B,QAAM,OAAO,KAAK,iBAAiB,cAAqB;AACxD,QAAM,SAAS,KAAK,UAAU;AAAA,IAC5B,MAAM,CAAC,QAAQC,UAAQ,KAAK,GAAG;AAAA,IAC/B,SAAS,CAAC,QAAQA,UAAQ,QAAQ,GAAG;AAAA,EACvC;AAEA,QAAM,SAASC,OAAK,QAAQ,KAAK,UAAU;AAC3C,MAAI,CAACC,YAAW,MAAM,GAAG;AACvB,UAAM,IAAI,MAAM,qBAAqB,MAAM,GAAG;AAAA,EAChD;AACA,QAAM,OAAO,MAAMC,KAAG,KAAK,MAAM;AACjC,MAAI,CAAC,KAAK,YAAY,GAAG;AACvB,UAAM,IAAI,MAAM,mCAAmC,MAAM,GAAG;AAAA,EAC9D;AAMA,QAAM,UAAU,MAAMA,KAAG,QAAQ,MAAM;AACvC,QAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,CAAC;AACzD,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,aAAa,MAAM;AAAA,IACrB;AAAA,EACF;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,aAAa,MAAM,kCAAkC,SAAS,KAAK,IAAI,CAAC;AAAA,IAC1E;AAAA,EACF;AACA,QAAM,UAAU,SAAS,CAAC;AAC1B,QAAM,OAAO,QAAQ,QAAQ,UAAU,EAAE;AAEzC,QAAM,oBAAoBF,OAAK,KAAK,QAAQ,WAAW;AACvD,QAAM,eAAeC,YAAW,iBAAiB;AAGjD,QAAM,UAAU,oBAAoB,MAAM,IAAI;AAC9C,QAAM,gBAAgB,aAAa,MAAM,IAAI;AAC7C,MAAIA,YAAW,OAAO,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,wBAAwB,OAAO,2EAA2E,IAAI;AAAA,IAChH;AAAA,EACF;AACA,MAAI,gBAAgBA,YAAW,aAAa,GAAG;AAC7C,UAAM,IAAI;AAAA,MACR,wBAAwB,aAAa,2EAA2E,IAAI;AAAA,IACtH;AAAA,EACF;AAGA,QAAMC,KAAG,MAAM,oBAAoB,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC7D,QAAMA,KAAG,SAASF,OAAK,KAAK,QAAQ,OAAO,GAAG,OAAO;AACrD,MAAI,cAAc;AAChB,UAAME,KAAG,GAAG,mBAAmB,eAAe,EAAE,WAAW,KAAK,CAAC;AAAA,EACnE;AAEA,SAAO,QAAQ,aAAa,IAAI,UAAU,WAAW,MAAM,CAAC,GAAG;AAC/D,SAAO;AAAA,IACL,yBAAyB,IAAI;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,eAAe,eAAe,gBAAgB;AAAA,EAChD;AACF;;;AD9HO,IAAM,iBAAiBC,gBAAc;AAAA,EAC1C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,eAAe;AAAA,MACb,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,WAAW,EAAE,YAAY,KAAK,aAAa,EAAE,CAAC;AAAA,IACtD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AE3BD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAGjB,IAAM,uBAAuBC,gBAAc;AAAA,EAChD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,iBAAiB;AAAA,QACpC,MAAM,KAAK;AAAA,QACX,KAAK,KAAK;AAAA,QACV,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC3CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAGjB,IAAM,wBAAwBC,gBAAc;AAAA,EACjD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,kBAAkB;AAAA,QACrC,MAAM,KAAK;AAAA,QACX,UAAU,KAAK;AAAA,QACf,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC3CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAGjB,IAAM,oBAAoBC,gBAAc;AAAA,EAC7C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,cAAc;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,QACb,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC3CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAGjB,IAAM,uBAAuBC,gBAAc;AAAA,EAChD,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,UAAU;AAAA,IACZ;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO,CAAC,GAAG;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,iBAAiB;AAAA,QACpC,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,KAAK,KAAK;AAAA,MACZ,CAAC;AACD,cAAQ,KAAK,OAAO,WAAW,YAAY,IAAI,CAAC;AAAA,IAClD,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC3CD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;;;ACDxB,SAAS,cAAAC,oBAAkB;AAC3B,OAAOC,YAAU;AAajB,eAAsB,SAAS,MAAwC;AACrE,wBAAsB,KAAK,IAAI;AAC/B,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,SAAS,MAAM;AAAA,IACnB,CAAC,MAAM,sBAAsB,KAAK,MAAM,kCAAkC;AAAA,IAC1E,KAAK;AAAA,IACL,EAAE,OAAO,KAAK;AAAA,EAChB;AACA,MAAI,WAAW,EAAG,QAAO;AAEzB,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,EAAE,aAAa,KAAK;AAAA,EACtB;AACF;AAEO,SAAS,sBAAsB,MAAoB;AACxD,MAAI,CAACC,aAAWC,OAAK,KAAK,MAAM,eAAe,CAAC,GAAG;AACjD,UAAM,IAAI;AAAA,MACR,wBAAwB,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;;;AC9BA,eAAsB,eACpB,MACiB;AACjB,MAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,wBAAsB,KAAK,IAAI;AAC/B,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,SAAS,MAAM;AAAA,IACnB,CAAC,MAAM,sBAAsB,KAAK,MAAM,kCAAkC;AAAA,IAC1E,KAAK;AAAA,IACL,EAAE,OAAO,KAAK;AAAA,EAChB;AACA,MAAI,WAAW,EAAG,QAAO;AAEzB,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,GAAG,KAAK;AAAA,IACV;AAAA,IACA,KAAK;AAAA,IACL,EAAE,aAAa,KAAK;AAAA,EACtB;AACF;;;AFrCO,IAAM,aAAaC,gBAAc;AAAA,EACtC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,UAAM,UAAU,CAAC,GAAG,aAAa,CAAC;AAClC,QAAI,QAAQ,WAAW,GAAG;AACxB,MAAAC,UAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI;AACF,YAAM,WAAW,MAAM,eAAe;AAAA,QACpC,MAAM,aAAa,KAAK,IAAI;AAAA,QAC5B;AAAA,MACF,CAAC;AACD,cAAQ,KAAK,QAAQ;AAAA,IACvB,SAAS,KAAK;AACZ,MAAAA,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AGxCD,SAAS,iBAAAC,uBAAqB;AAC9B,SAAS,WAAAC,iBAAe;AAIjB,IAAM,eAAeC,gBAAc;AAAA,EACxC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,QAAI;AACF,YAAM,WAAW,MAAM,SAAS,EAAE,MAAM,aAAa,KAAK,IAAI,EAAE,CAAC;AACjE,cAAQ,KAAK,QAAQ;AAAA,IACvB,SAAS,KAAK;AACZ,MAAAC,UAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF,CAAC;;;AC7BD,SAAS,iBAAAC,uBAAqB;AAKvB,IAAM,eAAeC,gBAAc;AAAA,EACxC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,WAAO,SAAS,MAAM,SAAS,EAAE,MAAM,aAAa,KAAK,IAAI,EAAE,CAAC,CAAC;AAAA,EACnE;AACF,CAAC;;;ACvBD,SAAS,iBAAAC,uBAAqB;AAKvB,IAAM,gBAAgBC,gBAAc;AAAA,EACzC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,WAAO;AAAA,MAAS,MACd,UAAU;AAAA,QACR,MAAM,aAAa,KAAK,IAAI;AAAA,QAC5B,GAAI,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MACtE,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;ACjCD,SAAS,iBAAAC,uBAAqB;AAKvB,IAAM,cAAcC,gBAAc;AAAA,EACvC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,MACF,UAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,IAAI,EAAE,KAAK,GAAG;AACZ,WAAO;AAAA,MAAS,MACd,QAAQ;AAAA,QACN,MAAM,aAAa,KAAK,IAAI;AAAA,QAC5B,GAAI,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MACtE,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;;;AtDNM,IAAM,OAAOC,gBAAc;AAAA,EAChC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,aACE;AAAA,EACJ;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,mBAAmB;AAAA,IACnB,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,YAAY;AAAA,EACd;AACF,CAAC;;;AHlDD,gCAAgC;AAEhC,eAAe,QAAuB;AAOpC,MAAI,MAAM,gBAAgB,QAAQ,KAAK,MAAM,CAAC,GAAG,IAAI,GAAG;AACtD;AAAA,EACF;AACA,QAAM,QAAQ,IAAI;AACpB;AAEA,MAAM,EAAE,MAAM,CAAC,QAAiB;AAG9B,UAAQ;AAAA,IACN,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,GAAG;AAAA,EAChE;AACA,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["entry","main","path","defineCommand","consola","fs","existsSync","fs","path","APT_PACKAGE_NAME_RE","FEATURE_REF_RE","INSTALL_URL_RE","REPO_URL_RE","REPO_PATH_RE","entry","path","existsSync","fs","entry","path","path","entry","fs","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","existsSync","fs","consola","fs","z","parseDocument","z","fs","parseDocument","fs","path","path","fs","entry","ANSI_BOLD","ANSI_UNDERLINE","ANSI_CYAN","ANSI_GREY","ANSI_RESET","isTty","ANSI_RESET","ANSI_BOLD","ANSI_UNDERLINE","ANSI_CYAN","ANSI_GREY","bold","underline","cyan","spawn","existsSync","path","consola","readFileSync","path","readFileSync","path","child","spawn","path","existsSync","consola","spawn","fs","path","spawn","cyan","path","fs","cyan","spawn","spawn","cyan","spawn","spawn","fs","path","consola","consola","existsSync","fs","cyan","entry","consola","defineCommand","defineCommand","defineCommand","consola","existsSync","fs","consola","existsSync","fs","path","z","z","existsSync","fs","entry","path","existsSync","readFileSync","path","path","existsSync","readFileSync","consola","existsSync","fs","defineCommand","consola","defineCommand","consola","defineCommand","consola","isTty","first","defineCommand","defineCommand","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","existsSync","fs","path","consola","consola","existsSync","path","fs","defineCommand","consola","defineCommand","consola","existsSync","fs","path","consola","consola","path","existsSync","fs","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","existsSync","path","existsSync","path","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand"]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/help.ts","../src/inner-args.ts","../src/main.ts","../src/commands/add-apt-packages.ts","../src/modify/index.ts","../src/config/io.ts","../src/config/schema.ts","../src/config/paths.ts","../src/create/catalog.ts","../src/create/scaffold.ts","../src/util/ref.ts","../src/modify/yml.ts","../src/commands/add-feature.ts","../src/commands/add-from-url.ts","../src/commands/add-repo.ts","../src/commands/add-language.ts","../src/commands/add-service.ts","../src/commands/apply.ts","../src/apply/index.ts","../src/config/global.ts","../src/config/state.ts","../src/config/transform.ts","../src/util/format.ts","../src/devcontainer/compose.ts","../src/util/mask-secrets.ts","../src/devcontainer/cli.ts","../src/devcontainer/credentials.ts","../src/devcontainer/repo-reachability.ts","../src/devcontainer/docker-mode.ts","../src/devcontainer/identity.ts","../src/version.ts","../src/commands/_dispatch.ts","../src/commands/completion.ts","../src/commands/init.ts","../src/init/index.ts","../src/init/components.ts","../src/init/generator.ts","../src/init/manifest.ts","../src/commands/list-components.ts","../src/commands/logs.ts","../src/commands/remove-apt-packages.ts","../src/commands/remove-feature.ts","../src/commands/remove.ts","../src/remove/index.ts","../src/commands/restore.ts","../src/restore/index.ts","../src/commands/remove-from-url.ts","../src/commands/remove-language.ts","../src/commands/remove-repo.ts","../src/commands/remove-service.ts","../src/commands/run.ts","../src/devcontainer/shell.ts","../src/devcontainer/run.ts","../src/commands/shell.ts","../src/commands/start.ts","../src/commands/status.ts","../src/commands/stop.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runMain } from 'citty';\nimport { maybeRenderHelp } from './help.js';\nimport { consumeInnerArgsFromProcessArgv } from './inner-args.js';\nimport { main } from './main.js';\n\n// Pull everything after `--` out of argv before citty starts parsing.\n// Otherwise citty's eager --help/--version handling shadows the inner\n// command (e.g. `monoceros run -- foo --help` would show monoceros run's\n// own help, not foo's).\nconsumeInnerArgsFromProcessArgv();\n\nasync function entry(): Promise<void> {\n // We render `--help` ourselves so the USAGE line shows positional\n // arguments *before* `[OPTIONS]`, matching the\n // `monoceros <command> <containername> [<args> …]` convention. Citty's\n // built-in renderer puts `[OPTIONS]` first which is the opposite. When\n // help was rendered, exit before handing off to citty so its own help\n // doesn't double up.\n if (await maybeRenderHelp(process.argv.slice(2), main)) {\n return;\n }\n await runMain(main);\n}\n\nentry().catch((err: unknown) => {\n // runMain handles its own errors; this catch is a safety net for the\n // help path.\n console.error(\n err instanceof Error ? (err.stack ?? err.message) : String(err),\n );\n process.exit(1);\n});\n","import type { CommandDef } from 'citty';\n\n/**\n * Custom help renderer. Citty's built-in `renderUsage` has two\n * issues for Monoceros:\n *\n * 1. It puts `[OPTIONS]` *before* the positional arguments in the\n * USAGE line. We want positionals first, matching the\n * `monoceros <command> <containername> [<args> …]` shape\n * documented in konzept.md and docs/commands/README.md.\n * 2. It lists all subcommands in a flat pipe-separated USAGE line\n * (`monoceros init|shell|run|…`) and a flat COMMANDS block.\n * With 20+ commands that becomes unreadable.\n *\n * This module checks for `--help` / `-h` in argv before citty gets a\n * chance to print its own help. When triggered, it resolves the\n * matching subcommand and prints our own block: positional-first\n * USAGE, COMMANDS grouped by `meta.group`, and descriptions wrapped\n * to terminal width.\n */\n\ninterface ResolvedArg {\n name: string;\n type: 'positional' | 'string' | 'boolean' | 'number' | 'enum';\n required?: boolean;\n description?: string;\n default?: unknown;\n alias?: string | string[];\n valueHint?: string;\n}\n\nconst ANSI_BOLD = '\\x1b[1m';\nconst ANSI_UNDERLINE = '\\x1b[4m';\nconst ANSI_CYAN = '\\x1b[36m';\nconst ANSI_GREY = '\\x1b[90m';\nconst ANSI_RESET = '\\x1b[0m';\n\nfunction isTty(): boolean {\n return process.stdout.isTTY ?? false;\n}\n\nfunction color(text: string, ...codes: string[]): string {\n if (!isTty()) return text;\n return codes.join('') + text + ANSI_RESET;\n}\n\nconst bold = (s: string) => color(s, ANSI_BOLD);\nconst underline = (s: string) => color(s, ANSI_UNDERLINE);\nconst cyan = (s: string) => color(s, ANSI_CYAN);\nconst grey = (s: string) => color(s, ANSI_GREY);\n\n/**\n * Ordered list of command-group keys with a human-readable label.\n * Anything a command file tags via `meta.group` lands in the\n * matching bucket; anything ungrouped falls through to \"Other\".\n * The render order follows this array.\n */\nconst GROUPS: ReadonlyArray<{ key: string; label: string }> = [\n { key: 'lifecycle', label: 'Container lifecycle' },\n { key: 'run', label: 'Run + inspect' },\n { key: 'edit', label: 'Edit container yml' },\n { key: 'discovery', label: 'Discovery' },\n { key: 'tooling', label: 'Tooling' },\n];\n\nfunction resolveArgs(\n argsDef: Record<string, unknown> | undefined,\n): ResolvedArg[] {\n if (!argsDef) return [];\n const out: ResolvedArg[] = [];\n for (const [name, defRaw] of Object.entries(argsDef)) {\n const def = (defRaw ?? {}) as Partial<ResolvedArg>;\n out.push({\n name,\n type: (def.type as ResolvedArg['type']) ?? 'string',\n required: def.required,\n description: def.description,\n default: def.default,\n alias: def.alias,\n valueHint: def.valueHint,\n });\n }\n return out;\n}\n\nfunction renderValueHint(arg: ResolvedArg): string {\n if (arg.type === 'boolean') return '';\n const hint = arg.valueHint ?? arg.name;\n return `=<${hint}>`;\n}\n\nfunction renderArgDescription(arg: ResolvedArg, isRequired: boolean): string {\n const parts: string[] = [];\n if (arg.description) parts.push(arg.description);\n if (isRequired) parts.push(grey('(Required)'));\n if (arg.default !== undefined && arg.type !== 'boolean') {\n parts.push(grey(`(Default: ${JSON.stringify(arg.default)})`));\n }\n return parts.join(' ');\n}\n\n// Strip ANSI escape sequences so column-padding measurements use\n// the visible width instead of the raw character count.\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;]*m/g;\n\nfunction visibleLen(s: string): number {\n return s.replace(ANSI_RE, '').length;\n}\n\nfunction terminalWidth(): number {\n return process.stdout.columns && process.stdout.columns > 40\n ? process.stdout.columns\n : 100;\n}\n\n/**\n * Wrap `text` (which may contain ANSI codes) to fit `width` columns,\n * with `continuationIndent` prepended to every wrapped line after\n * the first. Word-aware: breaks at spaces, falls back to hard breaks\n * only for individual tokens longer than `width`.\n */\nfunction wrapText(\n text: string,\n width: number,\n continuationIndent: string,\n): string {\n if (visibleLen(text) <= width) return text;\n // `width` is the budget for the actual text on each line — it does\n // not include the continuation indent (caller already accounted for\n // it when computing width). Continuation-line indent gets prefixed\n // at join time, so every line gets the same text-column budget.\n const words = text.split(/(\\s+)/);\n const lines: string[] = [];\n let current = '';\n for (const w of words) {\n if (visibleLen(current) + visibleLen(w) <= width) {\n current += w;\n continue;\n }\n if (current.length > 0) lines.push(current.replace(/\\s+$/, ''));\n current = w.replace(/^\\s+/, '');\n }\n if (current.length > 0) lines.push(current.replace(/\\s+$/, ''));\n return lines.map((l, i) => (i === 0 ? l : continuationIndent + l)).join('\\n');\n}\n\n/**\n * Render a left-aligned label column next to wrapped descriptions.\n * Column gutter is four spaces. Description wraps within the\n * remaining terminal width.\n */\nfunction alignTable(rows: Array<[string, string]>, indent: string): string {\n if (rows.length === 0) return '';\n const labelWidth = Math.max(...rows.map((r) => visibleLen(r[0])));\n const gutter = ' ';\n const descWidth =\n terminalWidth() - indent.length - labelWidth - gutter.length;\n const continuationIndent = ' '.repeat(\n indent.length + labelWidth + gutter.length,\n );\n return rows\n .map(([left, right]) => {\n const pad = ' '.repeat(labelWidth - visibleLen(left));\n const wrapped = wrapText(right, descWidth, continuationIndent);\n return `${indent}${left}${pad}${gutter}${wrapped}`;\n })\n .join('\\n');\n}\n\ninterface SubCommandEntry {\n name: string;\n description: string;\n group: string;\n}\n\nfunction collectSubCommands(cmd: CommandDef): SubCommandEntry[] {\n const subs = (cmd.subCommands ?? {}) as Record<string, CommandDef>;\n const out: SubCommandEntry[] = [];\n for (const [name, sub] of Object.entries(subs)) {\n const meta = (sub?.meta ?? {}) as {\n hidden?: boolean;\n description?: string;\n group?: string;\n };\n if (meta.hidden) continue;\n out.push({\n name,\n description: meta.description ?? '',\n group: meta.group ?? 'other',\n });\n }\n return out;\n}\n\nfunction renderCommandsBlock(entries: SubCommandEntry[]): string[] {\n if (entries.length === 0) return [];\n const lines: string[] = [];\n lines.push(underline(bold('COMMANDS')));\n\n // Group entries while preserving GROUPS' declared order. Anything\n // tagged with an unknown group (or no group) falls into \"Other\"\n // and renders last.\n const byGroup = new Map<string, SubCommandEntry[]>();\n for (const entry of entries) {\n const arr = byGroup.get(entry.group) ?? [];\n arr.push(entry);\n byGroup.set(entry.group, arr);\n }\n\n const renderSection = (label: string, items: SubCommandEntry[]) => {\n if (items.length === 0) return;\n lines.push('');\n // Group label is left-aligned, underlined, in grey — distinct\n // from the section headers above (which are bold+underlined+white)\n // through colour + weight. Blank line after gives the items room\n // to breathe; cyan command labels do the visual separation from\n // the heading without needing an indent, so commands go flush\n // left and the descriptions get the full terminal width.\n lines.push(underline(grey(label)));\n lines.push('');\n const rows: Array<[string, string]> = items.map((e) => [\n cyan(e.name),\n e.description,\n ]);\n lines.push(alignTable(rows, ''));\n };\n\n for (const { key, label } of GROUPS) {\n renderSection(label, byGroup.get(key) ?? []);\n byGroup.delete(key);\n }\n // Anything left over (ungrouped or unknown-group) lands in a\n // catch-all section so nothing silently disappears.\n for (const [groupKey, items] of byGroup) {\n const label = groupKey === 'other' ? 'Other' : groupKey;\n renderSection(label, items);\n }\n\n lines.push('');\n return lines;\n}\n\nexport function renderUsageBlock(\n cmd: CommandDef,\n commandPath: string[],\n): string {\n const meta = (cmd.meta ?? {}) as {\n name?: string;\n description?: string;\n version?: string;\n };\n const args = resolveArgs((cmd.args ?? {}) as Record<string, unknown>);\n const subCommandEntries = collectSubCommands(cmd);\n\n const fullName = commandPath.join(' ') || meta.name || 'monoceros';\n\n const positionals = args.filter((a) => a.type === 'positional');\n const flags = args.filter((a) => a.type !== 'positional');\n\n // USAGE line: positionals come first, then [OPTIONS]. When the\n // command has subcommands, render a single `<command>` placeholder\n // instead of a pipe-separated list — anything more than a couple\n // of subcommands makes the pipe list unreadable, and the COMMANDS\n // block below carries the actual menu.\n const usageTokens: string[] = [];\n for (const p of positionals) {\n const isRequired = p.required !== false && p.default === undefined;\n const t = p.name.toUpperCase();\n usageTokens.push(isRequired ? `<${t}>` : `[${t}]`);\n }\n if (subCommandEntries.length > 0) usageTokens.push('<command>');\n if (flags.length > 0) usageTokens.push('[OPTIONS]');\n\n const lines: string[] = [];\n const version = meta.version;\n const header = `${meta.description ?? ''} (${fullName}${version ? ` v${version}` : ''})`;\n lines.push(grey(wrapText(header, terminalWidth(), '')));\n lines.push('');\n lines.push(\n `${underline(bold('USAGE'))} ${cyan([fullName, ...usageTokens].join(' '))}`,\n );\n lines.push('');\n\n if (positionals.length > 0) {\n lines.push(underline(bold('ARGUMENTS')));\n lines.push('');\n const rows: Array<[string, string]> = positionals.map((p) => {\n const isRequired = p.required !== false && p.default === undefined;\n return [cyan(p.name.toUpperCase()), renderArgDescription(p, isRequired)];\n });\n lines.push(alignTable(rows, ' '));\n lines.push('');\n }\n\n if (flags.length > 0) {\n lines.push(underline(bold('OPTIONS')));\n lines.push('');\n const rows: Array<[string, string]> = flags.map((f) => {\n const isRequired = f.required === true && f.default === undefined;\n const aliases = (\n Array.isArray(f.alias) ? f.alias : f.alias ? [f.alias] : []\n ).map((a) => `-${a}`);\n const label = [...aliases, `--${f.name}`].join(', ') + renderValueHint(f);\n return [cyan(label), renderArgDescription(f, isRequired)];\n });\n lines.push(alignTable(rows, ' '));\n lines.push('');\n }\n\n if (subCommandEntries.length > 0) {\n for (const line of renderCommandsBlock(subCommandEntries)) {\n lines.push(line);\n }\n lines.push(\n `Use ${cyan(`${fullName} <command> --help`)} for more information about a command.`,\n );\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Detect `--help` / `-h` somewhere in argv that's *not* preceded by\n * a separator `--`. Returns the command path so the caller can render\n * the right subcommand's help.\n *\n * Returns `null` when help wasn't requested at all.\n */\nexport function detectHelpRequest(\n argv: string[],\n main: CommandDef,\n): { path: string[]; cmd: CommandDef } | null {\n const helpIdx = argv.findIndex((a) => a === '--help' || a === '-h');\n const separatorIdx = argv.indexOf('--');\n if (helpIdx === -1) return null;\n if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;\n\n // Walk subcommands by matching argv tokens (in order, before --)\n // against the current command's `subCommands` map.\n const path: string[] = [];\n const tokens = argv.slice(\n 0,\n separatorIdx === -1 ? argv.length : separatorIdx,\n );\n let cursor: CommandDef = main;\n const mainName = ((main.meta ?? {}) as { name?: string }).name ?? 'monoceros';\n path.push(mainName);\n for (const tok of tokens) {\n if (tok.startsWith('-')) continue;\n const subs = (cursor.subCommands ?? {}) as Record<string, CommandDef>;\n if (tok in subs) {\n cursor = subs[tok]!;\n path.push(tok);\n continue;\n }\n // Token isn't a subcommand name — stop walking. Any further\n // tokens are positionals/values for the current command, not\n // routing hints.\n break;\n }\n return { path, cmd: cursor };\n}\n\n/**\n * If argv requests --help, print our own usage block and tell the\n * caller to exit. Returns true when help was rendered.\n */\nexport async function maybeRenderHelp(\n argv: string[],\n main: CommandDef,\n): Promise<boolean> {\n const hit = detectHelpRequest(argv, main);\n if (!hit) return false;\n // Resolve cmd's lazy fields (citty allows them to be functions)\n // before we render. We don't currently use lazy fields, so a\n // simple pass-through suffices.\n process.stdout.write(renderUsageBlock(hit.cmd, hit.path) + '\\n');\n return true;\n}\n","/**\n * Splits the user-args at the first `--` marker.\n *\n * `monoceros run -- monoceros-plugin --help` should hand `--help` to\n * `monoceros-plugin` inside the container, not trigger citty's eager\n * `--help` parser on the outer `monoceros run`. Citty parses `--help`\n * and `--version` before our subcommand handlers run, so the only\n * reliable fix is to strip everything after `--` from `process.argv`\n * before `runMain()` ever sees it.\n *\n * `splitInnerArgs` is the pure helper.\n * `consumeInnerArgsFromProcessArgv` is the side-effecting glue called\n * from `bin.ts`. `getInnerArgs()` is read by `runCommand`.\n */\n\nlet innerArgs: readonly string[] = [];\n\nexport function splitInnerArgs(userArgs: readonly string[]): {\n outerArgs: string[];\n innerArgs: string[];\n} {\n const dashIdx = userArgs.indexOf('--');\n if (dashIdx === -1) {\n return { outerArgs: [...userArgs], innerArgs: [] };\n }\n return {\n outerArgs: userArgs.slice(0, dashIdx),\n innerArgs: userArgs.slice(dashIdx + 1),\n };\n}\n\nexport function consumeInnerArgsFromProcessArgv(): void {\n // process.argv[0] = node, [1] = script path, [2..] = user args\n const userArgs = process.argv.slice(2);\n const split = splitInnerArgs(userArgs);\n process.argv = [...process.argv.slice(0, 2), ...split.outerArgs];\n innerArgs = split.innerArgs;\n}\n\nexport function getInnerArgs(): readonly string[] {\n return innerArgs;\n}\n\n/** Test seam: lets unit tests set inner args without touching process.argv. */\nexport function setInnerArgsForTesting(args: readonly string[]): void {\n innerArgs = args;\n}\n","import { defineCommand } from 'citty';\nimport { addAptPackagesCommand } from './commands/add-apt-packages.js';\nimport { addFeatureCommand } from './commands/add-feature.js';\nimport { addFromUrlCommand } from './commands/add-from-url.js';\nimport { addRepoCommand } from './commands/add-repo.js';\nimport { addLanguageCommand } from './commands/add-language.js';\nimport { addServiceCommand } from './commands/add-service.js';\nimport { applyCommand } from './commands/apply.js';\nimport { completionCommand } from './commands/completion.js';\nimport { initCommand } from './commands/init.js';\nimport { listComponentsCommand } from './commands/list-components.js';\nimport { logsCommand } from './commands/logs.js';\nimport { removeAptPackagesCommand } from './commands/remove-apt-packages.js';\nimport { removeFeatureCommand } from './commands/remove-feature.js';\nimport { removeCommand } from './commands/remove.js';\nimport { restoreCommand } from './commands/restore.js';\nimport { removeFromUrlCommand } from './commands/remove-from-url.js';\nimport { removeLanguageCommand } from './commands/remove-language.js';\nimport { removeRepoCommand } from './commands/remove-repo.js';\nimport { removeServiceCommand } from './commands/remove-service.js';\nimport { runCommand } from './commands/run.js';\nimport { shellCommand } from './commands/shell.js';\nimport { startCommand } from './commands/start.js';\nimport { statusCommand } from './commands/status.js';\nimport { stopCommand } from './commands/stop.js';\nimport { CLI_VERSION } from './version.js';\n\nexport const main = defineCommand({\n meta: {\n name: 'monoceros',\n version: CLI_VERSION,\n description:\n 'Monoceros workbench — local, sandboxed AI-coding environment for solution builders.',\n },\n subCommands: {\n init: initCommand,\n 'list-components': listComponentsCommand,\n shell: shellCommand,\n run: runCommand,\n logs: logsCommand,\n start: startCommand,\n stop: stopCommand,\n status: statusCommand,\n apply: applyCommand,\n remove: removeCommand,\n restore: restoreCommand,\n 'add-service': addServiceCommand,\n 'add-language': addLanguageCommand,\n 'add-apt-packages': addAptPackagesCommand,\n 'add-feature': addFeatureCommand,\n 'add-from-url': addFromUrlCommand,\n 'add-repo': addRepoCommand,\n 'remove-service': removeServiceCommand,\n 'remove-language': removeLanguageCommand,\n 'remove-apt-packages': removeAptPackagesCommand,\n 'remove-feature': removeFeatureCommand,\n 'remove-from-url': removeFromUrlCommand,\n 'remove-repo': removeRepoCommand,\n completion: completionCommand,\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runAddAptPackages } from '../modify/index.js';\n\nexport const addAptPackagesCommand = defineCommand({\n meta: {\n name: 'add-apt-packages',\n group: 'edit',\n description:\n 'Add Debian/Ubuntu apt packages to the container config. Pass package names after `--` (e.g. `monoceros add-apt-packages sandbox -- make openssh-client jq`). Idempotent. No curated whitelist — invalid names surface as apt errors at container build time.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n const packages = [...getInnerArgs()];\n if (packages.length === 0) {\n consola.error(\n 'No package names given. Usage: `monoceros add-apt-packages <containername> [--yes] -- <pkg> [<pkg> …]`.',\n );\n process.exit(1);\n }\n try {\n const result = await runAddAptPackages({\n name: args.name,\n packages,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport { createPatch } from 'diff';\nimport type { Document } from 'yaml';\nimport { parseConfig, stringifyConfig } from '../config/io.js';\nimport {\n containerConfigPath,\n monocerosHome as defaultMonocerosHome,\n} from '../config/paths.js';\nimport {\n KNOWN_PROVIDER_HOSTS,\n PROVIDER_VALUES,\n REGEX,\n type RepoProvider,\n} from '../config/schema.js';\nimport {\n BUILTIN_LANGUAGES,\n LANGUAGE_CATALOG,\n SERVICE_CATALOG,\n knownLanguages,\n knownServices,\n} from '../create/catalog.js';\nimport { deriveRepoName } from '../create/scaffold.js';\nimport type { FeatureOptions, RepoEntry } from '../create/types.js';\nimport {\n addAptPackagesToDoc,\n addFeatureToDoc,\n addInstallUrlToDoc,\n addLanguageToDoc,\n addRepoToDoc,\n addServiceToDoc,\n removeAptPackagesFromDoc,\n removeFeatureFromDoc,\n removeInstallUrlFromDoc,\n removeLanguageFromDoc,\n removeRepoFromDoc,\n removeServiceFromDoc,\n} from './yml.js';\n\n/**\n * `monoceros add-*` / `monoceros remove-*` — edit the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml` for one container.\n *\n * No cwd magic. The first positional argument is always the container\n * name; the command looks up the yml via convention. Comment-preserving\n * AST mutation; the builder then runs `monoceros apply <name>` to\n * materialize.\n */\n\nexport interface ModifyLogger {\n info: (message: string) => void;\n success: (message: string) => void;\n warn: (message: string) => void;\n}\n\nexport type ConfirmFn = (prompt: string) => Promise<boolean>;\n\nexport interface ModifyOptions {\n /** Container name — resolves to `<home>/container-configs/<name>.yml`. */\n name: string;\n yes?: boolean;\n logger?: ModifyLogger;\n output?: (line: string) => void;\n confirm?: ConfirmFn;\n /** Override the resolved MONOCEROS_HOME. Tests inject a tmpdir. */\n monocerosHome?: string;\n}\n\nexport interface AddLanguageInput extends ModifyOptions {\n language: string;\n}\nexport interface AddServiceInput extends ModifyOptions {\n service: string;\n}\nexport interface AddAptPackagesInput extends ModifyOptions {\n packages: string[];\n}\nexport interface AddFeatureInput extends ModifyOptions {\n ref: string;\n options?: FeatureOptions;\n}\nexport interface AddFromUrlInput extends ModifyOptions {\n url: string;\n}\nexport interface AddRepoInput extends ModifyOptions {\n url: string;\n /**\n * Explicit destination path under `projects/`. Subfolders allowed\n * via `/` (e.g. `apps/web`). When omitted, the URL-derived single-\n * segment default is used (`https://.../foo.git` → `foo` →\n * `projects/foo/`).\n */\n path?: string;\n /**\n * Optional per-repo git committer identity override. Both name and\n * email must be set together; one alone is a usage error. Falls\n * back to the container-level `git.user` (which itself falls back\n * to the host's `git config --global`) when omitted.\n */\n gitName?: string;\n gitEmail?: string;\n /**\n * Git provider hint. Required when the URL host is not one of the\n * three canonical ones (github.com / gitlab.com / bitbucket.org);\n * optional otherwise. Validated against `PROVIDER_VALUES`.\n */\n provider?: string;\n}\n\nexport interface RemoveLanguageInput extends ModifyOptions {\n language: string;\n}\nexport interface RemoveServiceInput extends ModifyOptions {\n service: string;\n}\nexport interface RemoveAptPackagesInput extends ModifyOptions {\n packages: string[];\n}\nexport interface RemoveFeatureInput extends ModifyOptions {\n ref: string;\n}\nexport interface RemoveFromUrlInput extends ModifyOptions {\n url: string;\n}\nexport interface RemoveRepoInput extends ModifyOptions {\n /** url or (effective) name — `monoceros remove-repo` accepts either. */\n target: string;\n}\n\nexport type ModifyResult =\n | { status: 'no-change' }\n | { status: 'updated'; changedPaths: string[] }\n | { status: 'aborted' };\n\ntype YmlMutator = (doc: Document) => boolean;\n\n// ─── add-* ────────────────────────────────────────────────────────\n\nexport function runAddLanguage(input: AddLanguageInput): Promise<ModifyResult> {\n if (\n !BUILTIN_LANGUAGES.has(input.language) &&\n !LANGUAGE_CATALOG[input.language]\n ) {\n throw new Error(\n `Unknown language: ${input.language}. Known: ${knownLanguages().join(', ')}.`,\n );\n }\n return mutate(input, (doc) => addLanguageToDoc(doc, input.language));\n}\n\nexport function runAddService(input: AddServiceInput): Promise<ModifyResult> {\n if (!SERVICE_CATALOG[input.service]) {\n throw new Error(\n `Unknown service: ${input.service}. Known: ${knownServices().join(', ')}.`,\n );\n }\n return mutate(input, (doc) => addServiceToDoc(doc, input.service));\n}\n\nexport function runAddAptPackages(\n input: AddAptPackagesInput,\n): Promise<ModifyResult> {\n if (input.packages.length === 0) {\n throw new Error(\n 'No package names given. Usage: monoceros add-apt-packages <containername> -- <pkg> [<pkg> …].',\n );\n }\n return mutate(input, (doc) => addAptPackagesToDoc(doc, input.packages));\n}\n\nexport async function runAddRepo(input: AddRepoInput): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing repo URL. Usage: monoceros add-repo <containername> <url>.',\n );\n }\n const path = (input.path ?? deriveRepoName(url)).trim();\n // --git-name and --git-email come as a pair. Reject half-set input\n // loudly instead of silently dropping it.\n const hasName =\n typeof input.gitName === 'string' && input.gitName.trim().length > 0;\n const hasEmail =\n typeof input.gitEmail === 'string' && input.gitEmail.trim().length > 0;\n if (hasName !== hasEmail) {\n throw new Error(\n '--git-name and --git-email must be set together. Pass both, or neither.',\n );\n }\n // --provider validation:\n // - host is canonical (github.com / gitlab.com / bitbucket.org):\n // * no --provider → fine, auto-detected at apply time\n // * --provider matches canonical → accepted, written to yml\n // (harmless; round-trip stays clean)\n // * --provider contradicts canonical → reject loudly\n // - host is non-canonical:\n // * --provider given (valid enum) → write it\n // * --provider missing → reject; the apply pre-flight would\n // fail anyway, fail at add-repo time for a better signal\n // * --provider invalid value → reject with allowed list\n const explicitProvider = normalizeProvider(input.provider);\n let host: string | undefined;\n try {\n host = url.startsWith('https://') ? new URL(url).hostname : undefined;\n } catch {\n host = undefined;\n }\n const canonical = host ? KNOWN_PROVIDER_HOSTS[host.toLowerCase()] : undefined;\n if (host && !canonical && !explicitProvider) {\n throw new Error(\n `Host '${host}' is not a canonical Git provider Monoceros can auto-detect (github.com / gitlab.com / bitbucket.org). Pass --provider=github|gitlab|bitbucket so the credential-helper hints know which CLI to suggest.`,\n );\n }\n if (canonical && explicitProvider && explicitProvider !== canonical) {\n throw new Error(\n `--provider=${explicitProvider} contradicts host '${host}' (auto-detected as ${canonical}). Drop --provider for canonical hosts, or fix the value.`,\n );\n }\n // For canonical hosts we don't persist `provider:` in the yml even\n // when the flag was passed (matches what auto-detection would do\n // and keeps the yml minimal). Non-canonical hosts: write the\n // explicit value as-is.\n const providerToWrite =\n !canonical && explicitProvider ? explicitProvider : undefined;\n const entry: RepoEntry = {\n url,\n path,\n ...(hasName && hasEmail\n ? {\n gitUser: {\n name: input.gitName!.trim(),\n email: input.gitEmail!.trim(),\n },\n }\n : {}),\n ...(providerToWrite ? { provider: providerToWrite } : {}),\n };\n return mutate(input, (doc) => addRepoToDoc(doc, entry));\n}\n\nfunction normalizeProvider(raw: string | undefined): RepoProvider | undefined {\n if (typeof raw !== 'string') return undefined;\n const trimmed = raw.trim();\n if (trimmed.length === 0) return undefined;\n const lowered = trimmed.toLowerCase() as RepoProvider;\n if (!(PROVIDER_VALUES as readonly string[]).includes(lowered)) {\n throw new Error(\n `Invalid --provider value: ${JSON.stringify(raw)}. Allowed: ${PROVIDER_VALUES.join(', ')}.`,\n );\n }\n return lowered;\n}\n\nexport function runAddFromUrl(input: AddFromUrlInput): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing URL. Usage: monoceros add-from-url <containername> <url>.',\n );\n }\n return mutate(input, (doc) => addInstallUrlToDoc(doc, url));\n}\n\nexport function runAddFeature(input: AddFeatureInput): Promise<ModifyResult> {\n const ref = input.ref.trim();\n if (ref.length === 0) {\n throw new Error(\n 'Missing feature ref. Usage: monoceros add-feature <containername> <ref>.',\n );\n }\n return mutate(input, (doc) => addFeatureToDoc(doc, ref, input.options ?? {}));\n}\n\n// ─── remove-* ─────────────────────────────────────────────────────\n\nexport function runRemoveLanguage(\n input: RemoveLanguageInput,\n): Promise<ModifyResult> {\n return mutate(input, (doc) => removeLanguageFromDoc(doc, input.language));\n}\n\nexport function runRemoveService(\n input: RemoveServiceInput,\n): Promise<ModifyResult> {\n return mutate(input, (doc) => removeServiceFromDoc(doc, input.service));\n}\n\nexport function runRemoveAptPackages(\n input: RemoveAptPackagesInput,\n): Promise<ModifyResult> {\n if (input.packages.length === 0) {\n throw new Error(\n 'No package names given. Usage: monoceros remove-apt-packages <containername> -- <pkg> [<pkg> …].',\n );\n }\n return mutate(input, (doc) => removeAptPackagesFromDoc(doc, input.packages));\n}\n\nexport function runRemoveFeature(\n input: RemoveFeatureInput,\n): Promise<ModifyResult> {\n const ref = input.ref.trim();\n if (ref.length === 0) {\n throw new Error(\n 'Missing feature ref. Usage: monoceros remove-feature <containername> <ref>.',\n );\n }\n return mutate(input, (doc) => removeFeatureFromDoc(doc, ref));\n}\n\nexport function runRemoveFromUrl(\n input: RemoveFromUrlInput,\n): Promise<ModifyResult> {\n const url = input.url.trim();\n if (url.length === 0) {\n throw new Error(\n 'Missing URL. Usage: monoceros remove-from-url <containername> <url>.',\n );\n }\n return mutate(input, (doc) => removeInstallUrlFromDoc(doc, url));\n}\n\nexport function runRemoveRepo(input: RemoveRepoInput): Promise<ModifyResult> {\n const target = input.target.trim();\n if (target.length === 0) {\n throw new Error(\n 'Missing repo identifier. Usage: monoceros remove-repo <containername> <url-or-name>.',\n );\n }\n return mutate(input, (doc) => removeRepoFromDoc(doc, target));\n}\n\n// ─── core mutate skeleton ─────────────────────────────────────────\n\nasync function mutate(\n opts: ModifyOptions,\n apply: YmlMutator,\n): Promise<ModifyResult> {\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid container name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const ymlPath = containerConfigPath(opts.name, home);\n const logger = opts.logger ?? defaultLogger();\n\n let oldText: string;\n try {\n oldText = await fs.readFile(ymlPath, 'utf8');\n } catch {\n throw new Error(\n `No such config: ${ymlPath}. Run \\`monoceros init <template> ${opts.name}\\` first.`,\n );\n }\n\n const parsed = parseConfig(oldText, ymlPath);\n const changed = apply(parsed.doc);\n\n if (!changed) {\n logger.info('No changes — yml is already in the desired state.');\n return { status: 'no-change' };\n }\n\n // Re-validate via a round-trip so schema violations introduced by\n // the mutation surface here with the regular field-path error, not\n // later at apply time.\n const newText = stringifyConfig(parsed.doc);\n parseConfig(newText, ymlPath);\n\n const out = opts.output ?? ((line) => process.stdout.write(line + '\\n'));\n out(createPatch(ymlPath, oldText, newText, 'before', 'after'));\n\n if (!opts.yes) {\n const confirm = opts.confirm ?? defaultConfirm;\n const ok = await confirm('Apply these changes to the yml?');\n if (!ok) {\n logger.warn('Aborted by user. The yml was not modified.');\n return { status: 'aborted' };\n }\n }\n\n await fs.writeFile(ymlPath, newText, 'utf8');\n logger.success(`Updated ${ymlPath}.`);\n logger.info(\n `Run \\`monoceros apply ${opts.name}\\` to rebuild the dev-container and pick up the change.`,\n );\n return { status: 'updated', changedPaths: [ymlPath] };\n}\n\nfunction defaultLogger(): ModifyLogger {\n return {\n info: (m) => consola.info(m),\n success: (m) => consola.success(m),\n warn: (m) => consola.warn(m),\n };\n}\n\nconst defaultConfirm: ConfirmFn = async (message) => {\n const result = await consola.prompt(message, {\n type: 'confirm',\n initial: false,\n });\n return result === true;\n};\n","import { promises as fs } from 'node:fs';\nimport { Document, parseDocument } from 'yaml';\nimport { type SolutionConfig, validateConfig } from './schema.js';\n\n/**\n * A parsed solution-config yml plus its AST. `config` is the validated\n * plain-JS view (used to drive the apply pipeline); `doc` is the\n * `yaml.Document` (used by mutation helpers so comments and ordering\n * survive a round-trip).\n */\nexport interface ParsedConfig {\n config: SolutionConfig;\n doc: Document.Parsed;\n /** Source path or `<inline>` for an in-memory parse. Used in errors. */\n source: string;\n}\n\n/**\n * Parse a yml string and validate against the schema. Throws on\n * yaml syntax errors and on schema violations. The returned `doc`\n * preserves comments and node ordering — pass it to mutation helpers\n * (`addRepoToDoc`, …) and `stringifyConfig` so the builder's hand-\n * written comments survive `monoceros add-*`.\n */\nexport function parseConfig(\n yamlText: string,\n source = '<inline>',\n): ParsedConfig {\n const doc = parseDocument(yamlText, { prettyErrors: true });\n if (doc.errors.length > 0) {\n const first = doc.errors[0]!;\n throw new Error(`yaml parse error in ${source}: ${first.message}`);\n }\n const config = validateConfig(doc.toJS());\n return { config, doc, source };\n}\n\nexport async function readConfig(filePath: string): Promise<ParsedConfig> {\n const text = await fs.readFile(filePath, 'utf8');\n return parseConfig(text, filePath);\n}\n\n/** Serialize a Document back to yaml. */\nexport function stringifyConfig(doc: Document): string {\n return String(doc);\n}\n\nexport async function writeConfig(\n filePath: string,\n doc: Document,\n): Promise<void> {\n await fs.writeFile(filePath, stringifyConfig(doc), 'utf8');\n}\n\n/**\n * Build a fresh Document from a plain-JS config object. Used when\n * generating a yml from a template (no source comments to preserve)\n * or when migrating an existing stack.json. The resulting Document\n * is suitable for further mutation + `stringifyConfig`.\n *\n * The output is stable: keys appear in the canonical order defined\n * by `KEY_ORDER` below, so two configs with the same content yield\n * byte-identical yaml.\n */\nexport function createDoc(config: SolutionConfig): Document {\n const ordered: Record<string, unknown> = {};\n for (const key of KEY_ORDER) {\n if (key in config) {\n const value = (config as unknown as Record<string, unknown>)[key];\n if (isEmptyContainer(value)) continue;\n ordered[key] = value;\n }\n }\n const doc = new Document(ordered);\n return doc;\n}\n\n/**\n * Canonical key order in generated yaml. Matches the example skeleton\n * in `docs/backlog.md`. Hand-edited yml does not have to follow this\n * order (parser accepts any) — but anything `createDoc` writes does.\n */\nconst KEY_ORDER = [\n 'schemaVersion',\n 'name',\n 'languages',\n 'aptPackages',\n 'features',\n 'installUrls',\n 'services',\n 'repos',\n 'externalServices',\n 'git',\n] as const;\n\nfunction isEmptyContainer(value: unknown): boolean {\n if (Array.isArray(value)) return value.length === 0;\n if (value && typeof value === 'object') {\n return Object.keys(value as Record<string, unknown>).length === 0;\n }\n return false;\n}\n","import { z } from 'zod';\n\n/**\n * Shape validation for a Monoceros solution-config yml. Catalog\n * validation (which languages/services actually exist) happens\n * separately in `apply`, against `create/catalog.ts` — that keeps the\n * schema decoupled from the catalog and lets the schema live without\n * pulling the whole devcontainer scaffold module in.\n *\n * Schema mirrors the StackFile shape from `create/types.ts` except:\n *\n * - `features` is an **array** of `{ ref, options }` entries (yml is\n * edited by humans and arrays diff/comment better than maps).\n * The apply step converts to the Record shape `devcontainer.json`\n * expects.\n *\n * - `externalServices.postgres` carries what `CreateOptions.postgresUrl`\n * does today.\n *\n * - `git.user.{name,email}` carries the host-captured identity so the\n * yml-as-profile is self-contained when shared across containers.\n * Optional — falls back to host-side `git config --global --get` +\n * `.monoceros/gitconfig` at apply time, same as today.\n */\n\nconst SOLUTION_NAME_RE = /^[A-Za-z0-9._-]+$/;\nconst APT_PACKAGE_NAME_RE = /^[a-z0-9][a-z0-9.+-]*$/;\n// Feature refs are OCI-style:\n// <registry>/<namespace>/<feature>:<tag>\n// e.g. ghcr.io/devcontainers/features/python:1\n// ghcr.io/getmonoceros/monoceros-features/claude-code:1\nconst FEATURE_REF_RE = /^[a-z0-9.-]+(\\/[a-z0-9._-]+)+:[a-z0-9._-]+$/;\nconst INSTALL_URL_RE = /^https:\\/\\/[A-Za-z0-9.\\-_~/:?#[\\]@!&'()*+,;=%]+$/;\n// Repo URLs are HTTPS-only by design. SSH-style URLs (git@host:...,\n// ssh://...) are explicitly out of scope — see ADR 0006 for the\n// reasoning. The schema rejects them at parse time with a clear\n// message rather than letting them through and failing opaquely\n// during the clone in post-create.sh.\nconst REPO_URL_RE = /^https:\\/\\/[A-Za-z0-9@:/+_~.#=&?-]+$/;\n// Path under `projects/`. Allows nested subfolders via `/` (e.g.\n// `apps/web`, `monorepo/libs/shared`). The regex enforces:\n// - non-empty\n// - segments use [A-Za-z0-9._-] (same charset as a leaf folder name)\n// - no leading `/`, no trailing `/`, no consecutive `//`\n// A separate refine rejects `.` / `..` segments — those would either\n// be no-ops or escape `projects/`, neither belongs in a checked-in\n// container yml.\nconst REPO_PATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\nconst POSTGRES_URL_RE = /^postgres(ql)?:\\/\\//;\n\nexport const REGEX = {\n solutionName: SOLUTION_NAME_RE,\n aptPackage: APT_PACKAGE_NAME_RE,\n featureRef: FEATURE_REF_RE,\n installUrl: INSTALL_URL_RE,\n repoUrl: REPO_URL_RE,\n repoPath: REPO_PATH_RE,\n postgresUrl: POSTGRES_URL_RE,\n};\n\n/**\n * The providers Monoceros knows how to render setup hints for.\n *\n * Canonical SaaS hostnames (`github.com` / `gitlab.com` /\n * `bitbucket.org`) auto-detect to their provider. Everything else\n * — self-hosted GitLab, GitHub Enterprise, Bitbucket Data Center,\n * Gitea / Forgejo — must declare `provider:` explicitly. Gitea has\n * no canonical SaaS host (gitea.com is a demo, not a SaaS), so any\n * `provider: gitea` entry is by definition self-hosted.\n *\n * Forgejo (the community fork of Gitea) shares Gitea's API, UI, and\n * auth flow — we bundle it under `provider: gitea` rather than\n * carrying a separate enum value.\n */\nexport const PROVIDER_VALUES = [\n 'github',\n 'gitlab',\n 'bitbucket',\n 'gitea',\n] as const;\nexport type RepoProvider = (typeof PROVIDER_VALUES)[number];\n\n/**\n * Hostnames whose provider is implicit — no `provider:` field needed\n * in the yml. Everything else (self-hosted GitLab on `git.firma.de`,\n * Gitea instances, …) requires an explicit declaration; the apply\n * pre-flight enforces that.\n */\nexport const KNOWN_PROVIDER_HOSTS: Readonly<Record<string, RepoProvider>> = {\n 'github.com': 'github',\n 'gitlab.com': 'gitlab',\n 'bitbucket.org': 'bitbucket',\n};\n\n/** Current schema version. Bumped only on breaking yml changes. */\nexport const CONFIG_SCHEMA_VERSION = 1 as const;\n\nexport const FeatureOptionValueSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n]);\n\nexport const FeatureEntrySchema = z.object({\n ref: z\n .string()\n .regex(\n FEATURE_REF_RE,\n \"Invalid feature ref. Expected an OCI-image-style ref like 'ghcr.io/devcontainers/features/<name>:<tag>'.\",\n ),\n options: z.record(z.string(), FeatureOptionValueSchema).optional(),\n});\n\nexport const GitUserSchema = z.object({\n name: z.string().min(1),\n email: z\n .string()\n .min(3)\n .regex(/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/, 'Invalid email'),\n});\n\nexport const RepoEntrySchema = z.object({\n url: z\n .string()\n .regex(\n REPO_URL_RE,\n 'Invalid repo URL. Only HTTPS URLs are supported (https://...). SSH-style URLs (git@host:..., ssh://...) are not in scope — see ADR 0006.',\n ),\n path: z\n .string()\n .regex(\n REPO_PATH_RE,\n \"Invalid repo path. Use letters/digits/'._-', forward slashes for nested folders, no leading or trailing slash.\",\n )\n .refine(\n (p) => !p.split('/').some((seg) => seg === '..' || seg === '.'),\n 'Repo path segments cannot be \".\" or \"..\".',\n )\n .optional(),\n // Per-repo git identity override. Falls back to the container-level\n // `git.user` (which itself falls back to the host's\n // `git config --global` at apply time). Useful when a single\n // container clones multiple repos that need different committer\n // identities — e.g. work GitHub org vs personal projects.\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n // Provider hint for the pre-flight credential check. For the three\n // canonical hosts (github.com / gitlab.com / bitbucket.org) the\n // provider is auto-detected and this field is unnecessary. For any\n // other host (self-hosted GitLab on a custom domain, Gitea, …) the\n // builder MUST declare the provider so apply can suggest the right\n // CLI setup (`glab auth login --hostname <host>` etc.) when\n // credentials are missing. Enforced at apply pre-flight, not at\n // parse time — see ADR 0006.\n provider: z.enum(PROVIDER_VALUES).optional(),\n});\n\nexport const ExternalServicesSchema = z.object({\n postgres: z\n .string()\n .regex(\n POSTGRES_URL_RE,\n \"Postgres URL must start with 'postgres://' or 'postgresql://'\",\n )\n .optional(),\n});\n\nexport const SolutionConfigSchema = z.object({\n schemaVersion: z.literal(CONFIG_SCHEMA_VERSION),\n name: z\n .string()\n .regex(\n SOLUTION_NAME_RE,\n \"Invalid solution name. Use letters, digits, '.', '_' or '-'.\",\n ),\n languages: z.array(z.string().min(1)).default([]),\n aptPackages: z\n .array(\n z\n .string()\n .regex(\n APT_PACKAGE_NAME_RE,\n \"Invalid apt package name. Expected lowercase alphanumeric plus '.+-'.\",\n ),\n )\n .default([]),\n features: z.array(FeatureEntrySchema).default([]),\n installUrls: z\n .array(\n z\n .string()\n .regex(\n INSTALL_URL_RE,\n \"Invalid install URL. Must start with 'https://' and contain only URL-safe characters (no shell metacharacters).\",\n ),\n )\n .default([]),\n services: z.array(z.string().min(1)).default([]),\n repos: z.array(RepoEntrySchema).default([]),\n externalServices: ExternalServicesSchema.default({}),\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n});\n\nexport type SolutionConfig = z.infer<typeof SolutionConfigSchema>;\nexport type FeatureEntry = z.infer<typeof FeatureEntrySchema>;\nexport type RepoEntry = z.infer<typeof RepoEntrySchema>;\nexport type GitUser = z.infer<typeof GitUserSchema>;\nexport type ExternalServices = z.infer<typeof ExternalServicesSchema>;\n\n/**\n * Validate parsed yml (e.g. from `doc.toJS()`) against the schema. On\n * failure, throws an Error whose message lists every issue with its\n * dotted path — the apply step prints that verbatim, so the builder\n * sees exactly which yml field is wrong.\n */\nexport function validateConfig(input: unknown): SolutionConfig {\n const result = SolutionConfigSchema.safeParse(input);\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid solution config:\\n${issues}`);\n }\n return result.data;\n}\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * Path helpers for the M2.5 Phase 3 yml-profile model.\n *\n * Two distinct roots:\n *\n * - `workbenchRoot()` — where the **CLI bundle** lives. In dev that's\n * the monoceros-workbench checkout (so `templates/yml/` is reachable\n * and the workbench can be bind-mounted into generated containers\n * as `/opt/monoceros-workbench`). In prod (post-M4) this is the\n * installed package directory.\n *\n * - `monocerosHome()` — where **user data** lives: container-configs,\n * materialized containers, the global `monoceros-config.yml`.\n *\n * The two used to be conflated under one `workbenchRoot()`; splitting\n * them is what lets `monoceros apply <name>` resolve a fixed\n * `<MONOCEROS_HOME>/container/<name>/` location without any cwd\n * magic, while the CLI itself still knows where its bundled templates\n * live.\n *\n * Layout under `<MONOCEROS_HOME>/`:\n * container-configs/<name>.yml ← yml-Profile (`monoceros init`)\n * container/<name>/ ← materialized dev-containers\n * monoceros-config.yml ← optional, user-edited defaults\n * monoceros-config.sample.yml ← marker (in dev) + template (in prod)\n */\n\nconst MONOCEROS_HOME_MARKER = 'monoceros-config.sample.yml';\nconst WORKBENCH_MARKER = path.join('templates', 'components', 'README.md');\nconst CHECKOUT_MARKER = 'pnpm-workspace.yaml';\n\nlet cachedWorkbenchRoot: string | null = null;\nlet cachedMonocerosHome: string | null = null;\nlet cachedCheckoutRoot: string | null | undefined = undefined;\n\n/**\n * Walk upwards from this module until we find the workbench checkout's\n * marker (`templates/components/README.md`). In dev that hits the\n * workbench root reliably; in production the file does not exist\n * outside the shipped CLI package, so callers that need a workbench\n * root for dev-only purposes (bind-mounting `/opt/monoceros-workbench`)\n * get a clear error.\n */\nexport function workbenchRoot(): string {\n if (cachedWorkbenchRoot) return cachedWorkbenchRoot;\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n if (existsSync(path.join(dir, WORKBENCH_MARKER))) {\n cachedWorkbenchRoot = dir;\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(\n `Could not locate the monoceros workbench checkout (no ${WORKBENCH_MARKER} found by walking up). Run the CLI from a workbench checkout.`,\n );\n }\n dir = parent;\n }\n}\n\n/**\n * Resolve `MONOCEROS_HOME` (where user data lives):\n *\n * 1. Honor the `MONOCEROS_HOME` env-var if set.\n * 2. Walk upwards from this module and accept the first\n * `<dir>/.local/monoceros-config.sample.yml` we find; the\n * containing `<dir>/.local` is treated as the home. This is the\n * dev-workbench detection path.\n * 3. Fall back to `~/.monoceros`.\n *\n * Caches the result for the lifetime of the process — flip `force` to\n * recompute (tests do this between cases).\n */\nexport function monocerosHome(opts: { force?: boolean } = {}): string {\n if (!opts.force && cachedMonocerosHome) return cachedMonocerosHome;\n\n const fromEnv = process.env.MONOCEROS_HOME;\n if (fromEnv && fromEnv.length > 0) {\n cachedMonocerosHome = path.resolve(fromEnv);\n return cachedMonocerosHome;\n }\n\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const candidate = path.join(dir, '.local');\n if (existsSync(path.join(candidate, MONOCEROS_HOME_MARKER))) {\n cachedMonocerosHome = candidate;\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n cachedMonocerosHome = path.join(os.homedir(), '.monoceros');\n return cachedMonocerosHome;\n}\n\n/**\n * Walk upwards from this module to find the workbench checkout root,\n * marked by `pnpm-workspace.yaml`. Distinct from `workbenchRoot()`:\n *\n * - `workbenchRoot()` returns where the **CLI bundle** lives —\n * `packages/cli/` in dev, the installed package directory in prod.\n * - `workbenchCheckoutRoot()` returns where the **full workbench\n * checkout** lives. Only meaningful in dev; returns `null` in\n * prod (the marker doesn't ship with the npm package).\n *\n * Used by features like the dev-only local-source-fallback in\n * `resolveFeatures`, where we want to look at `images/features/<name>/`\n * at the checkout root — not inside the CLI package, where that\n * directory deliberately doesn't exist.\n */\nexport function workbenchCheckoutRoot(): string | null {\n if (cachedCheckoutRoot !== undefined) return cachedCheckoutRoot;\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n if (existsSync(path.join(dir, CHECKOUT_MARKER))) {\n cachedCheckoutRoot = dir;\n return dir;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n cachedCheckoutRoot = null;\n return null;\n }\n dir = parent;\n }\n}\n\n/** Reset cached lookups. Test-only. */\nexport function _resetPathCachesForTests(): void {\n cachedWorkbenchRoot = null;\n cachedMonocerosHome = null;\n cachedCheckoutRoot = undefined;\n}\n\n// ─── CLI-bundle paths (templates) ─────────────────────────────────\n\n/**\n * `templates/components/` — the components catalog used by\n * `monoceros init`. Each file under this directory is a small yml\n * snippet describing one composable component (a language, a\n * service, or a feature). See `templates/components/README.md`.\n */\nexport function componentsDir(root: string = workbenchRoot()): string {\n return path.join(root, 'templates', 'components');\n}\n\n/**\n * `features/` (inside the CLI bundle) — npm-shipped copies of the\n * Monoceros feature manifests (`devcontainer-feature.json`). Built\n * by `pnpm manifests:sync` from `images/features/<name>/` and\n * included in the published tarball via the `files` field. The\n * init generator's hint loader looks here as the production\n * fallback when the workbench checkout isn't available.\n */\nexport function bundledFeaturesDir(root: string = workbenchRoot()): string {\n return path.join(root, 'features');\n}\n\n// ─── User-home paths (configs, containers, global config) ────────\n\nexport function containerConfigsDir(home: string = monocerosHome()): string {\n return path.join(home, 'container-configs');\n}\n\nexport function containerConfigPath(\n name: string,\n home: string = monocerosHome(),\n): string {\n return path.join(containerConfigsDir(home), `${name}.yml`);\n}\n\nexport function containersDir(home: string = monocerosHome()): string {\n return path.join(home, 'container');\n}\n\nexport function containerDir(\n name: string,\n home: string = monocerosHome(),\n): string {\n return path.join(containersDir(home), name);\n}\n\nexport function monocerosConfigPath(home: string = monocerosHome()): string {\n return path.join(home, 'monoceros-config.yml');\n}\n\n// ─── User-facing path formatting ─────────────────────────────────\n\n/**\n * Format an absolute path for printing in CLI output: collapses a\n * `$HOME` prefix to `~` so messages stay short without losing\n * information. Non-home paths pass through verbatim.\n *\n * prettyPath('/Users/x/.monoceros/container-configs/hello.yml')\n * → '~/.monoceros/container-configs/hello.yml'\n *\n * Use this whenever a log line tells the user where something\n * landed on disk — `monoceros init`, `apply`, `remove`, `restore`\n * all rely on it so users see one consistent format.\n */\nexport function prettyPath(p: string): string {\n const home = os.homedir();\n if (!home) return p;\n if (p === home) return '~';\n const prefix = home.endsWith(path.sep) ? home : home + path.sep;\n if (p.startsWith(prefix)) {\n return '~' + path.sep + p.slice(prefix.length);\n }\n return p;\n}\n","// Catalogs of supported language toolchains and backing services for\n// the yml profile. Curated whitelists keep the surface small and\n// reviewable; unknown values are rejected up front rather than passed\n// through to devcontainer / compose.\n\n// Monoceros runtime image — thin layer on top of Microsoft's\n// typescript-node base (see images/runtime/Dockerfile). The default\n// points at the floating major tag on GHCR, so an `apply` from a\n// fresh install pulls a published image without further setup.\n//\n// Contributors who are iterating on the runtime image itself\n// (`pnpm image:build` → `monoceros-runtime:dev`) can override this\n// via the `MONOCEROS_BASE_IMAGE_OVERRIDE` env var to point at their\n// local tag without editing source. Empty or whitespace-only values\n// are ignored so an accidentally-set-blank var doesn't break apply.\nconst DEFAULT_BASE_IMAGE = 'ghcr.io/getmonoceros/monoceros-runtime:1';\nconst override = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();\nexport const BASE_IMAGE =\n override && override.length > 0 ? override : DEFAULT_BASE_IMAGE;\n\nexport interface LanguageEntry {\n id: string;\n feature: string;\n}\n\n// `node` is included in the base runtime image, so the bare entry\n// `languages: [node]` is accepted as input but installs nothing\n// extra. Versioned node — `node:20` — bypasses the builtin set and\n// goes through the upstream feature like the other languages,\n// because the base image's node version (22) isn't selectable\n// otherwise.\nexport const BUILTIN_LANGUAGES = new Set(['node']);\n\nexport const LANGUAGE_CATALOG: Readonly<Record<string, LanguageEntry>> = {\n node: { id: 'node', feature: 'ghcr.io/devcontainers/features/node:1' },\n python: { id: 'python', feature: 'ghcr.io/devcontainers/features/python:1' },\n java: { id: 'java', feature: 'ghcr.io/devcontainers/features/java:1' },\n go: { id: 'go', feature: 'ghcr.io/devcontainers/features/go:1' },\n rust: { id: 'rust', feature: 'ghcr.io/devcontainers/features/rust:1' },\n dotnet: { id: 'dotnet', feature: 'ghcr.io/devcontainers/features/dotnet:2' },\n};\n\n/**\n * Language entries in a container yml may carry an optional\n * version suffix: `java:17`, `node:20`. The suffix is anything\n * the upstream devcontainer feature accepts as its `version`\n * option (typically `latest`, a major like `17`, or an exact\n * semver like `3.12.1`).\n */\nexport const LANGUAGE_SPEC_RE = /^([a-z][a-z0-9-]*)(?::([A-Za-z0-9._-]+))?$/;\n\nexport interface LanguageSpec {\n name: string;\n version?: string;\n}\n\n/**\n * Split a yml language entry into name + optional version. Returns\n * `null` when the input is not a valid language spec. Callers use\n * that null to surface a schema error.\n */\nexport function parseLanguageSpec(spec: string): LanguageSpec | null {\n const m = LANGUAGE_SPEC_RE.exec(spec);\n if (!m) return null;\n return { name: m[1]!, ...(m[2] !== undefined ? { version: m[2] } : {}) };\n}\n\nexport interface ServiceEntry {\n id: string;\n image: string;\n env?: Readonly<Record<string, string>>;\n /**\n * Container-side mount target for the service's persistent data.\n * Monoceros bind-mounts this onto `<container-dir>/data/<id>/` on\n * the host so DB content is visible in the host filesystem\n * (browsable, backupable, removable with the usual tools instead\n * of `docker volume ...`). See ADR 0003 for the per-container\n * state-model the data dir slots into.\n */\n dataMount?: string;\n}\n\n// The literal `monoceros` user/password/db on the service entries\n// below is a deliberate dev-only convention, not a secret. The\n// services are only reachable from inside the workspace container\n// (no host port mapping), and the value is hardcoded into the\n// catalog + docs so any builder running this workbench knows the\n// connection string at a glance:\n//\n// postgresql://monoceros:monoceros@postgres:5432/monoceros\n// mysql://monoceros:monoceros@mysql:3306/monoceros\n//\n// Because it isn't a secret, the secret-masking layer\n// (util/mask-secrets.ts) doesn't and shouldn't mask it. Builders\n// who want a real password should either:\n// - run their own DB outside the workbench and configure it via\n// `externalServices.postgres: postgresql://…` in the container\n// yml, OR\n// - swap to a per-container generated password — open issue when\n// this becomes a real need.\nexport const SERVICE_CATALOG: Readonly<Record<string, ServiceEntry>> = {\n postgres: {\n id: 'postgres',\n image: 'postgres:18',\n env: {\n POSTGRES_USER: 'monoceros',\n POSTGRES_PASSWORD: 'monoceros',\n POSTGRES_DB: 'monoceros',\n },\n // Postgres 18+ stores data under /var/lib/postgresql/<major>/, so\n // the recommended mount is the parent directory; pre-18 used\n // /var/lib/postgresql/data directly. See\n // https://github.com/docker-library/postgres/pull/1259.\n dataMount: '/var/lib/postgresql',\n },\n mysql: {\n id: 'mysql',\n image: 'mysql:8',\n env: {\n MYSQL_ROOT_PASSWORD: 'monoceros',\n MYSQL_DATABASE: 'monoceros',\n },\n dataMount: '/var/lib/mysql',\n },\n redis: {\n id: 'redis',\n image: 'redis:8',\n dataMount: '/data',\n },\n};\n\nexport function knownLanguages(): string[] {\n return [...BUILTIN_LANGUAGES, ...Object.keys(LANGUAGE_CATALOG)].sort();\n}\n\nexport function knownServices(): string[] {\n return Object.keys(SERVICE_CATALOG).sort();\n}\n","import { existsSync, readFileSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { workbenchCheckoutRoot } from '../config/paths.js';\nimport { matchMonocerosFeature } from '../util/ref.js';\nimport {\n BASE_IMAGE,\n BUILTIN_LANGUAGES,\n LANGUAGE_CATALOG,\n SERVICE_CATALOG,\n knownLanguages,\n knownServices,\n parseLanguageSpec,\n} from './catalog.js';\nimport type { CreateOptions } from './types.js';\n\n// Debian/Ubuntu apt package name rules: start with alphanumeric, then\n// alphanumerics + `.+-` are allowed. We intentionally don't allow shell\n// metacharacters (`;`, `&`, `|`, `$`, `(`, …) so a typo can't smuggle\n// arbitrary shell into the apt-packages feature config.\nconst APT_PACKAGE_NAME_RE = /^[a-z0-9][a-z0-9.+-]*$/;\n\n// Devcontainer feature refs are OCI-style:\n// <registry>/<namespace>/<feature>:<tag>\n// e.g. ghcr.io/devcontainers/features/python:1\n// ghcr.io/getmonoceros/monoceros-features/claude-code:1\nconst FEATURE_REF_RE = /^[a-z0-9.-]+(\\/[a-z0-9._-]+)+:[a-z0-9._-]+$/;\n\n// Install URLs must be https:// (no plain http, no other schemes) and\n// contain only URL-safe characters. We deliberately reject shell\n// metacharacters even inside a query string — the URL is embedded into\n// a generated bash script, and a stray `$` or backtick would be a\n// shell-injection vector.\nconst INSTALL_URL_RE = /^https:\\/\\/[A-Za-z0-9.\\-_~/:?#[\\]@!&'()*+,;=%]+$/;\n\n// Git URLs: covers HTTPS, SSH (`git@host:path/repo.git`), and\n// `ssh://`/`git://` schemes. Permissive but no shell metacharacters.\nconst REPO_URL_RE = /^[A-Za-z0-9@:/+_~.#=&?-]+$/;\n\n// Repo destination = path under `projects/`. Allows nested subfolders\n// (`apps/web`) via `/`; segments use `[A-Za-z0-9._-]` (same charset as\n// a leaf folder name). `.` / `..` segments are rejected separately\n// because the regex alone allows pure-dot segments.\nconst REPO_PATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\n\n/**\n * Derive a repo name from its URL.\n *\n * `git@github.com:foo/bar.git` → `bar`\n * `https://github.com/foo/bar.git` → `bar`\n * `https://github.com/foo/bar` → `bar`\n * `ssh://git@host:22/foo/bar.git` → `bar`\n */\nexport function deriveRepoName(url: string): string {\n const lastSep = Math.max(url.lastIndexOf('/'), url.lastIndexOf(':'));\n const tail = url.slice(lastSep + 1);\n return tail.replace(/\\.git$/, '');\n}\n\nexport function validateOptions(opts: CreateOptions): void {\n if (!opts.name || !/^[a-zA-Z0-9._-]+$/.test(opts.name)) {\n throw new Error(\n `Invalid solution name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n for (const langSpec of opts.languages) {\n const parsed = parseLanguageSpec(langSpec);\n if (!parsed) {\n throw new Error(\n `Invalid language spec: ${JSON.stringify(langSpec)}. Expected '<name>' or '<name>:<version>'.`,\n );\n }\n if (!BUILTIN_LANGUAGES.has(parsed.name) && !LANGUAGE_CATALOG[parsed.name]) {\n throw new Error(\n `Unknown language: ${parsed.name}. Known: ${knownLanguages().join(', ')}.`,\n );\n }\n }\n for (const svc of opts.services) {\n if (!SERVICE_CATALOG[svc]) {\n throw new Error(\n `Unknown service: ${svc}. Known: ${knownServices().join(', ')}.`,\n );\n }\n }\n for (const pkg of opts.aptPackages ?? []) {\n if (!APT_PACKAGE_NAME_RE.test(pkg)) {\n throw new Error(\n `Invalid apt package name: ${JSON.stringify(pkg)}. Expected lowercase alphanumeric plus '.+-'.`,\n );\n }\n }\n for (const ref of Object.keys(opts.features ?? {})) {\n if (!FEATURE_REF_RE.test(ref)) {\n throw new Error(\n `Invalid devcontainer feature ref: ${JSON.stringify(ref)}. Expected OCI-image-style ref like 'ghcr.io/devcontainers/features/<name>:<tag>'.`,\n );\n }\n }\n for (const url of opts.installUrls ?? []) {\n if (!INSTALL_URL_RE.test(url)) {\n throw new Error(\n `Invalid install URL: ${JSON.stringify(url)}. Must start with 'https://' and contain only URL-safe characters (no shell metacharacters).`,\n );\n }\n }\n const seenRepoPaths = new Set<string>();\n for (const repo of opts.repos ?? []) {\n if (!REPO_URL_RE.test(repo.url)) {\n throw new Error(\n `Invalid repo URL: ${JSON.stringify(repo.url)}. Use HTTPS or SSH/git@ form; no shell metacharacters.`,\n );\n }\n if (!REPO_PATH_RE.test(repo.path)) {\n throw new Error(\n `Invalid repo path: ${JSON.stringify(repo.path)}. Use letters/digits/'._-', forward slashes for nested folders, no leading or trailing slash.`,\n );\n }\n if (repo.path.split('/').some((seg) => seg === '..' || seg === '.')) {\n throw new Error(\n `Invalid repo path: ${JSON.stringify(repo.path)}. Path segments cannot be \".\" or \"..\".`,\n );\n }\n if (seenRepoPaths.has(repo.path)) {\n throw new Error(\n `Duplicate repo path: ${JSON.stringify(repo.path)}. Each projects/<path> folder must be unique — pass --path to disambiguate.`,\n );\n }\n seenRepoPaths.add(repo.path);\n }\n}\n\n// Normalize: dedupe + sort + drop postgres from compose services when an\n// external --postgres-url is provided.\nexport function normalizeOptions(opts: CreateOptions): CreateOptions {\n const languages = [...new Set(opts.languages)].sort();\n let services = [...new Set(opts.services)].sort();\n if (opts.postgresUrl) {\n services = services.filter((s) => s !== 'postgres');\n }\n const aptPackages = [...new Set(opts.aptPackages ?? [])].sort();\n // Sort feature refs alphabetically so devcontainer.json + stack.json\n // output is deterministic regardless of insertion order.\n const features = opts.features\n ? Object.fromEntries(\n Object.entries(opts.features).sort(([a], [b]) => a.localeCompare(b)),\n )\n : undefined;\n // Install URLs preserve insertion order (installs may depend on each\n // other), but we deduplicate to keep stack.json stable across re-adds.\n const installUrls = opts.installUrls\n ? [...new Set(opts.installUrls)]\n : undefined;\n // Repos: preserve insertion order, dedupe by (url, name, branch)\n // signature — same triple twice is a no-op, different triples\n // coexist. (Same name with different URL is a validation error\n // in validateOptions, not silently merged here.)\n const repos = opts.repos\n ? Array.from(\n new Map(opts.repos.map((r) => [`${r.url}\u0001${r.path}`, r])).values(),\n )\n : undefined;\n return {\n name: opts.name,\n languages,\n services,\n postgresUrl: opts.postgresUrl,\n ...(aptPackages.length > 0 ? { aptPackages } : {}),\n ...(features && Object.keys(features).length > 0 ? { features } : {}),\n ...(installUrls && installUrls.length > 0 ? { installUrls } : {}),\n ...(repos && repos.length > 0 ? { repos } : {}),\n };\n}\n\nexport function needsCompose(opts: CreateOptions): boolean {\n return opts.services.length > 0;\n}\n\ninterface DevcontainerImageMode {\n name: string;\n image: string;\n remoteUser: string;\n // Scaffold-level mounts: only the SSH-agent forward for git auth when\n // the yml lists repos. Tool-specific mounts (e.g. ~/.claude for the\n // claude-code feature) come from the feature's own manifest, not from\n // here.\n mounts?: string[];\n // Override of the workspace bind-mount. Set only when the host\n // runs rootless Docker — we append `idmap` so the kernel applies\n // the user-namespace mapping to the mount, which makes files\n // written by either side appear with sane UIDs on the other.\n // Without this, host-pre-created `projects/` appears as root in\n // the container and the non-root `node` user can't write into it.\n workspaceMount?: string;\n // Required so the runtime image's entrypoint can install iptables\n // rules if MONOCEROS_EGRESS=enforce is set. Default mode is `off`\n // (see ADR 0002) so the cap is harmless when unused.\n runArgs: string[];\n forwardPorts: number[];\n postCreateCommand: string;\n features?: Record<string, Record<string, unknown>>;\n // Env vars injected into the workspace container at start time\n // (inherited by postCreateCommand). Used by add-repo to wire the\n // forwarded SSH-agent socket and a permissive SSH host-key policy.\n containerEnv?: Record<string, string>;\n}\n\ninterface DevcontainerComposeMode {\n name: string;\n dockerComposeFile: string;\n service: string;\n // Without runServices, `devcontainer up` only brings up the named service.\n // Listing the auxiliary services here ensures postgres/redis/… come up\n // alongside the workspace container.\n runServices?: string[];\n workspaceFolder: string;\n remoteUser: string;\n forwardPorts: number[];\n postCreateCommand: string;\n features?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * The host docker daemon's mode — passed in by `apply` after a\n * `docker info` probe. Drives whether we emit `idmap` on bind\n * mounts. See `devcontainer/docker-mode.ts` for the rationale.\n */\nexport type DockerMode = 'rootful' | 'rootless';\n\n// Repo auth note: Monoceros supports HTTPS-only repo URLs (see ADR\n// 0006). The host's git credential helper provides the username/token\n// per host (osxkeychain on macOS, libsecret on Linux, wincred on\n// Windows, plus `gh auth setup-git` for GitHub specifically), the\n// apply pipeline writes them to <container-dir>/.monoceros/git-\n// credentials, and post-create.sh wires `git config --global\n// credential.helper \"store --file=…\"` so the container reads from\n// the same file. SSH-agent forwarding, multi-key wiring, host-OS\n// platform-specific socket paths — all that complexity stays out.\n\nexport type DevcontainerJson = DevcontainerImageMode | DevcontainerComposeMode;\n\n/**\n * Per-feature plan for the container build.\n *\n * - `devcontainerKey` — the key used in `devcontainer.json → features`.\n * - `localSourceDir` / `localName` — set when the workbench has the\n * feature on disk. `writeScaffold` copies the directory into\n * `<container>/.devcontainer/features/<name>/` and uses the\n * relative path `./features/<name>` in devcontainer.json.\n * (devcontainer-cli accepts relative paths from `.devcontainer/`\n * but rejects absolute filesystem paths to local features.)\n */\ninterface ResolvedFeature {\n devcontainerKey: string;\n options: Record<string, unknown>;\n localSourceDir?: string;\n localName?: string;\n /**\n * Subdirectories of `/home/node/` that this feature wants to\n * persist across container rebuilds. Each entry is bind-mounted\n * from `<container-dir>/home/<path>` into `/home/node/<path>` and\n * pre-created as an empty directory on the host. Read from the\n * feature manifest's `x-monoceros.persistentHomePaths` array.\n */\n persistentHomePaths: string[];\n /**\n * Like `persistentHomePaths`, but for individual **files** rather\n * than directories. Necessary for tools that keep state in a\n * dotfile next to (not inside) their config directory — e.g.\n * Claude Code's `~/.claude.json` lives alongside `~/.claude/`.\n *\n * Each entry can be a bare path string (file pre-created empty)\n * or `{ path, initialContent }` so a feature author can seed\n * valid initial content. The latter is needed for tools that\n * refuse to parse an empty file: Claude Code, for instance, errors\n * on an empty `.claude.json` and demands at least `{}`. Read from\n * the feature manifest's `x-monoceros.persistentHomeFiles` array.\n */\n persistentHomeFiles: PersistentHomeFile[];\n}\n\ninterface PersistentHomeFile {\n path: string;\n initialContent: string;\n}\n\n/**\n * Compute the feature list for `opts`. Detects Monoceros-owned refs\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`) and, if\n * the workbench has the feature on disk, rewrites the key to\n * `./features/<name>` and records the source for the copy step.\n *\n * Third-party refs and Monoceros refs without a local source pass\n * through verbatim — devcontainer-cli pulls them from the registry.\n */\nexport function resolveFeatures(opts: CreateOptions): ResolvedFeature[] {\n const resolved: ResolvedFeature[] = [];\n\n for (const langSpec of opts.languages) {\n const parsed = parseLanguageSpec(langSpec);\n if (!parsed) continue;\n // Builtin only applies to the bare `node` (no version) — the\n // base image's node 22 isn't pinnable, so any `node:<version>`\n // has to go through the upstream feature like everything else.\n if (BUILTIN_LANGUAGES.has(parsed.name) && parsed.version === undefined) {\n continue;\n }\n const entry = LANGUAGE_CATALOG[parsed.name];\n if (!entry) continue;\n const options: Record<string, string> = {};\n if (parsed.version !== undefined) options.version = parsed.version;\n resolved.push({\n devcontainerKey: entry.feature,\n options,\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n if (opts.aptPackages && opts.aptPackages.length > 0) {\n resolved.push({\n devcontainerKey: 'ghcr.io/devcontainers-contrib/features/apt-packages:1',\n options: { packages: opts.aptPackages.join(',') },\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n if (opts.features) {\n for (const [rawRef, options] of Object.entries(opts.features)) {\n const match = matchMonocerosFeature(rawRef);\n if (match) {\n const name = match.name;\n // Dev-only fallback: when the CLI is run from a workbench\n // checkout, prefer the on-disk copy under `images/features/`\n // so feature edits are testable without a publish. In prod\n // (npm-installed), `workbenchCheckoutRoot()` returns null\n // and we fall through to the GHCR-ref passthrough.\n const checkout = workbenchCheckoutRoot();\n const localSourceDir = checkout\n ? path.join(checkout, 'images', 'features', name)\n : null;\n if (localSourceDir && existsSync(localSourceDir)) {\n const { paths, files } = readPersistentHomeEntries(localSourceDir);\n resolved.push({\n devcontainerKey: `./features/${name}`,\n options,\n localSourceDir,\n localName: name,\n persistentHomePaths: paths,\n persistentHomeFiles: files,\n });\n continue;\n }\n }\n resolved.push({\n devcontainerKey: rawRef,\n options,\n persistentHomePaths: [],\n persistentHomeFiles: [],\n });\n }\n }\n return resolved;\n}\n\n/**\n * Read `x-monoceros.persistentHomePaths` and\n * `x-monoceros.persistentHomeFiles` from a feature's manifest on\n * disk. Returns `{paths: [], files: []}` when the manifest doesn't\n * exist, can't be parsed, or the fields are missing — always\n * best-effort, never throws. Both arrays are validated to contain\n * only safe relative subpaths (no `..`, no absolute, no shell\n * metacharacters); anything else is silently dropped, since a bad\n * value here is a feature-author bug, not something a builder can fix.\n */\nfunction readPersistentHomeEntries(localSourceDir: string): {\n paths: string[];\n files: PersistentHomeFile[];\n} {\n const manifestPath = path.join(localSourceDir, 'devcontainer-feature.json');\n try {\n const text = readFileSync(manifestPath, 'utf8');\n const parsed = JSON.parse(text) as {\n 'x-monoceros'?: {\n persistentHomePaths?: unknown;\n persistentHomeFiles?: unknown;\n };\n };\n return {\n paths: filterSubpaths(parsed['x-monoceros']?.persistentHomePaths),\n files: filterFileEntries(parsed['x-monoceros']?.persistentHomeFiles),\n };\n } catch {\n return { paths: [], files: [] };\n }\n}\n\nfunction filterSubpaths(raw: unknown): string[] {\n if (!Array.isArray(raw)) return [];\n return raw.filter(\n (p): p is string =>\n typeof p === 'string' &&\n p.length > 0 &&\n !p.startsWith('/') &&\n !p.includes('..') &&\n HOME_SUBPATH_RE.test(p),\n );\n}\n\n/**\n * Accept either bare strings or `{path, initialContent}` objects in\n * `persistentHomeFiles`. Bare string is shorthand for \"create an\n * empty file\"; the object form lets feature authors provide initial\n * content (e.g. `{}` for a JSON config that the tool refuses to\n * parse when empty).\n */\nfunction filterFileEntries(raw: unknown): PersistentHomeFile[] {\n if (!Array.isArray(raw)) return [];\n const result: PersistentHomeFile[] = [];\n for (const entry of raw) {\n if (typeof entry === 'string') {\n if (isValidHomeSubpath(entry)) {\n result.push({ path: entry, initialContent: '' });\n }\n continue;\n }\n if (\n entry !== null &&\n typeof entry === 'object' &&\n 'path' in entry &&\n typeof (entry as { path: unknown }).path === 'string'\n ) {\n const e = entry as { path: string; initialContent?: unknown };\n if (!isValidHomeSubpath(e.path)) continue;\n const initialContent =\n typeof e.initialContent === 'string' ? e.initialContent : '';\n result.push({ path: e.path, initialContent });\n }\n }\n return result;\n}\n\nfunction isValidHomeSubpath(p: string): boolean {\n return (\n p.length > 0 &&\n !p.startsWith('/') &&\n !p.includes('..') &&\n HOME_SUBPATH_RE.test(p)\n );\n}\n\n// Home subpaths: dot-prefixed dirs and config-like sub-dirs.\n// Restrictive on purpose — only `.foo`, `.foo/bar`, `foo`, `foo/bar`,\n// no whitespace, no shell metacharacters.\nconst HOME_SUBPATH_RE = /^[A-Za-z0-9._-]+(\\/[A-Za-z0-9._-]+)*$/;\n\nexport function buildDevcontainerJson(\n opts: CreateOptions,\n dockerMode: DockerMode = 'rootful',\n): DevcontainerJson {\n const resolvedFeatures = resolveFeatures(opts);\n const features: Record<string, Record<string, unknown>> = {};\n for (const f of resolvedFeatures) {\n features[f.devcontainerKey] = f.options;\n }\n\n const featuresField =\n Object.keys(features).length > 0 ? { features } : undefined;\n\n // `idmap=true` is the bind-mount option that asks the Linux kernel\n // to apply the user-namespace mapping to the mount. Required on\n // rootless Docker so that host-pre-created files (`projects/`,\n // `home/`, `.monoceros/`) are writable by the container's `node`\n // user and container-written files land on the host with the\n // host user's UID instead of a shifted /etc/subuid id. On rootful\n // Docker and on Mac/Windows Docker Desktop, idmap is either a\n // no-op (extra round-trip) or an outright mount error — so we\n // ONLY emit it when the host probe confirmed rootless.\n //\n // Docker 25+ accepts the option in `key=value` form\n // (`idmap=true`). Older docker versions that briefly accepted the\n // bare `idmap` flag are out of scope — Ubuntu 24.04 ships docker\n // 27.x via get.docker.com, which strictly requires the key=value\n // syntax (we hit this in M5 testing 2026-05-23).\n const idmapSuffix = dockerMode === 'rootless' ? ',idmap=true' : '';\n\n // Bind-mounts for per-feature persistent home entries. Source on\n // the host is `<container-dir>/home/<subpath>` (under the\n // localWorkspaceFolder); target inside the container is the same\n // subpath under `/home/node/`. Files and directories both go through\n // the same `type=bind` syntax — docker decides from the source's\n // on-disk type. We pre-create both kinds in writeScaffold so the\n // owner matches the host user (otherwise docker auto-creates as\n // root on Linux, breaking writes inside the container) and so a\n // requested **file** bind doesn't get spawned as a directory.\n const homeMounts: string[] = [];\n for (const f of resolvedFeatures) {\n const allSubs = [\n ...f.persistentHomePaths,\n ...f.persistentHomeFiles.map((entry) => entry.path),\n ];\n for (const sub of allSubs) {\n homeMounts.push(\n `source=\\${localWorkspaceFolder}/home/${sub},target=/home/node/${sub},type=bind${idmapSuffix}`,\n );\n }\n }\n\n // No scaffold-level VS Code customizations today — extension hints\n // belong with the feature that needs them (e.g. the claude-code\n // feature itself recommends `anthropic.claude-code`).\n\n if (needsCompose(opts)) {\n // Compose-mode: per-feature persistent home mounts go onto the\n // workspace service in compose.yaml (see buildComposeYaml). The\n // devcontainer.json just references compose.\n return {\n name: opts.name,\n dockerComposeFile: 'compose.yaml',\n service: 'workspace',\n ...(opts.services.length > 0 ? { runServices: opts.services } : {}),\n workspaceFolder: `/workspaces/${opts.name}`,\n remoteUser: 'node',\n forwardPorts: [3000, 4000],\n postCreateCommand: '.devcontainer/post-create.sh',\n ...(featuresField ?? {}),\n };\n }\n\n // Image-mode mounts: per-feature persistent-home binds.\n const mounts: string[] = [...homeMounts];\n const mountsField = mounts.length > 0 ? { mounts } : {};\n\n // On rootless we also override the workspace bind-mount so the\n // main /workspaces/<name> mount gets idmap. Without this override,\n // devcontainer-cli generates the workspace mount itself without\n // idmap, and the post-create.sh that runs inside hits permission-\n // denied on host-pre-created `projects/`.\n const workspaceMountField =\n dockerMode === 'rootless'\n ? {\n workspaceMount: `source=\\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind${idmapSuffix}`,\n }\n : {};\n\n return {\n name: opts.name,\n image: BASE_IMAGE,\n remoteUser: 'node',\n ...workspaceMountField,\n ...mountsField,\n runArgs: ['--cap-add=NET_ADMIN'],\n forwardPorts: [3000, 4000],\n postCreateCommand: '.devcontainer/post-create.sh',\n ...(featuresField ?? {}),\n };\n}\n\n// Hand-rolled YAML for compose.yaml. The shape is narrow enough that\n// avoiding a YAML dependency outweighs the cost of careful indentation.\n//\n// `dockerMode === 'rootless'` switches every workspace-side bind to\n// the long-syntax with `bind: { create_host_path: true }` plus the\n// `idmap` flag — same fix as in image mode (see buildDevcontainerJson),\n// just expressed in compose's volume long-form. Compose spec exposes\n// idmap as of compose-spec 1.16 (docker compose v2.30+, Ubuntu 24.04\n// has compatible versions). On rootful we keep the short syntax — it\n// stays readable and avoids any compose-version friction.\nexport function buildComposeYaml(\n opts: CreateOptions,\n dockerMode: DockerMode = 'rootful',\n): string {\n const lines: string[] = ['services:'];\n const isRootless = dockerMode === 'rootless';\n\n lines.push(' workspace:');\n lines.push(` image: ${BASE_IMAGE}`);\n lines.push(\" command: 'sleep infinity'\");\n // No `user:` directive here — the runtime image's entrypoint runs as\n // root to set up iptables, then drops to the `node` user via gosu\n // before exec'ing the command. NET_ADMIN is required for that\n // iptables setup; see ADR 0002.\n lines.push(' cap_add:');\n lines.push(' - NET_ADMIN');\n lines.push(' volumes:');\n if (isRootless) {\n // Long syntax + idmap so the kernel applies the user-ns mapping\n // to the workspace bind. Without it, the container's `node` can't\n // write into host-pre-created `projects/`.\n lines.push(' - type: bind');\n lines.push(' source: ..');\n lines.push(` target: /workspaces/${opts.name}`);\n lines.push(' bind:');\n lines.push(' create_host_path: true');\n lines.push(' idmap: true');\n } else {\n lines.push(` - ..:/workspaces/${opts.name}:cached`);\n }\n // Per-feature persistent home subpaths (dirs and files alike).\n // Paths inside compose.yaml are relative to the .devcontainer/\n // directory; `..` walks up to the container root, where `home/`\n // lives. Docker reads the host-side inode type to decide whether\n // the mount target inside the container is a file or a directory.\n const resolvedFeatures = resolveFeatures(opts);\n for (const f of resolvedFeatures) {\n const allSubs = [\n ...f.persistentHomePaths,\n ...f.persistentHomeFiles.map((entry) => entry.path),\n ];\n for (const sub of allSubs) {\n if (isRootless) {\n lines.push(' - type: bind');\n lines.push(` source: ../home/${sub}`);\n lines.push(` target: /home/node/${sub}`);\n lines.push(' bind:');\n lines.push(' create_host_path: true');\n lines.push(' idmap: true');\n } else {\n lines.push(` - ../home/${sub}:/home/node/${sub}`);\n }\n }\n }\n for (const svcId of opts.services) {\n const def = SERVICE_CATALOG[svcId];\n if (!def) continue;\n lines.push(` ${def.id}:`);\n lines.push(` image: ${def.image}`);\n if (def.env) {\n lines.push(' environment:');\n for (const [k, v] of Object.entries(def.env)) {\n lines.push(` ${k}: ${v}`);\n }\n }\n if (def.dataMount) {\n // Per-service data dir bind-mounted from the host so DB content\n // is visible at `<container-dir>/data/<svc>/`. See ADR 0003 for\n // the per-container state-model this slots into. Pre-created in\n // writeScaffold so docker doesn't auto-mkdir as root.\n lines.push(' volumes:');\n lines.push(` - ../data/${def.id}:${def.dataMount}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\ninterface CodeWorkspaceFolder {\n path: string;\n name?: string;\n}\n\ninterface CodeWorkspaceFile {\n folders: CodeWorkspaceFolder[];\n}\n\n/**\n * The `<name>.code-workspace` file VS Code uses to open the solution as\n * a multi-root workspace. The first entry is `.` so the workspace root\n * (with its system dotfolders) stays visible in the Explorer. Each\n * repo added via `monoceros add-repo` appears as a sibling root\n * pointing at `projects/<name>/`.\n */\nexport function buildCodeWorkspaceJson(opts: CreateOptions): CodeWorkspaceFile {\n const folders: CodeWorkspaceFolder[] = [{ path: '.' }];\n // Sort repos by path so the Explorer order is deterministic and\n // doesn't depend on insertion order. (Clone order in post-create\n // stays as-added so deps still work.)\n const sortedRepos = [...(opts.repos ?? [])].sort((a, b) =>\n a.path.localeCompare(b.path),\n );\n for (const repo of sortedRepos) {\n // The folder's display label is the leaf segment of the path\n // (the deepest folder name). VS Code shows it in the Explorer\n // tree; for nested clones (`apps/web`) we want `web`, not the\n // whole path.\n const label = repo.path.split('/').pop() ?? repo.path;\n folders.push({ path: `projects/${repo.path}`, name: label });\n }\n return { folders };\n}\n\n/**\n * Generate the `post-create.sh` content for a solution. Base sections\n * (git include + pnpm install) are fixed. The `installUrls` and\n * `repos` sections are appended only when those yml fields are\n * populated.\n */\nexport function buildPostCreateScript(opts: CreateOptions): string {\n const lines: string[] = [\n '#!/usr/bin/env bash',\n 'set -euo pipefail',\n '',\n '# Inherit host-side git identity (user.name / user.email) captured',\n '# into .monoceros/gitconfig by `monoceros apply`. Container-local',\n \"# git config loads first; the include below merges the host's\",\n '# identity values in.',\n `git config --global include.path \"/workspaces/${opts.name}/.monoceros/gitconfig\"`,\n '',\n '# Per-feature post-create hooks. Each Monoceros-curated feature',\n '# may drop a script into /usr/local/share/monoceros/post-create.d/',\n '# during its install.sh — typical job is a non-interactive login',\n '# against bind-mounted state under /home/node, using the option',\n '# values the feature received as env vars at install time. Scripts',\n '# run in lexicographic order, each in its own subshell, and a',\n '# failure aborts post-create (set -e is in effect).',\n 'if [ -d /usr/local/share/monoceros/post-create.d ]; then',\n ' for hook in /usr/local/share/monoceros/post-create.d/*.sh; do',\n ' [ -f \"$hook\" ] || continue',\n ' echo \"→ post-create hook: $(basename \"$hook\")\"',\n ' bash \"$hook\"',\n ' done',\n 'fi',\n '',\n '# Bring up Node dependencies if the workspace has a package.json.',\n 'if [ -f package.json ]; then',\n ' pnpm install',\n 'fi',\n ];\n\n if (opts.installUrls && opts.installUrls.length > 0) {\n lines.push(\n '',\n '# Custom install URLs added via `monoceros add-from-url`. Each is',\n '# fetched and piped to `sh` on every container rebuild. URLs run',\n '# in insertion order so later installs can build on earlier ones.',\n '#',\n '# Why `sh` (not `bash`): most install scripts target POSIX `sh`',\n '# and some (starship, rustup, …) explicitly refuse to run under',\n '# `bash`. Outer `set -o pipefail` in this script makes a curl',\n '# failure abort the post-create as expected.',\n `echo \"→ Running ${opts.installUrls.length} install URL(s) added via add-from-url…\"`,\n );\n for (const url of opts.installUrls) {\n lines.push(`echo \"→ ${url}\"`, `curl -fsSL \"${url}\" | sh`);\n }\n }\n\n if (opts.repos && opts.repos.length > 0) {\n const hasHttpsRepo = opts.repos.some((r) => r.url.startsWith('https://'));\n if (hasHttpsRepo) {\n lines.push(\n '',\n '# Wire git to the per-dev-container credentials file populated',\n '# by `monoceros apply` (via `git credential fill` on the host).',\n '# Path uses the workspace bind-mount so the file is reachable',\n '# from inside the container.',\n `git config --global credential.helper \"store --file=/workspaces/${opts.name}/.monoceros/git-credentials\"`,\n );\n }\n lines.push(\n '',\n '# Repos managed by `monoceros add-repo`. Each entry is cloned',\n '# into `projects/<path>/` if (and only if) the directory does',\n '# not exist yet. Existing project subfolders are left alone so',\n '# local changes survive `monoceros apply` rebuilds. Nested',\n '# `<path>` (e.g. apps/web) is created via `mkdir -p` before the',\n '# clone so the parent directories exist.',\n 'mkdir -p projects',\n );\n for (const repo of opts.repos) {\n // For nested paths (`apps/web`), make sure the parent dir\n // exists before git clone — otherwise git fails with \"could\n // not create work tree dir\".\n const parent = repo.path.includes('/')\n ? repo.path.slice(0, repo.path.lastIndexOf('/'))\n : null;\n if (parent) {\n lines.push(`mkdir -p \"projects/${parent}\"`);\n }\n lines.push(\n `if [ ! -d \"projects/${repo.path}\" ]; then`,\n ` echo \"→ Cloning ${repo.path} from ${repo.url}…\"`,\n ` git clone \"${repo.url}\" \"projects/${repo.path}\"`,\n `else`,\n ` echo \"→ projects/${repo.path} already exists, skipping clone\"`,\n `fi`,\n );\n // Per-repo git identity override: set user.name/email inside\n // the cloned repo, so commits from THIS repo go out under the\n // override identity. Idempotent — git config overwrites the\n // value each run, no duplicate accumulation. Falls outside the\n // `if [ ! -d ... ]` clone-guard so an explicit yml update of\n // gitUser also takes effect on re-apply against an existing\n // clone.\n if (repo.gitUser) {\n const safeName = repo.gitUser.name.replace(/\"/g, '\\\\\"');\n const safeEmail = repo.gitUser.email.replace(/\"/g, '\\\\\"');\n lines.push(\n `git -C \"projects/${repo.path}\" config user.name \"${safeName}\"`,\n `git -C \"projects/${repo.path}\" config user.email \"${safeEmail}\"`,\n );\n }\n }\n }\n\n return lines.join('\\n') + '\\n';\n}\n\nexport async function writePostCreateScript(\n devcontainerDir: string,\n opts: CreateOptions,\n): Promise<void> {\n const dest = path.join(devcontainerDir, 'post-create.sh');\n await fs.writeFile(dest, buildPostCreateScript(opts));\n await fs.chmod(dest, 0o755);\n}\n\n/**\n * Materialize the full devcontainer scaffold for `opts` into\n * `targetDir`. Idempotent overwrite — re-running with different opts\n * produces the new scaffold and overwrites any older files.\n *\n * Writes:\n * - `.devcontainer/devcontainer.json`\n * - `.devcontainer/post-create.sh`\n * - `.devcontainer/compose.yaml` (only when services are configured)\n * - `.monoceros/.gitignore`\n * - `projects/.gitkeep`\n * - `<name>.code-workspace`\n *\n * Does NOT write `README.md` — the README is a once-only stub that\n * `runCreate` produces but `runApplyFromYml` should leave alone (the\n * builder may have edited it).\n *\n * Caller is responsible for `validateOptions(opts)` and\n * `normalizeOptions(opts)`; this function trusts the input.\n */\nexport async function writeScaffold(\n opts: CreateOptions,\n targetDir: string,\n scaffoldOpts: { dockerMode?: DockerMode } = {},\n): Promise<void> {\n const dockerMode: DockerMode = scaffoldOpts.dockerMode ?? 'rootful';\n const devcontainerDir = path.join(targetDir, '.devcontainer');\n const monocerosDir = path.join(targetDir, '.monoceros');\n const projectsDir = path.join(targetDir, 'projects');\n const homeDir = path.join(targetDir, 'home');\n const dataDir = path.join(targetDir, 'data');\n await fs.mkdir(devcontainerDir, { recursive: true });\n await fs.mkdir(monocerosDir, { recursive: true });\n await fs.mkdir(projectsDir, { recursive: true });\n await fs.mkdir(homeDir, { recursive: true });\n if (needsCompose(opts)) {\n await fs.mkdir(dataDir, { recursive: true });\n // Pre-create one subdir per service so docker bind-mounts onto\n // an existing host path (and doesn't auto-mkdir as root, which\n // breaks postgres/mysql first-run on Linux).\n for (const svcId of opts.services) {\n const def = SERVICE_CATALOG[svcId];\n if (def?.dataMount) {\n await fs.mkdir(path.join(dataDir, def.id), { recursive: true });\n }\n }\n }\n\n // Container-root `.gitignore`. Excludes the directories that hold\n // builder-private or container-runtime state:\n // - `home/` — logins, sessions, secrets baked into tool\n // config files\n // - `.monoceros/` — git-credentials captured from the host\n // credential helper, machine-local gitconfig\n // - `data/` — DB data the compose services write at\n // runtime (postgres/mysql/redis), often big,\n // always container-specific\n // Inside `projects/<repo>/` builders have their own `.git` and\n // any wrapping git operation should be at that level, not at the\n // container root — but a stray `git init` at the root is exactly\n // the accident this .gitignore protects against.\n const containerGitignore = path.join(targetDir, '.gitignore');\n await fs.writeFile(containerGitignore, '/home/\\n/.monoceros/\\n/data/\\n');\n\n // `.gitkeep` so `projects/` survives a fresh git clone before any\n // sub-project has been added.\n const gitkeep = path.join(projectsDir, '.gitkeep');\n if (!existsSync(gitkeep)) {\n await fs.writeFile(gitkeep, '');\n }\n\n // `.monoceros/.gitignore` keeps per-builder runtime state out of any\n // wrapping git repo. Always overwrite — content is fixed.\n await fs.writeFile(\n path.join(monocerosDir, '.gitignore'),\n 'git-credentials*\\ngitconfig\\n',\n );\n\n const devcontainerJson = buildDevcontainerJson(opts, dockerMode);\n await fs.writeFile(\n path.join(devcontainerDir, 'devcontainer.json'),\n JSON.stringify(devcontainerJson, null, 2) + '\\n',\n );\n\n // Copy any Monoceros-owned features that the workbench has on disk\n // into `<devcontainerDir>/features/<name>/`. The devcontainer.json\n // references them via the relative path `./features/<name>` — the\n // devcontainer-cli accepts relative paths from the `.devcontainer/`\n // directory but rejects absolute filesystem paths to local features.\n //\n // We always rebuild the whole `features/` directory: drop the old\n // copy and recreate from current sources, so a feature that was\n // removed from the yml doesn't linger as stale on-disk content that\n // devcontainer-cli would still see.\n const featuresDir = path.join(devcontainerDir, 'features');\n if (existsSync(featuresDir)) {\n await fs.rm(featuresDir, { recursive: true, force: true });\n }\n const resolvedFeatures = resolveFeatures(opts);\n for (const f of resolvedFeatures) {\n if (!f.localSourceDir || !f.localName) continue;\n const dest = path.join(featuresDir, f.localName);\n await fs.mkdir(dest, { recursive: true });\n await fs.cp(f.localSourceDir, dest, { recursive: true });\n }\n\n // Pre-create persistent home entries so docker doesn't auto-mkdir\n // them as root at container start. We only ensure existence; any\n // existing content survives, which is the whole point — apply\n // never touches `home/<sub>` once it's there. Directories get\n // mkdir; files get an empty touch (only when missing — already-\n // populated files like a complete .claude.json must not be\n // truncated on re-apply).\n for (const f of resolvedFeatures) {\n for (const sub of f.persistentHomePaths) {\n await fs.mkdir(path.join(homeDir, sub), { recursive: true });\n }\n for (const entry of f.persistentHomeFiles) {\n const filePath = path.join(homeDir, entry.path);\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n if (!existsSync(filePath)) {\n // Seed with the feature-author's initial content (defaults\n // to empty). For JSON configs this should be at least `{}`\n // so the tool doesn't choke on an unparseable empty file.\n await fs.writeFile(filePath, entry.initialContent);\n }\n }\n }\n\n await writePostCreateScript(devcontainerDir, opts);\n\n const composePath = path.join(devcontainerDir, 'compose.yaml');\n if (needsCompose(opts)) {\n await fs.writeFile(composePath, buildComposeYaml(opts, dockerMode));\n } else if (existsSync(composePath)) {\n // Services dropped from the yml — clean up the now-stale file so a\n // later `monoceros start` doesn't pick it up.\n await fs.rm(composePath);\n }\n\n await fs.writeFile(\n path.join(targetDir, `${opts.name}.code-workspace`),\n JSON.stringify(buildCodeWorkspaceJson(opts), null, 2) + '\\n',\n );\n}\n","/**\n * Shape detection for OCI refs that point at Monoceros-owned\n * devcontainer features. Two regexes live here:\n *\n * - `MONOCEROS_FEATURE_RE` matches the current ref shape\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`),\n * which is what `monoceros init` writes and what GHCR serves.\n *\n * - `DEPRECATED_MONOCEROS_FEATURE_RE` matches the legacy shape\n * (`ghcr.io/monoceros/features/<name>:<tag>`) that was used\n * before the M4 cut. We keep this only to emit a migration\n * warning at `apply` time when a builder's yml still carries\n * the old ref.\n */\n\nconst FEATURE_NAME_CHARSET = '[a-z0-9._-]+';\nconst FEATURE_TAG_CHARSET = '[a-z0-9._-]+';\n\nexport const MONOCEROS_FEATURE_RE = new RegExp(\n `^ghcr\\\\.io/getmonoceros/monoceros-features/(${FEATURE_NAME_CHARSET}):${FEATURE_TAG_CHARSET}$`,\n);\n\nexport const DEPRECATED_MONOCEROS_FEATURE_RE = new RegExp(\n `^ghcr\\\\.io/monoceros/features/(${FEATURE_NAME_CHARSET}):(${FEATURE_TAG_CHARSET})$`,\n);\n\n/**\n * Extract `{ name }` from a current-shape Monoceros feature ref,\n * or `null` for anything else (third-party features, deprecated\n * shape, malformed input).\n */\nexport function matchMonocerosFeature(ref: string): { name: string } | null {\n const match = MONOCEROS_FEATURE_RE.exec(ref);\n if (!match) return null;\n return { name: match[1]! };\n}\n\n/**\n * Translate a legacy-shape ref into the current-shape ref it would\n * map to (`ghcr.io/monoceros/features/<name>:<tag>` →\n * `ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`). Returns\n * `null` for refs that don't match the legacy shape, so callers can\n * use it as a \"should I warn?\" check.\n */\nexport function migrateDeprecatedFeatureRef(ref: string): string | null {\n const match = DEPRECATED_MONOCEROS_FEATURE_RE.exec(ref);\n if (!match) return null;\n const name = match[1]!;\n const tag = match[2]!;\n return `ghcr.io/getmonoceros/monoceros-features/${name}:${tag}`;\n}\n","import { type Document, isMap, isScalar, isSeq, YAMLMap, YAMLSeq } from 'yaml';\nimport type { FeatureOptions, RepoEntry } from '../create/types.js';\nimport { deriveRepoName } from '../create/scaffold.js';\n\n/**\n * AST-level mutators for solution-config yml. Each function:\n * - takes a yaml.Document obtained from `parseConfig`\n * - mutates it in place if the operation introduces a change\n * - returns `true` iff the doc was changed, `false` for a no-op\n * (matches the `mutate()` skeleton's \"no-change\" branch)\n *\n * All mutators preserve every comment and blank line in the surrounding\n * yml — that's the whole point of the AST approach over a plain\n * `toJS → edit → toString` cycle.\n *\n * Validation is NOT done here. The caller is expected to re-validate\n * the doc (via `parseConfig(stringifyConfig(doc))`) before persisting\n * — that surfaces schema violations with the regular field-path-aware\n * error message.\n */\n\n/** Ensure `doc[key]` is a sequence and return it. */\nfunction ensureSeq(doc: Document, key: string): YAMLSeq {\n const existing = doc.get(key, true);\n if (existing && isSeq(existing)) return existing;\n const seq = new YAMLSeq();\n doc.set(key, seq);\n return seq;\n}\n\n/** Drop `doc[key]` when its sequence is empty. */\nfunction pruneEmptySeq(doc: Document, key: string): void {\n const node = doc.get(key, true);\n if (node && isSeq(node) && node.items.length === 0) {\n doc.delete(key);\n }\n}\n\n/** Compare a scalar item's value (handles both Scalar nodes and plain JS). */\nfunction scalarValue(item: unknown): unknown {\n return isScalar(item) ? item.value : item;\n}\n\nexport function addLanguageToDoc(doc: Document, lang: string): boolean {\n const seq = ensureSeq(doc, 'languages');\n if (seq.items.some((i) => scalarValue(i) === lang)) return false;\n seq.add(lang);\n return true;\n}\n\nexport function addServiceToDoc(doc: Document, service: string): boolean {\n const seq = ensureSeq(doc, 'services');\n if (seq.items.some((i) => scalarValue(i) === service)) return false;\n seq.add(service);\n return true;\n}\n\nexport function addAptPackagesToDoc(\n doc: Document,\n packages: string[],\n): boolean {\n const seq = ensureSeq(doc, 'aptPackages');\n let changed = false;\n for (const pkg of packages) {\n if (seq.items.some((i) => scalarValue(i) === pkg)) continue;\n seq.add(pkg);\n changed = true;\n }\n return changed;\n}\n\nexport function addInstallUrlToDoc(doc: Document, url: string): boolean {\n const seq = ensureSeq(doc, 'installUrls');\n if (seq.items.some((i) => scalarValue(i) === url)) return false;\n seq.add(url);\n return true;\n}\n\n/**\n * Add (or no-op) a devcontainer feature entry. Mirrors the legacy\n * `add-feature` semantics: re-adding the same ref with different\n * options is an explicit error (the builder must remove + re-add to\n * change options); same ref + same options is a no-op.\n */\nexport function addFeatureToDoc(\n doc: Document,\n ref: string,\n options: FeatureOptions = {},\n): boolean {\n const seq = ensureSeq(doc, 'features');\n for (const item of seq.items) {\n if (!isMap(item)) continue;\n const itemRef = item.get('ref');\n if (itemRef !== ref) continue;\n // Same ref: check options equality. Use the live doc's toJS so the\n // sub-map's scalars resolve to plain values; passing doc as the\n // schema/context is required by yaml@2.\n const itemJs = item.toJS(doc) as { options?: FeatureOptions };\n const existingJs = itemJs.options ?? {};\n if (JSON.stringify(existingJs) === JSON.stringify(options)) {\n return false;\n }\n throw new Error(\n `Feature ${ref} is already configured with different options. Remove it first (\\`monoceros remove-feature ${ref}\\`) before re-adding.`,\n );\n }\n const entry = new YAMLMap();\n entry.set('ref', ref);\n if (Object.keys(options).length > 0) {\n entry.set('options', options);\n }\n seq.add(entry);\n return true;\n}\n\n/**\n * Add (or no-op) a repo entry to the `repos:` sequence.\n *\n * Idempotency: if an existing entry has the same URL AND the same\n * effective path AND the same gitUser, this is a no-op (returns\n * false). \"Effective path\" means the explicit `path:` value if set,\n * or the URL-derived single-segment default otherwise. Same URL\n * with a different path is intentionally allowed — that's the \"I\n * want two clones of the same repo into different folders\" case.\n *\n * `gitUser` is an optional per-repo override of the container-level\n * git.user. When set, persisted as a `git.user` nested map; falls\n * back to the container default at apply time when omitted.\n *\n * Branches are not part of this model. Switching branches is a\n * `git checkout` inside the container, not a yml-level concern.\n */\nexport function addRepoToDoc(doc: Document, repo: RepoEntry): boolean {\n const seq = ensureSeq(doc, 'repos');\n for (const item of seq.items) {\n if (!isMap(item)) continue;\n const url = item.get('url');\n if (url !== repo.url) continue;\n const existingPath = item.get('path');\n const effectivePath =\n typeof existingPath === 'string'\n ? existingPath\n : deriveRepoName(url as string);\n if (effectivePath !== repo.path) continue;\n // Same url + same path. Check gitUser + provider equivalence too\n // so an entry that adds/changes either field is treated as an\n // update, not silently ignored.\n const existingGit = item.get('git', true);\n const existingUser =\n existingGit && isMap(existingGit) ? existingGit.get('user', true) : null;\n const existingName =\n existingUser && isMap(existingUser) ? existingUser.get('name') : null;\n const existingEmail =\n existingUser && isMap(existingUser) ? existingUser.get('email') : null;\n const existingGitUser =\n typeof existingName === 'string' && typeof existingEmail === 'string'\n ? { name: existingName, email: existingEmail }\n : undefined;\n const sameGitUser =\n (existingGitUser?.name ?? null) === (repo.gitUser?.name ?? null) &&\n (existingGitUser?.email ?? null) === (repo.gitUser?.email ?? null);\n const existingProvider = item.get('provider');\n const sameProvider =\n (typeof existingProvider === 'string' ? existingProvider : null) ===\n (repo.provider ?? null);\n if (sameGitUser && sameProvider) {\n return false;\n }\n // Different gitUser or provider → update in place instead of\n // appending a duplicate. Re-running add-repo with new values is\n // the natural way to change either field.\n if (repo.gitUser) {\n const gitMap = new YAMLMap();\n const userMap = new YAMLMap();\n userMap.set('name', repo.gitUser.name);\n userMap.set('email', repo.gitUser.email);\n gitMap.set('user', userMap);\n item.set('git', gitMap);\n } else {\n item.delete('git');\n }\n if (repo.provider) {\n item.set('provider', repo.provider);\n } else {\n item.delete('provider');\n }\n return true;\n }\n const entry = new YAMLMap();\n entry.set('url', repo.url);\n // Only persist `path` when it differs from the URL-derived default.\n // Keeps the yml minimal — the apply pipeline re-derives at runtime.\n if (repo.path !== deriveRepoName(repo.url)) {\n entry.set('path', repo.path);\n }\n if (repo.gitUser) {\n const gitMap = new YAMLMap();\n const userMap = new YAMLMap();\n userMap.set('name', repo.gitUser.name);\n userMap.set('email', repo.gitUser.email);\n gitMap.set('user', userMap);\n entry.set('git', gitMap);\n }\n if (repo.provider) {\n entry.set('provider', repo.provider);\n }\n seq.add(entry);\n return true;\n}\n\n/**\n * Remove helpers — symmetric to add, used by Task 6's `remove-*`\n * commands. Returning false when the target isn't present makes them\n * idempotent (caller logs \"no-change\" instead of erroring).\n */\nexport function removeLanguageFromDoc(doc: Document, lang: string): boolean {\n return removeScalarFromSeq(doc, 'languages', lang);\n}\n\nexport function removeServiceFromDoc(doc: Document, service: string): boolean {\n return removeScalarFromSeq(doc, 'services', service);\n}\n\nexport function removeAptPackageFromDoc(doc: Document, pkg: string): boolean {\n return removeScalarFromSeq(doc, 'aptPackages', pkg);\n}\n\n/** Plural form — for `monoceros remove-apt-packages a b c`. */\nexport function removeAptPackagesFromDoc(\n doc: Document,\n packages: string[],\n): boolean {\n let changed = false;\n for (const pkg of packages) {\n if (removeAptPackageFromDoc(doc, pkg)) changed = true;\n }\n return changed;\n}\n\nexport function removeInstallUrlFromDoc(doc: Document, url: string): boolean {\n return removeScalarFromSeq(doc, 'installUrls', url);\n}\n\nexport function removeFeatureFromDoc(doc: Document, ref: string): boolean {\n const seq = doc.get('features', true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((i) => isMap(i) && i.get('ref') === ref);\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, 'features');\n return true;\n}\n\n/**\n * Remove a repo by either its url or its (effective) path. Symmetry\n * to add-repo: `monoceros remove-repo <url-or-path>` matches either\n * field. For nested paths the full path is the match key\n * (`remove-repo apps/web`), not the leaf segment.\n */\nexport function removeRepoFromDoc(doc: Document, urlOrPath: string): boolean {\n const seq = doc.get('repos', true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((item) => {\n if (!isMap(item)) return false;\n const url = item.get('url');\n if (url === urlOrPath) return true;\n const path = item.get('path');\n const effectivePath =\n typeof path === 'string'\n ? path\n : typeof url === 'string'\n ? deriveRepoName(url)\n : undefined;\n return effectivePath === urlOrPath;\n });\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, 'repos');\n return true;\n}\n\nfunction removeScalarFromSeq(\n doc: Document,\n key: string,\n value: string,\n): boolean {\n const seq = doc.get(key, true);\n if (!seq || !isSeq(seq)) return false;\n const idx = seq.items.findIndex((i) => scalarValue(i) === value);\n if (idx < 0) return false;\n seq.items.splice(idx, 1);\n pruneEmptySeq(doc, key);\n return true;\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport type { FeatureOptions } from '../create/types.js';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runAddFeature } from '../modify/index.js';\n\nexport const addFeatureCommand = defineCommand({\n meta: {\n name: 'add-feature',\n group: 'edit',\n description:\n 'Add a devcontainer feature by ref to the container config. Options follow `--` as `key=value` pairs. Idempotent (same ref + same options is a no-op). Adding the same ref with different options is an error.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n ref: {\n type: 'positional',\n description:\n 'Devcontainer feature ref (OCI image style, e.g. `ghcr.io/devcontainers/features/docker-in-docker:2`).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n let options: FeatureOptions;\n try {\n options = parseOptionsAfterDashes(getInnerArgs());\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n try {\n const result = await runAddFeature({\n name: args.name,\n ref: args.ref,\n options,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\n/**\n * Parse `key=value` tokens (one per arg) into a feature options hash.\n * Coerces `true`/`false` to booleans and pure-integer strings to\n * numbers; everything else stays a string.\n */\nfunction parseOptionsAfterDashes(tokens: readonly string[]): FeatureOptions {\n const result: FeatureOptions = {};\n for (const token of tokens) {\n const eqIdx = token.indexOf('=');\n if (eqIdx <= 0) {\n throw new Error(\n `Invalid option: ${JSON.stringify(token)}. Expected key=value (e.g. version=latest).`,\n );\n }\n const key = token.slice(0, eqIdx);\n const raw = token.slice(eqIdx + 1);\n result[key] = coerce(raw);\n }\n return result;\n}\n\nfunction coerce(value: string): string | number | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (/^-?\\d+$/.test(value)) {\n const n = Number(value);\n if (Number.isSafeInteger(n)) return n;\n }\n return value;\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddFromUrl } from '../modify/index.js';\n\nexport const addFromUrlCommand = defineCommand({\n meta: {\n name: 'add-from-url',\n group: 'edit',\n description:\n 'Add an https:// install URL to the container config. The URL gets piped to sh on every container rebuild. Loudly warns about remote-code execution before persisting. Idempotent.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description:\n 'https:// URL of an install script (e.g. https://starship.rs/install.sh).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description:\n 'Skip the security warning + diff confirm. Use only in scripts where you have already audited the URL.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n if (!args.yes) {\n printSecurityWarning(args.url);\n }\n try {\n const result = await runAddFromUrl({\n name: args.name,\n url: args.url,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\nfunction printSecurityWarning(url: string): void {\n const w = (line: string) => process.stderr.write(line + '\\n');\n w('');\n w('⚠️ SECURITY WARNING — `monoceros add-from-url`');\n w('');\n w(` URL: ${url}`);\n w('');\n w(' This URL will be fetched and piped to sh on every container rebuild.');\n w(\n ' Remote-code execution against a URL you do not control is a supply-chain',\n );\n w(\n ' risk: the maintainer could change the script tomorrow and your container',\n );\n w(' would silently run the new payload.');\n w('');\n w(' Before confirming below:');\n w(' 1. Open the URL in a browser, read what the script does.');\n w(\n ' 2. Verify the maintainer is who you think they are (HTTPS cert, repo).',\n );\n w(' 3. Ideally, vendor the install steps as `add-apt-packages` or');\n w(\n ' `add-feature` instead — those reference signed/versioned artifacts.',\n );\n w('');\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddRepo } from '../modify/index.js';\n\nexport const addRepoCommand = defineCommand({\n meta: {\n name: 'add-repo',\n group: 'edit',\n description:\n 'Add a git repo to the container config. Cloned into projects/<path>/ on container build. Idempotent — existing project subfolders are left alone. Destination path derived from URL by default; override with --path (supports nested subfolders like apps/web). Branches/PRs are git-level concerns: clone, then `git checkout` inside the container.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description:\n 'Git URL (HTTPS or SSH/git@ form). E.g. https://github.com/foo/bar.git, git@github.com:foo/bar.git.',\n required: true,\n },\n path: {\n type: 'string',\n description:\n 'Destination under projects/. Subfolders via `/` (e.g. apps/web). Default: URL-derived single segment (bar.git → bar).',\n },\n 'git-name': {\n type: 'string',\n description:\n 'Per-repo git committer name. Overrides the container-level git.user.name for this repo only. Pair with --git-email.',\n },\n 'git-email': {\n type: 'string',\n description:\n 'Per-repo git committer email. Overrides the container-level git.user.email for this repo only. Pair with --git-name.',\n },\n provider: {\n type: 'string',\n description:\n 'Git provider for credential-helper guidance: github | gitlab | bitbucket. Required when the URL host is not github.com, gitlab.com, or bitbucket.org — Monoceros uses this to suggest the right CLI (gh / glab / Atlassian token) on missing credentials.',\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddRepo({\n name: args.name,\n url: args.url,\n ...(typeof args.path === 'string' ? { path: args.path } : {}),\n ...(typeof args['git-name'] === 'string'\n ? { gitName: args['git-name'] }\n : {}),\n ...(typeof args['git-email'] === 'string'\n ? { gitEmail: args['git-email'] }\n : {}),\n ...(typeof args.provider === 'string'\n ? { provider: args.provider }\n : {}),\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddLanguage } from '../modify/index.js';\n\nexport const addLanguageCommand = defineCommand({\n meta: {\n name: 'add-language',\n group: 'edit',\n description:\n 'Add a language toolchain (devcontainer feature) to the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n language: {\n type: 'positional',\n description:\n 'Language identifier from the feature whitelist (e.g. python, java, rust).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddLanguage({\n name: args.name,\n language: args.language,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runAddService } from '../modify/index.js';\n\nexport const addServiceCommand = defineCommand({\n meta: {\n name: 'add-service',\n group: 'edit',\n description:\n 'Add a compose service (postgres, mysql, redis, …) to the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'positional',\n description: 'Service identifier (postgres, mysql, redis).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runAddService({\n name: args.name,\n service: args.service,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { runApply } from '../apply/index.js';\nimport { CLI_VERSION } from '../version.js';\nimport { dispatch } from './_dispatch.js';\n\n/**\n * `monoceros apply <name>` — materialize the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml` into\n * `<MONOCEROS_HOME>/container/<name>/` and bring the container up.\n *\n * The target location is fixed by convention. cwd is irrelevant. No\n * `--path` override — one config maps to exactly one container\n * directory, and that's the whole mental model.\n */\nexport const applyCommand = defineCommand({\n meta: {\n name: 'apply',\n group: 'lifecycle',\n description:\n 'Materialize a container config into $MONOCEROS_HOME/container/<name>/ and bring the dev-container up. Close any VS Code Remote Containers session for the target first — the extension auto-recreates and races with apply.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.',\n required: true,\n },\n },\n run({ args }) {\n return dispatch(async () => {\n const result = await runApply({\n name: args.name,\n cliVersion: CLI_VERSION,\n });\n return result.containerExitCode;\n });\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport { type MonocerosConfig, readMonocerosConfig } from '../config/global.js';\nimport { readConfig } from '../config/io.js';\nimport {\n containerConfigPath,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\nimport { REGEX } from '../config/schema.js';\nimport {\n buildStateFile,\n readStateFile,\n writeStateFile,\n} from '../config/state.js';\nimport type { SolutionConfig } from '../config/schema.js';\nimport { solutionConfigToCreateOptions } from '../config/transform.js';\nimport {\n needsCompose,\n normalizeOptions,\n validateOptions,\n writeScaffold,\n} from '../create/scaffold.js';\nimport { cyan, dim, sectionLine } from '../util/format.js';\nimport { migrateDeprecatedFeatureRef } from '../util/ref.js';\nimport {\n type ComposeSpawn,\n runContainerCycle,\n} from '../devcontainer/compose.js';\nimport {\n type CredentialsSpawn,\n collectGitCredentials,\n uniqueHttpsHosts,\n formatMissingCredentialsError,\n formatUnknownProviderError,\n} from '../devcontainer/credentials.js';\nimport {\n type ReachabilitySpawn,\n checkRepoReachability,\n formatUnreachableReposError,\n} from '../devcontainer/repo-reachability.js';\nimport {\n type DockerInfoSpawn,\n detectDockerMode,\n} from '../devcontainer/docker-mode.js';\nimport { type DevcontainerSpawn } from '../devcontainer/cli.js';\nimport {\n collectGitIdentity,\n type IdentityPrompt,\n type IdentitySpawn,\n} from '../devcontainer/identity.js';\n\n/**\n * `monoceros apply <name>` — read the yml at\n * `<MONOCEROS_HOME>/container-configs/<name>.yml`, materialize the\n * devcontainer scaffold at `<MONOCEROS_HOME>/container/<name>/`, write\n * `.monoceros/state.json` with `origin: <name>`, then teardown + bring\n * the container up.\n *\n * The target location is determined by convention, not by cwd or an\n * explicit path argument. That's deliberate: a config is the source of\n * truth, the container directory mirrors it 1:1, and the builder never\n * has to remember \"which directory was sandbox materialized into\".\n *\n * Idempotent: re-running picks up the current yml, overwrites scaffold\n * files, restarts the container.\n *\n * Refuses to materialize into a non-empty directory whose state.json\n * points at a different origin — protects against accidental clobber\n * if a builder somehow seeded `<MONOCEROS_HOME>/container/<name>/`\n * outside of this command.\n */\n\nexport interface RunApplyOptions {\n /** Config name — resolves to `<home>/container-configs/<name>.yml`. */\n name: string;\n cliVersion: string;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n now?: Date;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n warn?: (msg: string) => void;\n /**\n * Print a structural section marker (`▸ Configuration` etc).\n * Optional — tests typically pass a silent logger without one,\n * in which case section markers are no-ops.\n */\n section?: (label: string) => void;\n };\n cleanupSpawn?: ComposeSpawn;\n devcontainerSpawn?: DevcontainerSpawn;\n credentialsSpawn?: CredentialsSpawn;\n reachabilitySpawn?: ReachabilitySpawn;\n dockerInfoSpawn?: DockerInfoSpawn;\n identitySpawn?: IdentitySpawn;\n identityPrompt?: IdentityPrompt;\n}\n\nexport interface RunApplyResult {\n /** Absolute path to the materialized container directory. */\n targetDir: string;\n /** Absolute path to the source yml. */\n configPath: string;\n /** Exit code of the trailing `devcontainer up` step. */\n containerExitCode: number;\n}\n\nexport async function runApply(opts: RunApplyOptions): Promise<RunApplyResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n warn: (msg) => consola.warn(msg),\n // Default section renderer: empty line, bold-underlined \"▸ Label\",\n // empty line. Mirrors install.sh's section visuals.\n section: (label) => process.stderr.write(`\\n${sectionLine(label)}\\n\\n`),\n };\n const section = (label: string) => logger.section?.(label);\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const ymlPath = containerConfigPath(opts.name, home);\n if (!existsSync(ymlPath)) {\n throw new Error(\n `No such config: ${ymlPath}. Run \\`monoceros init <template> ${opts.name}\\` first.`,\n );\n }\n\n const targetDir = containerDir(opts.name, home);\n await assertSafeTargetDir(targetDir, opts.name);\n\n // ── Configuration ────────────────────────────────────────────\n section('Configuration');\n\n const parsed = await readConfig(ymlPath);\n // Read global defaults early — feature option defaults from\n // `monoceros-config.yml` need to be merged before scaffold codegen,\n // and the git identity logic later in this function also needs the\n // global config.\n const globalConfig = await readMonocerosConfig({ monocerosHome: home });\n\n // Pre-M4 the canonical feature namespace was\n // `ghcr.io/monoceros/features/…`. After the M4 cut it moved to\n // `ghcr.io/getmonoceros/monoceros-features/…`. Old refs still\n // structurally parse as third-party refs, so apply would silently\n // try to pull them from GHCR and 404. Warn loudly and tell the\n // builder what to write instead — but don't rewrite their yml or\n // fail the apply, so they stay in control of the migration.\n warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);\n\n // Shape validation happened in readConfig; catalog validation\n // (which language/service exists) happens here against\n // create/scaffold's known set.\n const createOpts = normalizeOptions(\n solutionConfigToCreateOptions(\n parsed.config,\n globalConfig?.defaults?.features ?? {},\n ),\n );\n validateOptions(createOpts);\n logger.success(`yml validated ${dim(`(${prettyPath(ymlPath)})`)}`);\n\n // Refresh host git identity and HTTPS credentials before the\n // container teardown so they're in place when post-create.sh runs.\n // Identity resolution priority: yml override → monoceros-config.yml\n // defaults → host global → persisted .monoceros/gitconfig → prompt.\n const idLogger = {\n info: logger.info,\n warn: logger.warn ?? logger.info,\n };\n await collectGitIdentity(targetDir, {\n ...(opts.identitySpawn ? { spawn: opts.identitySpawn } : {}),\n ...(opts.identityPrompt ? { prompt: opts.identityPrompt } : {}),\n ...(parsed.config.git?.user\n ? { containerOverride: parsed.config.git.user }\n : {}),\n ...(globalConfig?.defaults?.git?.user\n ? { defaults: globalConfig.defaults.git.user }\n : {}),\n logger: idLogger,\n });\n // Pre-fetch HTTPS credentials for every unique host derived from\n // the declared repos. Pre-flight: if any host returns no credentials,\n // fail fast with provider-specific setup hints — much more\n // actionable than letting the in-container `git clone` later die\n // with \"could not read Username\".\n //\n // First pass: reject hosts whose provider couldn't be resolved\n // (non-canonical host without an explicit `provider:` in the yml).\n // Those produce a separate \"set provider:\" error message — much\n // more useful than a generic \"no credentials\" hint because the\n // builder might actually have credentials in their helper, but we\n // wouldn't know which CLI to suggest.\n const hostsToFetch = uniqueHttpsHosts(createOpts.repos ?? []);\n const unknownProviderHosts = hostsToFetch\n .filter((h) => h.provider === 'unknown')\n .map((h) => h.host);\n if (unknownProviderHosts.length > 0) {\n throw new Error(formatUnknownProviderError(unknownProviderHosts));\n }\n if (hostsToFetch.length > 0) {\n const credResult = await collectGitCredentials(targetDir, hostsToFetch, {\n ...(opts.credentialsSpawn ? { spawn: opts.credentialsSpawn } : {}),\n logger: idLogger,\n });\n const missing = credResult.perHost.filter((p) => p.status !== 'ok');\n if (missing.length > 0) {\n throw new Error(formatMissingCredentialsError(missing));\n }\n }\n\n // Pre-flight stage 2: now that credentials are in place, probe each\n // declared repo URL via host-side `git ls-remote`. Catches the\n // \"repo doesn't exist / token can't see it / DNS broken\" failure\n // modes before the docker build runs — saving ~1–2 min on first\n // apply and replacing the noisy devcontainer-cli stack trace with\n // a focused per-repo error.\n const declaredRepos = createOpts.repos ?? [];\n if (declaredRepos.length > 0) {\n const reachability = await checkRepoReachability(declaredRepos, {\n ...(opts.reachabilitySpawn ? { spawn: opts.reachabilitySpawn } : {}),\n });\n const unreachable = reachability.filter((r) => !r.ok);\n if (unreachable.length > 0) {\n throw new Error(formatUnreachableReposError(unreachable));\n }\n }\n\n // ── Scaffold ─────────────────────────────────────────────────\n section('Scaffold');\n\n // Probe the host docker daemon for its mode (rootful vs rootless).\n // The result drives whether the generated devcontainer.json /\n // compose.yaml include `idmap` on their bind mounts — required for\n // rootless Docker on Linux where bind mounts otherwise apply a\n // user-namespace shift that breaks file ownership across the\n // host/container boundary. See docker-mode.ts for the rationale.\n const dockerMode = await detectDockerMode({\n ...(opts.dockerInfoSpawn ? { spawn: opts.dockerInfoSpawn } : {}),\n });\n\n await fs.mkdir(targetDir, { recursive: true });\n await writeScaffold(createOpts, targetDir, { dockerMode });\n await writeStateFile(\n targetDir,\n buildStateFile({\n origin: opts.name,\n cliVersion: opts.cliVersion,\n ...(opts.now ? { now: opts.now } : {}),\n }),\n );\n logger.success(`materialized into ${prettyPath(targetDir)}`);\n\n // ── Container ────────────────────────────────────────────────\n section('Container');\n\n // Pre-announce the feature list so the builder knows what's about\n // to be installed before devcontainer-cli's stream takes over.\n // Empty list = base-image-only container, no features section needed.\n const featureRefs = parsed.config.features.map((f) => f.ref);\n if (featureRefs.length > 0) {\n logger.info(`Features: ${featureRefs.map((r) => cyan(r)).join(', ')}`);\n }\n\n // First-apply UX: devcontainer-cli's upstream output prints\n // `Error fetching image details: No manifest found for …` for\n // multi-arch GHCR images, then sits silent for ~1 min while\n // Docker actually pulls the runtime image. Both are non-fatal —\n // the docker buildx step right after consumes the image just\n // fine. Flag in dim grey so it reads as ambient context.\n logger.info(\n dim(\n 'Pulling runtime image and building feature layers. First apply takes ~1–2 min (Docker downloads the multi-arch base); subsequent applies are cached and fast. devcontainer-cli may log a \"No manifest found\" line — harmless, the pull continues.',\n ),\n );\n\n const exitCode = await runContainerCycle(targetDir, {\n hasCompose: needsCompose(createOpts),\n ...(opts.cleanupSpawn !== undefined\n ? { cleanupSpawn: opts.cleanupSpawn }\n : {}),\n ...(opts.devcontainerSpawn !== undefined\n ? { devcontainerSpawn: opts.devcontainerSpawn }\n : {}),\n logger,\n });\n\n // ── Next steps ───────────────────────────────────────────────\n // Only print the wrap-up on a successful container start;\n // otherwise the failing devcontainer-cli output is the relevant\n // signal and a cheery \"shell into it!\" line would be misleading.\n if (exitCode === 0) {\n section('Next steps');\n logger.info(` ${cyan(`monoceros shell ${opts.name}`)}`);\n }\n\n return { targetDir, configPath: ymlPath, containerExitCode: exitCode };\n}\n\n/**\n * `<MONOCEROS_HOME>/container/<name>/` is safe to (re-)materialize iff:\n * - it doesn't exist or is empty (fresh apply), OR\n * - it already carries `.monoceros/state.json` with the same origin\n * (re-apply against the same yml), OR\n * - the only top-level entry is `.monoceros/` and there's no\n * state.json — that's a partial-apply remnant: pre-flight wrote\n * `gitconfig` / `git-credentials` into `.monoceros/` before\n * something (reachability failure, Ctrl-C, expired token, …)\n * aborted the apply ahead of `writeStateFile`. We own\n * `.monoceros/`, so re-running is safe.\n *\n * Anything else — state.json with a different origin, or files\n * outside `.monoceros/` that aren't ours — stays an error so we\n * don't clobber unrelated work.\n */\nasync function assertSafeTargetDir(\n targetDir: string,\n expectedOrigin: string,\n): Promise<void> {\n if (!existsSync(targetDir)) return;\n const entries = await fs.readdir(targetDir);\n if (entries.length === 0) return;\n\n const state = await readStateFile(targetDir);\n if (state) {\n if (state.origin !== expectedOrigin) {\n throw new Error(\n `${targetDir} is already materialized from config '${state.origin}', not '${expectedOrigin}'. Delete the directory to re-target, or run \\`monoceros apply ${state.origin}\\`.`,\n );\n }\n return; // safe: re-apply same origin\n }\n\n // No state.json. If the only top-level entry is `.monoceros/`, this\n // is a partial-apply remnant from a failed earlier run — pre-flight\n // writes `.monoceros/gitconfig` and `.monoceros/git-credentials`\n // BEFORE the scaffold + state.json sequence, so a mid-apply abort\n // leaves exactly this shape behind. Treat it as recoverable.\n if (entries.length === 1 && entries[0] === '.monoceros') {\n return;\n }\n\n throw new Error(\n `Refusing to materialize into non-empty directory ${targetDir} (no Monoceros state.json found, and the directory has files we don't recognise). Delete the directory before re-running.`,\n );\n}\n\ninterface MigrationLogger {\n warn?: (msg: string) => void;\n info: (msg: string) => void;\n}\n\nfunction warnOnDeprecatedFeatureRefs(\n containerFeatures: SolutionConfig['features'],\n globalConfig: MonocerosConfig | undefined,\n logger: MigrationLogger,\n): void {\n const warn = logger.warn ?? logger.info;\n const seen = new Set<string>();\n const emit = (oldRef: string, source: string) => {\n if (seen.has(oldRef)) return;\n seen.add(oldRef);\n const newRef = migrateDeprecatedFeatureRef(oldRef);\n if (!newRef) return;\n warn(\n `Deprecated feature ref in ${source}: '${oldRef}'. ` +\n `Replace with '${newRef}' — the old namespace is no longer published. ` +\n `See docs/MIGRATION-M4.md for a sed snippet.`,\n );\n };\n\n for (const entry of containerFeatures) {\n emit(entry.ref, 'container yml');\n }\n const globalDefaults = globalConfig?.defaults?.features;\n if (globalDefaults) {\n for (const ref of Object.keys(globalDefaults)) {\n emit(ref, 'monoceros-config.yml');\n }\n }\n}\n","import { promises as fs } from 'node:fs';\nimport { z } from 'zod';\nimport { parseDocument } from 'yaml';\nimport { FeatureOptionValueSchema, GitUserSchema, REGEX } from './schema.js';\nimport { monocerosConfigPath, monocerosHome } from './paths.js';\n\n/**\n * `<MONOCEROS_HOME>/monoceros-config.yml` — optional builder-owned\n * defaults that apply across every container materialized through\n * this home. Today the only field is git identity; future fields\n * (default editor, Claude auth profile, ...) plug into the same\n * structure.\n *\n * Schema is permissive: missing top-level keys are fine, the file\n * itself is optional. Schema violations surface as a hard error\n * (better to refuse than silently ignore a typo'd key the builder\n * thought was effective).\n */\n\nconst SCHEMA_VERSION = 1 as const;\n\n/**\n * `defaults.features` — map of devcontainer feature ref to a default\n * option object. When a container yml references the same feature ref\n * without overriding a specific option, the value from here is used.\n * Per-container options always win.\n *\n * Typical use: stash the Atlassian apiToken / Anthropic apiKey here\n * once globally instead of repeating them in every container yml.\n */\nexport const MonocerosConfigSchema = z.object({\n schemaVersion: z.literal(SCHEMA_VERSION),\n // .nullish() (= .optional().nullable()) on defaults so the shipped\n // sample yml — where `defaults:` is uncommented but every sub-block\n // is commented out — parses cleanly. YAML produces `defaults: null`\n // in that case; without .nullish() the schema would reject it and\n // we'd be back to forcing builders to comment-juggle three lines.\n defaults: z\n .object({\n git: z\n .object({\n user: GitUserSchema.optional(),\n })\n .optional(),\n features: z\n .record(\n z\n .string()\n .regex(\n REGEX.featureRef,\n \"Invalid feature ref. Expected an OCI-image-style ref like 'ghcr.io/getmonoceros/monoceros-features/<name>:<tag>'.\",\n ),\n z.record(z.string(), FeatureOptionValueSchema),\n )\n .optional(),\n })\n .nullish(),\n});\n\nexport type MonocerosConfig = z.infer<typeof MonocerosConfigSchema>;\n\nexport interface ReadMonocerosConfigOptions {\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n}\n\n/**\n * Read `<home>/monoceros-config.yml`. Returns `undefined` if the file\n * isn't there (the normal case for a fresh setup). Throws on a parse\n * or schema error — the builder explicitly created the file, so a\n * silent ignore would be worse than a loud abort.\n */\nexport async function readMonocerosConfig(\n opts: ReadMonocerosConfigOptions = {},\n): Promise<MonocerosConfig | undefined> {\n const home = opts.monocerosHome ?? monocerosHome();\n const filePath = monocerosConfigPath(home);\n let text: string;\n try {\n text = await fs.readFile(filePath, 'utf8');\n } catch {\n return undefined;\n }\n const doc = parseDocument(text, { prettyErrors: true });\n if (doc.errors.length > 0) {\n throw new Error(\n `yaml parse error in ${filePath}: ${doc.errors[0]!.message}`,\n );\n }\n const result = MonocerosConfigSchema.safeParse(doc.toJS());\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(\n `Invalid ${filePath}:\\n${issues}\\n\\nSee ${filePath.replace(\n /\\.yml$/,\n '.sample.yml',\n )} for a valid example.`,\n );\n }\n return result.data;\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { CONFIG_SCHEMA_VERSION } from './schema.js';\n\n/**\n * `.monoceros/state.json` — the Phase-3 replacement for `stack.json`.\n *\n * The yml at `.local/container-configs/<origin>.yml` is the source\n * of truth. `state.json` is a back-reference: it tells `monoceros\n * apply` (no args) which yml to read and re-apply for this dev-\n * container. Other fields (appliedAt, cliVersion) are pure diagnostics\n * for `monoceros status` and ad-hoc debugging.\n *\n * `materializedAt` is the timestamp of the most recent apply, NOT\n * the create. There is no `createdAt` because the yml is the\n * lifecycle anchor now — multiple dev-containers can share one yml,\n * and each has its own state.json timeline.\n */\nexport interface StateFile {\n schemaVersion: typeof CONFIG_SCHEMA_VERSION;\n /** Config name the yml is stored under (`<origin>.yml`). */\n origin: string;\n /** Monoceros CLI version that wrote this state.json. */\n monocerosCliVersion: string;\n /** ISO-8601 timestamp of the most recent apply. */\n materializedAt: string;\n}\n\nexport function buildStateFile(opts: {\n origin: string;\n cliVersion: string;\n now?: Date;\n}): StateFile {\n return {\n schemaVersion: CONFIG_SCHEMA_VERSION,\n origin: opts.origin,\n monocerosCliVersion: opts.cliVersion,\n materializedAt: (opts.now ?? new Date()).toISOString(),\n };\n}\n\nexport function stateFilePath(targetDir: string): string {\n return path.join(targetDir, '.monoceros', 'state.json');\n}\n\nexport async function readStateFile(\n targetDir: string,\n): Promise<StateFile | undefined> {\n try {\n const content = await fs.readFile(stateFilePath(targetDir), 'utf8');\n return JSON.parse(content) as StateFile;\n } catch {\n return undefined;\n }\n}\n\nexport async function writeStateFile(\n targetDir: string,\n state: StateFile,\n): Promise<void> {\n const monocerosDir = path.join(targetDir, '.monoceros');\n await fs.mkdir(monocerosDir, { recursive: true });\n await fs.writeFile(\n stateFilePath(targetDir),\n JSON.stringify(state, null, 2) + '\\n',\n );\n}\n","import { deriveRepoName } from '../create/scaffold.js';\nimport type { CreateOptions, FeatureOptions } from '../create/types.js';\nimport type { SolutionConfig } from './schema.js';\n\n/**\n * Translate a yml-shaped `SolutionConfig` into the `CreateOptions`\n * shape the existing scaffolders (devcontainer.json, compose.yaml,\n * post-create.sh) consume.\n *\n * The big shape mismatch is `features`:\n * - yml: `[{ ref, options }, …]` (array; humans edit/comment this)\n * - CreateOptions: `Record<ref, options>` (devcontainer.json shape)\n * We dedupe by `ref` and keep the LAST occurrence — same rule the\n * existing `add-feature` command uses when a builder re-adds with new\n * options.\n *\n * `externalServices.postgres` → `postgresUrl` (the CreateOptions\n * field name predates the yml schema; rename would touch every M1\n * caller, so the translator absorbs the diff here).\n *\n * `featureDefaults` (optional) — `defaults.features` from\n * `monoceros-config.yml`. Per-container options always override these;\n * keys not set per-container fall back to the default. A feature ref\n * that exists only in `featureDefaults` (not in the container yml)\n * does NOT get included — the container yml is what decides whether\n * a feature is active at all; the defaults only fill in option values.\n */\nexport function solutionConfigToCreateOptions(\n config: SolutionConfig,\n featureDefaults: Record<string, FeatureOptions> = {},\n): CreateOptions {\n const featureRecord: Record<string, FeatureOptions> = {};\n for (const entry of config.features) {\n const defaults = featureDefaults[entry.ref] ?? {};\n featureRecord[entry.ref] = { ...defaults, ...(entry.options ?? {}) };\n }\n\n const result: CreateOptions = {\n name: config.name,\n languages: [...config.languages],\n services: [...config.services],\n };\n\n if (config.externalServices.postgres !== undefined) {\n result.postgresUrl = config.externalServices.postgres;\n }\n if (config.aptPackages.length > 0) {\n result.aptPackages = [...config.aptPackages];\n }\n if (Object.keys(featureRecord).length > 0) {\n result.features = featureRecord;\n }\n if (config.installUrls.length > 0) {\n result.installUrls = [...config.installUrls];\n }\n if (config.repos.length > 0) {\n result.repos = config.repos.map((r) => ({\n url: r.url,\n // `path` is optional in the yml; CreateOptions requires it.\n // When the yml omits `path`, fall back to the URL-derived\n // single-segment default (`https://.../foo.git` → `foo`),\n // which lands the clone at `projects/foo/`.\n path: r.path ?? deriveRepoName(r.url),\n ...(r.git?.user\n ? { gitUser: { name: r.git.user.name, email: r.git.user.email } }\n : {}),\n ...(r.provider ? { provider: r.provider } : {}),\n }));\n }\n return result;\n}\n","// Shared terminal-formatting helpers for CLI output. Same palette\n// as install.sh and packages/cli/src/help.ts:\n//\n// cyan = identifiers you type (commands, refs, component names)\n// grey = supplementary metadata (paths, version notes, hints)\n// bold+und. = structural section markers\n//\n// Status semantics (green ✓, red ✗, yellow !) live in consola for\n// log-level lines and aren't duplicated here.\n//\n// Two flavours of consumer:\n// - status output (install.sh, apply) goes to stderr — use the\n// top-level helpers below; they gate on process.stderr.isTTY.\n// - data output (list-components) goes to stdout — use\n// `colorsFor(process.stdout)` to get the same helpers gated\n// against the right stream, so colours drop out cleanly when\n// the user pipes the output into grep/less/etc.\n\nconst ESC = '\\x1b[';\nconst ANSI_BOLD = `${ESC}1m`;\nconst ANSI_UNDERLINE = `${ESC}4m`;\nconst ANSI_CYAN = `${ESC}36m`;\nconst ANSI_GREY = `${ESC}90m`;\nconst ANSI_RESET = `${ESC}0m`;\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;]*m/g;\n\n/**\n * Visible character count, ANSI escape sequences stripped. Used\n * for column-padding so coloured labels still line up.\n */\nexport function visibleLen(s: string): number {\n return s.replace(ANSI_RE, '').length;\n}\n\nexport interface Palette {\n bold: (s: string) => string;\n underline: (s: string) => string;\n cyan: (s: string) => string;\n dim: (s: string) => string;\n /**\n * Section marker — bold + underlined with a `▸` chevron prefix.\n * Same visual treatment as install.sh's section headers.\n */\n sectionLine: (label: string) => string;\n}\n\nfunction makeWrap(isTty: boolean): (s: string, ...codes: string[]) => string {\n return (s, ...codes) => (isTty ? codes.join('') + s + ANSI_RESET : s);\n}\n\nfunction makePalette(isTty: boolean): Palette {\n const wrap = makeWrap(isTty);\n return {\n bold: (s) => wrap(s, ANSI_BOLD),\n underline: (s) => wrap(s, ANSI_UNDERLINE),\n cyan: (s) => wrap(s, ANSI_CYAN),\n dim: (s) => wrap(s, ANSI_GREY),\n sectionLine: (label) => wrap(`▸ ${label}`, ANSI_BOLD, ANSI_UNDERLINE),\n };\n}\n\n/**\n * Resolve a stream-specific palette. Pass `process.stdout` for\n * commands whose payload goes to stdout (so colours drop out when\n * piped); `process.stderr` for status output that stays on stderr\n * regardless of stdout's destination.\n */\nexport function colorsFor(stream: NodeJS.WriteStream): Palette {\n return makePalette(stream.isTTY ?? false);\n}\n\n// Top-level convenience helpers — gated on stderr, matching the\n// install-/apply-style status output that's always written to\n// stderr. Existing call sites keep working unchanged.\nconst stderrPalette = makePalette(process.stderr.isTTY ?? false);\nexport const bold = stderrPalette.bold;\nexport const underline = stderrPalette.underline;\nexport const cyan = stderrPalette.cyan;\nexport const dim = stderrPalette.dim;\nexport const sectionLine = stderrPalette.sectionLine;\n","import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport { createSecretMaskStream } from '../util/mask-secrets.js';\nimport { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\n\nexport type ComposeSpawn = (args: string[], cwd: string) => Promise<number>;\n\n// Default spawn: shells out to `docker compose` (the v2 docker\n// subcommand). Stdout/stderr are streamed through a secret masker\n// (see util/mask-secrets.ts) so feature option dumps, ENV-printouts\n// and similar do not leak Atlassian/GitHub/Anthropic tokens onto\n// the host terminal.\nexport const spawnDockerCompose: ComposeSpawn = (args, cwd) => {\n return new Promise((resolve, reject) => {\n const child = spawn('docker', ['compose', ...args], {\n cwd,\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n\n// Generic shell spawn used by `monoceros apply`/`remove` for label-\n// based docker cleanup pipelines. Same ComposeSpawn shape so tests\n// can inject a fake; `args[0]` is `-c`, `args[1]` is the shell\n// command string. Output goes through the secret masker for the\n// same reasons spawnDockerCompose does.\nexport const spawnBash: ComposeSpawn = (args, cwd) => {\n return new Promise((resolve, reject) => {\n const child = spawn('bash', args, {\n cwd,\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n\ninterface ResolvedCompose {\n composeFile: string;\n projectName: string;\n}\n\n// Match the project name `@devcontainers/cli` derives when it brings a\n// compose-mode devcontainer up: `<root-basename>_devcontainer`.\n// Aligning here means `monoceros start/stop/status/logs` and the\n// implicit `devcontainer up` from `monoceros run/shell` act on the\n// same compose project — without it docker would create two parallel\n// stacks.\nexport function composeProjectName(root: string): string {\n return `${path.basename(root)}_devcontainer`;\n}\n\n/**\n * Resolve compose-mode metadata for the container rooted at `root`.\n * `root` is `<MONOCEROS_HOME>/container/<name>/` and must already\n * exist with a `.devcontainer/compose.yaml` inside. The compose-only\n * lifecycle commands (`start/stop/status/logs/down`) error when the\n * file is missing.\n */\nexport function resolveCompose(root: string): ResolvedCompose {\n if (!existsSync(path.join(root, '.devcontainer'))) {\n throw new Error(\n `No .devcontainer/ at ${root}. Run \\`monoceros apply <name>\\` first.`,\n );\n }\n const composeFile = path.join(root, '.devcontainer', 'compose.yaml');\n if (!existsSync(composeFile)) {\n throw new Error(\n `No compose.yaml at ${composeFile}. \\`start\\` / \\`stop\\` / \\`status\\` / \\`logs\\` require services configured via \\`monoceros add-service <name> <svc>\\`. Use \\`monoceros shell <name>\\` to enter the container directly.`,\n );\n }\n return { composeFile, projectName: composeProjectName(root) };\n}\n\nexport interface ComposeActionOptions {\n root: string;\n service?: string;\n spawn?: ComposeSpawn;\n}\n\nasync function runComposeAction(\n buildSubArgs: (service: string | undefined) => string[],\n opts: ComposeActionOptions,\n): Promise<number> {\n const { composeFile, projectName } = resolveCompose(opts.root);\n const spawnFn = opts.spawn ?? spawnDockerCompose;\n const subArgs = buildSubArgs(opts.service);\n return spawnFn(['-f', composeFile, '-p', projectName, ...subArgs], opts.root);\n}\n\nexport interface StartOptions {\n root: string;\n spawn?: DevcontainerSpawn;\n logger?: { info: (message: string) => void };\n}\n\n// `monoceros start` delegates to `devcontainer up` rather than to\n// `docker compose up -d`. The detour through @devcontainers/cli matters\n// because:\n// - it labels the workspace container with `devcontainer.local_folder`\n// so subsequent `devcontainer exec` (from `monoceros run/shell`) can\n// find the container by workspace path,\n// - it applies devcontainer features (which docker compose ignores), and\n// - it triggers the postCreateCommand once.\n// The auxiliary services come up alongside because the generated\n// devcontainer.json lists them under `runServices`.\nexport async function runStart(opts: StartOptions): Promise<number> {\n resolveCompose(opts.root); // throws if no compose.yaml\n const logger = opts.logger ?? { info: (msg) => consola.info(msg) };\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n logger.info(`Bringing devcontainer up at ${opts.root}…`);\n return spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n );\n}\n\nexport interface RunContainerCycleOptions {\n hasCompose: boolean;\n cleanupSpawn?: ComposeSpawn;\n devcontainerSpawn?: DevcontainerSpawn;\n logger: {\n info: (message: string) => void;\n warn?: (message: string) => void;\n };\n}\n\n/**\n * Container teardown + up for a devcontainer rooted at `root`.\n * Used by `runApply` (apply/index.ts) after writing the scaffold.\n */\nexport async function runContainerCycle(\n root: string,\n opts: RunContainerCycleOptions,\n): Promise<number> {\n const { hasCompose, logger } = opts;\n\n if (hasCompose) {\n const projectName = composeProjectName(root);\n logger.info(\n `Force-removing existing ${projectName} containers (volumes preserved)…`,\n );\n const cleanupSpawn = opts.cleanupSpawn ?? spawnBash;\n // Two-step removal so a container with stale/missing labels still\n // gets caught:\n // - by docker-compose project label\n // - by container-name prefix `<project>-*`\n // After removal we re-query: if anything remains, VS Code's Remote\n // Containers extension is the likely culprit (auto-recreates on\n // container loss); we abort with a clear hint rather than letting\n // `devcontainer up` collide.\n const script = [\n `set -u`,\n `echo \"[cleanup] checking project ${projectName}…\"`,\n `by_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n `by_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n `to_remove=$(printf \"%s\\\\n%s\\\\n\" \"$by_label\" \"$by_name\" | sort -u | grep -v \"^$\" || true)`,\n `if [ -n \"$to_remove\" ]; then echo \"[cleanup] removing: $(echo $to_remove | tr \"\\\\n\" \" \")\"; docker rm -f $to_remove >/dev/null || true; else echo \"[cleanup] no containers to remove\"; fi`,\n `docker network rm ${projectName}_default 2>/dev/null && echo \"[cleanup] network ${projectName}_default removed\" || echo \"[cleanup] network ${projectName}_default not present\"`,\n `remaining_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n `remaining_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n `if [ -n \"$remaining_label\" ] || [ -n \"$remaining_name\" ]; then echo \"\" >&2; echo \"ERROR: containers under project ${projectName} reappeared after removal.\" >&2; echo \"This typically means VS Code's Remote Containers extension is connected to\" >&2; echo \"this devcontainer and auto-recreated it. Close the dev container session\" >&2; echo \"in VS Code (Cmd+Shift+P → 'Dev Containers: Close Remote Connection')\" >&2; echo \"and retry \\\\\\`monoceros apply\\\\\\`.\" >&2; exit 1; fi`,\n `echo \"[cleanup] done\"`,\n ].join('; ');\n const cleanupCode = await cleanupSpawn(['-c', script], root);\n if (cleanupCode !== 0) return cleanupCode;\n\n return runStart({\n root,\n ...(opts.devcontainerSpawn ? { spawn: opts.devcontainerSpawn } : {}),\n logger,\n });\n }\n\n logger.info(`Recreating image-mode devcontainer at ${root}…`);\n const spawnFn = opts.devcontainerSpawn ?? spawnDevcontainer;\n return spawnFn(\n [\n 'up',\n '--workspace-folder',\n root,\n '--mount-workspace-git-root=false',\n '--remove-existing-container',\n ],\n root,\n );\n}\n\nexport function runStop(opts: ComposeActionOptions): Promise<number> {\n return runComposeAction(\n (service) => ['stop', ...(service ? [service] : [])],\n opts,\n );\n}\n\nexport function runStatus(opts: ComposeActionOptions): Promise<number> {\n return runComposeAction(\n (service) => ['ps', ...(service ? [service] : [])],\n opts,\n );\n}\n\nexport interface LogsOptions extends ComposeActionOptions {\n follow?: boolean;\n}\n\nexport function runLogs(opts: LogsOptions): Promise<number> {\n const follow = opts.follow ?? true;\n return runComposeAction(\n (service) => [\n 'logs',\n ...(follow ? ['-f'] : []),\n ...(service ? [service] : []),\n ],\n opts,\n );\n}\n","import { Transform, type TransformCallback } from 'node:stream';\n\n/**\n * Mask known token-shaped strings in arbitrary text.\n *\n * Devcontainer-cli and docker compose stream the build/up output\n * straight to stdout. They include the feature options (Atlassian\n * apiToken, GitHub PAT, Anthropic apiKey, …) verbatim, which leaks\n * real, per-builder secrets onto the user's terminal and into any\n * captured CI log.\n *\n * The fix is a regex sweep on each output line: when a token\n * matches a known prefix shape, replace its middle with `…` and\n * keep the prefix + last 6 characters so the value is still\n * recognizable for debugging (\"did the right token get loaded?\")\n * without exposing the secret.\n *\n * What's **not** masked here, by design:\n *\n * - The literal `monoceros` user/password baked into the compose\n * service catalog (postgres, mysql). It's a documented dev-\n * convention, identical on every Monoceros container, openly\n * listed in `create/catalog.ts` and the components README. Not\n * a secret. Masking it would just make the connection string\n * harder to spot for the builder.\n * - Anything that looks \"password-shaped\" via a key= pattern.\n * Risk of false positives outweighs cosmetic benefit when the\n * value isn't actually sensitive (see also ADR-style note in\n * `create/catalog.ts`).\n */\n\ninterface SecretPattern {\n /** Short label for the pattern, useful in debugging. */\n name: string;\n /** Match shape. Must be a /g regex so all occurrences get replaced. */\n re: RegExp;\n}\n\n// Order doesn't matter — patterns are disjoint by prefix.\nconst PATTERNS: SecretPattern[] = [\n // Atlassian Cloud API token. Starts with literal `ATATT3xFf` plus\n // a long URL-safe-base64 tail. Tightened to that prefix to avoid\n // matching unrelated all-caps words.\n { name: 'atlassian-api', re: /ATATT3xFf[A-Za-z0-9+/=_-]{20,}/g },\n // Bitbucket Cloud app password.\n { name: 'bitbucket-app', re: /ATBB[A-Za-z0-9+/=_-]{20,}/g },\n // GitHub PAT (classic), OAuth, user, server, refresh — all share\n // the `gh<lower-letter>_<base62>` shape per GitHub's token format.\n { name: 'github-token', re: /gh[a-z]_[A-Za-z0-9]{20,}/g },\n // GitHub fine-grained PAT.\n { name: 'github-pat', re: /github_pat_[A-Za-z0-9_]{20,}/g },\n // Anthropic API key.\n { name: 'anthropic-api', re: /sk-ant-[A-Za-z0-9_-]{20,}/g },\n];\n\n/**\n * Replace token-shaped substrings with a masked form. Idempotent\n * for already-masked strings (the elision character isn't part of\n * any pattern's alphabet).\n */\nexport function maskSecrets(text: string): string {\n let result = text;\n for (const { re } of PATTERNS) {\n result = result.replace(re, maskOne);\n }\n return result;\n}\n\nfunction maskOne(token: string): string {\n if (token.length <= 12) return token;\n return `${token.slice(0, 5)}…${token.slice(-6)}`;\n}\n\n/**\n * Transform stream that runs every chunk through `maskSecrets`.\n *\n * Tokens can in theory straddle a chunk boundary if the upstream\n * writer flushes mid-token, leaving an unmasked tail. Mitigation:\n * the transform holds back the last line of every chunk until a\n * newline arrives, since real Docker / devcontainer-cli output is\n * line-oriented and tokens don't contain newlines. On final flush\n * any leftover buffer is masked and emitted.\n */\nexport function createSecretMaskStream(): Transform {\n let buffer = '';\n return new Transform({\n decodeStrings: true,\n transform(chunk: Buffer | string, _enc, cb: TransformCallback): void {\n const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');\n buffer += text;\n const lastNewline = buffer.lastIndexOf('\\n');\n if (lastNewline === -1) {\n // No complete line yet — keep buffering. We'd rather hold\n // back partial output briefly than emit half a token.\n cb(null);\n return;\n }\n const flushable = buffer.slice(0, lastNewline + 1);\n buffer = buffer.slice(lastNewline + 1);\n cb(null, maskSecrets(flushable));\n },\n flush(cb: TransformCallback): void {\n if (buffer.length > 0) {\n const tail = maskSecrets(buffer);\n buffer = '';\n cb(null, tail);\n return;\n }\n cb(null);\n },\n });\n}\n","import { spawn } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { createSecretMaskStream, maskSecrets } from '../util/mask-secrets.js';\n\nconst require_ = createRequire(import.meta.url);\n\nlet cachedBinaryPath: string | null = null;\n\n// Resolve the absolute path to the `@devcontainers/cli` JS entry point. We\n// invoke it via `node <path>` rather than relying on a `.bin/` shim being on\n// PATH, so the CLI works regardless of how the user installed the workbench.\nexport function devcontainerCliPath(): string {\n if (cachedBinaryPath) return cachedBinaryPath;\n const pkgJsonPath = require_.resolve('@devcontainers/cli/package.json');\n const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8')) as {\n bin?: string | Record<string, string>;\n };\n const binEntry =\n typeof pkg.bin === 'string' ? pkg.bin : (pkg.bin?.devcontainer ?? '');\n if (!binEntry) {\n throw new Error('Could not resolve @devcontainers/cli bin entry.');\n }\n cachedBinaryPath = path.resolve(path.dirname(pkgJsonPath), binEntry);\n return cachedBinaryPath;\n}\n\nexport interface DevcontainerSpawnOptions {\n // When true, capture stdout and stderr instead of inheriting them.\n // The buffered output is only flushed (to stderr) if the process exits\n // non-zero, so successful no-op invocations stay silent. Use this for\n // intermediate steps like the implicit `up` that `monoceros run` does\n // before `exec`; leave it unset for explicit lifecycle calls\n // (`monoceros start`) and for the final exec where the user expects to\n // see output.\n quiet?: boolean;\n // When true, hand the child process direct stdio. Pure `inherit` —\n // no piping, no secret masking, no buffering. Required for any\n // interactive use case (`monoceros shell`, the `exec` step of\n // `monoceros run`): bash detects a non-TTY stdin/stdout and exits\n // immediately, which makes `--with` stdio pipes a non-starter.\n // The build/log paths in apply and start still go through the\n // masked-pipe path, where there's no TTY at stake.\n interactive?: boolean;\n}\n\nexport type DevcontainerSpawn = (\n args: string[],\n cwd: string,\n options?: DevcontainerSpawnOptions,\n) => Promise<number>;\n\n// Default spawn implementation: runs the @devcontainers/cli binary\n// directly. Both stdout and stderr are streamed through a secret\n// masker (see util/mask-secrets.ts) so that feature options like\n// Atlassian apiTokens or GitHub PATs do not leak verbatim to the\n// terminal when devcontainer-cli logs the build args / feature\n// option dumps. With `{ quiet: true }` output is buffered (and\n// masked) and only flushed on a non-zero exit.\nexport const spawnDevcontainer: DevcontainerSpawn = (\n args,\n cwd,\n options = {},\n) => {\n const binPath = devcontainerCliPath();\n return new Promise((resolve, reject) => {\n if (options.interactive) {\n // Direct inherit — required so the child binary sees a real\n // TTY on stdin/stdout/stderr. Secret masking is irrelevant\n // here (the builder is running an interactive command;\n // build-time option dumps don't fire on this path).\n const child = spawn(process.execPath, [binPath, ...args], {\n cwd,\n stdio: 'inherit',\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n return;\n }\n const child = spawn(process.execPath, [binPath, ...args], {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n if (options.quiet) {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n child.stdout?.on('data', (chunk: Buffer) => stdoutChunks.push(chunk));\n child.stderr?.on('data', (chunk: Buffer) => stderrChunks.push(chunk));\n child.on('error', reject);\n child.on('exit', (code) => {\n const exitCode = code ?? 0;\n if (exitCode !== 0) {\n process.stderr.write(\n maskSecrets(Buffer.concat(stderrChunks).toString('utf8')),\n );\n process.stderr.write(\n maskSecrets(Buffer.concat(stdoutChunks).toString('utf8')),\n );\n }\n resolve(exitCode);\n });\n return;\n }\n child.stdout?.pipe(createSecretMaskStream()).pipe(process.stdout);\n child.stderr?.pipe(createSecretMaskStream()).pipe(process.stderr);\n child.on('error', reject);\n child.on('exit', (code) => resolve(code ?? 0));\n });\n};\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { RepoEntry } from '../create/types.js';\nimport { KNOWN_PROVIDER_HOSTS, type RepoProvider } from '../config/schema.js';\nimport { cyan } from '../util/format.js';\n\n/**\n * Spawn signature for `git credential fill`: takes the credential-\n * protocol input on stdin, returns the helper's response on stdout\n * plus the process exit code. Injected by tests.\n */\nexport type CredentialsSpawn = (\n input: string,\n) => Promise<{ stdout: string; exitCode: number }>;\n\nconst realGitCredentialFill: CredentialsSpawn = (input) => {\n return new Promise((resolve, reject) => {\n // GIT_TERMINAL_PROMPT=0 disables git's interactive\n // username/password fallback. Without this, when no credential\n // helper has an entry for the host, `git credential fill` would\n // open /dev/tty and prompt the user — which hangs apply\n // indefinitely because the parent process is running non-\n // interactively. With the env var set, git returns whatever\n // the helpers produced (possibly empty) and exits cleanly,\n // letting our pre-flight detect \"no credentials\" reliably.\n //\n // GIT_ASKPASS='' / SSH_ASKPASS='' are belt-and-suspenders for\n // setups where a GUI askpass helper is configured globally —\n // emptying them prevents a popup that would also block apply.\n const child = spawn('git', ['credential', 'fill'], {\n stdio: ['pipe', 'pipe', 'inherit'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: '',\n SSH_ASKPASS: '',\n },\n });\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve({ stdout, exitCode: code ?? 0 }));\n child.stdin.write(input);\n child.stdin.end();\n });\n};\n\n/**\n * Resolve a host's provider:\n * - canonical hosts (github.com / gitlab.com / bitbucket.org) →\n * their fixed provider, ignoring any explicit hint (the canonical\n * mapping is the source of truth)\n * - any other host → the explicit hint if given, else 'unknown'\n *\n * Returning 'unknown' triggers the apply pre-flight error that asks\n * the builder to set `provider:` in the yml. We deliberately never\n * guess from hostname patterns (\"starts with `gitlab.`\" etc.) —\n * those produced wrong results for corporate domains like\n * `git.firma.de` and silently fell through to the generic hint.\n */\nexport type ResolvedProvider = RepoProvider | 'unknown';\n\nexport function resolveProvider(\n host: string,\n explicit?: RepoProvider,\n): ResolvedProvider {\n const canonical = KNOWN_PROVIDER_HOSTS[host.toLowerCase()];\n if (canonical) return canonical;\n return explicit ?? 'unknown';\n}\n\nexport interface HostWithProvider {\n host: string;\n provider: ResolvedProvider;\n}\n\n/**\n * Reduce a repo list to one entry per host, carrying along the\n * resolved provider so the pre-flight check can render the right\n * setup hint (or error out with \"set provider:\" for unknowns).\n *\n * If the same host appears with conflicting provider declarations\n * across multiple repo entries, the first one wins — the apply\n * pre-flight surfaces the conflict as a separate diagnostic before\n * we get here in normal flow. (Schema-level dedup would lock us in\n * before the builder ever sees the warning.)\n */\nfunction uniqueHttpsHosts(repos: readonly RepoEntry[]): HostWithProvider[] {\n const byHost = new Map<string, HostWithProvider>();\n for (const repo of repos) {\n if (!repo.url.startsWith('https://')) continue;\n let host: string;\n try {\n host = new URL(repo.url).hostname;\n } catch {\n // Skip malformed URLs — validateOptions catches them at the\n // add-repo step, so reaching this in production means a stack\n // file was hand-edited. Don't fail the whole apply for it.\n continue;\n }\n if (byHost.has(host)) continue;\n byHost.set(host, { host, provider: resolveProvider(host, repo.provider) });\n }\n return [...byHost.values()];\n}\n\n/**\n * Render a provider-specific install command, filtered to the host\n * OS. Returns the relevant line for the current platform — macOS gets\n * the brew command, Windows gets the winget command, Linux falls back\n * to a docs link unless the provider officially recommends a uniform\n * Linux command (then `linuxBrew` is set and used). Callers embed the\n * resulting single line into a setup-instructions block.\n */\nfunction installCommandForOS(opts: {\n brew: string;\n winget: string;\n /**\n * Linux install command when the provider's docs officially\n * recommend a single uniform path (e.g. GitLab's glab CLI ships\n * Homebrew as the supported Linux install method). When omitted,\n * Linux gets a docs link instead because distro packaging is too\n * heterogeneous to pick a winner.\n */\n linuxBrew?: string;\n linuxDocsUrl: string;\n}): string {\n switch (process.platform) {\n case 'darwin':\n return cyan(opts.brew);\n case 'win32':\n return cyan(opts.winget);\n default:\n if (opts.linuxBrew) return cyan(opts.linuxBrew);\n return `See ${opts.linuxDocsUrl} for package instructions.`;\n }\n}\n\n/**\n * Provider-specific setup hint per host. Used in the pre-flight\n * error message when `git credential fill` returns nothing for a\n * host. Shows only the install command for the current host OS —\n * less visual noise, no \"is this me?\" guesswork for the builder.\n *\n * Provider is resolved upstream (canonical-host lookup or explicit\n * yml field). This function NEVER guesses from hostname patterns;\n * see `resolveProvider` for the rationale.\n */\nexport function providerSetupHint(\n host: string,\n provider: RepoProvider,\n): {\n /** Short title for the host, formatted as \"host — Provider\". */\n title: string;\n /** Multiline body, left-aligned, no leading indentation. */\n body: string;\n} {\n if (provider === 'github') {\n // `--hostname` is only needed for self-hosted GitHub Enterprise\n // Server. For github.com (SaaS) gh defaults to that host, so we\n // omit the flag. Both `gh auth login` and `gh auth setup-git`\n // accept --hostname with identical semantics — verified against\n // https://cli.github.com/manual/gh_auth_login and\n // https://cli.github.com/manual/gh_auth_setup-git .\n const isSaas = host.toLowerCase() === 'github.com';\n const hostArg = isSaas ? '' : ` --hostname ${host}`;\n const install = installCommandForOS({\n brew: 'brew install gh',\n winget: 'winget install --id GitHub.cli',\n linuxDocsUrl: 'https://github.com/cli/cli#installation',\n });\n return {\n title: `${host} — GitHub`,\n body: [\n 'Install the GitHub CLI:',\n install,\n '',\n 'Then run once:',\n cyan(`gh auth login${hostArg}`),\n cyan(`gh auth setup-git${hostArg}`),\n '',\n '`gh auth login` walks through OAuth in your browser.',\n '`gh auth setup-git` wires gh into git as a credential helper.',\n ].join('\\n'),\n };\n }\n if (provider === 'gitlab') {\n // `--hostname` is only needed for self-hosted GitLab. For\n // gitlab.com glab defaults to the SaaS host, so we omit the flag.\n const isSaas = host.toLowerCase() === 'gitlab.com';\n const hostArg = isSaas ? '' : ` --hostname ${host}`;\n // GitLab's official install docs (https://gitlab.com/gitlab-org/\n // cli/-/blob/main/docs/installation_options.md) state that\n // Homebrew is \"the officially supported installation method for\n // Linux\" — so we use the same brew command on macOS AND Linux,\n // with winget on Windows and a docs link as the absolute last\n // resort.\n const install = installCommandForOS({\n brew: 'brew install glab',\n winget: 'winget install --id GLab.GLab',\n linuxBrew: 'brew install glab',\n linuxDocsUrl: 'https://gitlab.com/gitlab-org/cli#installation',\n });\n return {\n title: `${host} — GitLab`,\n body: [\n 'Install the GitLab CLI (glab):',\n install,\n '',\n 'Then run once:',\n cyan(`glab auth login${hostArg}`),\n '',\n 'Choose `HTTPS` when asked for git-protocol, then accept',\n '\"Authenticate Git with your GitLab credentials\" — glab',\n 'configures itself as the git credential helper.',\n ].join('\\n'),\n };\n }\n if (provider === 'bitbucket') {\n // Bitbucket has no first-party CLI for git-credentials (no\n // `bb auth login` equivalent to gh/glab), so this is a manual\n // one-time setup either way. The Cloud and Data-Center variants\n // differ in where you get the token and what the username field\n // expects — same pattern as the github / gitlab branches above\n // (canonical SaaS host vs. self-hosted).\n const isCloud = host.toLowerCase() === 'bitbucket.org';\n if (isCloud) {\n return {\n title: `${host} — Bitbucket Cloud`,\n body: [\n 'Bitbucket has no first-party CLI for git-credentials, so this',\n 'is a manual one-time setup. Generate an Atlassian API token at',\n 'https://id.atlassian.com/manage-profile/security/api-tokens',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-atlassian-email>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n }\n return {\n title: `${host} — Bitbucket Data Center`,\n body: [\n 'Bitbucket has no first-party CLI for git-credentials, so this',\n 'is a manual one-time setup. Generate a personal HTTP access',\n `token in your Bitbucket UI: profile picture (top right on ${host})`,\n '→ Manage account → HTTP access tokens → Create token. Give it',\n 'at least repo-read + repo-write scopes for the repos you need.',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-bitbucket-username>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n }\n // provider === 'gitea' — Gitea is always self-hosted (gitea.com is\n // a demo / sandbox, not a SaaS), so there's no canonical-host\n // branch. The `tea` CLI exists but logs into its own config and\n // doesn't register as a git credential helper (verified against\n // https://gitea.com/gitea/tea), so we point at the UI flow + a\n // direct `git credential approve` — same pattern as Bitbucket\n // Data Center. Forgejo (the Gitea fork) shares this flow exactly.\n return {\n title: `${host} — Gitea`,\n body: [\n 'Gitea has no first-party CLI helper for git-credentials (the',\n '`tea` CLI logs into its own config, not into your git credential',\n 'helper), so this is a manual one-time setup. Generate an access',\n `token in your Gitea UI: profile picture (top right on ${host}) →`,\n 'Settings → Applications → \"Generate New Token\". Give it at',\n 'least the `read:repository` scope (add `write:repository` if you',\n 'need push from the container).',\n '',\n 'Then store it via your OS credential helper:',\n cyan(\n `git credential approve <<< $'protocol=https\\\\nhost=${host}\\\\nusername=<your-gitea-username>\\\\npassword=<token>\\\\n'`,\n ),\n ].join('\\n'),\n };\n}\n\ninterface ParsedCreds {\n username?: string;\n password?: string;\n}\n\nfunction parseCredentialFillOutput(output: string): ParsedCreds {\n const result: ParsedCreds = {};\n for (const line of output.split('\\n')) {\n const eqIdx = line.indexOf('=');\n if (eqIdx <= 0) continue;\n const key = line.slice(0, eqIdx);\n const value = line.slice(eqIdx + 1);\n if (key === 'username') result.username = value;\n if (key === 'password') result.password = value;\n }\n return result;\n}\n\nfunction formatCredentialLine(\n host: string,\n username: string,\n password: string,\n): string {\n // Both fields percent-encoded so a `@`, `:`, or `/` in the token\n // doesn't break URL parsing inside git's `store` helper.\n const encUser = encodeURIComponent(username);\n const encPass = encodeURIComponent(password);\n return `https://${encUser}:${encPass}@${host}`;\n}\n\nexport interface CollectCredentialsOptions {\n spawn?: CredentialsSpawn;\n logger?: { info: (msg: string) => void; warn: (msg: string) => void };\n}\n\nexport interface HostCredentialStatus {\n host: string;\n /**\n * Resolved provider for this host — canonical lookup for the three\n * known hosts, explicit yml hint for anything else. Carried into\n * the failure message so `formatMissingCredentialsError` can render\n * the right setup block without re-resolving.\n */\n provider: RepoProvider;\n /** 'ok' when username+password came back from `git credential fill`. */\n status: 'ok' | 'no-credentials' | 'spawn-error' | 'non-zero-exit';\n /** Diagnostic text — empty when status is 'ok'. */\n detail: string;\n}\n\nexport interface CollectCredentialsResult {\n /** Hosts for which credentials were successfully written. */\n hostsWritten: number;\n /** Hosts for which `git credential fill` failed or returned no creds. */\n hostsSkipped: number;\n /** Per-host status (in input order). */\n perHost: HostCredentialStatus[];\n /** Absolute path to the written credentials file (always written, possibly empty). */\n credentialsPath: string;\n}\n\n/**\n * For each unique HTTPS host across the dev-container's repos, ask the\n * host-side git for credentials and write them to\n * `<devContainerRoot>/.monoceros/git-credentials`. The container's\n * post-create.sh configures git to read from that file via `store`\n * credential helper.\n *\n * Host-side `git credential fill` consults whatever helper the host\n * has configured (osxkeychain on macOS, manager on Windows, libsecret\n * on Linux). If a helper has the cached credentials, returns silent.\n * If not, the helper prompts the builder via its native UI\n * (Keychain-popup, GCM-window, terminal-prompt). That's the intended\n * UX — Monoceros never prompts directly, the host's helper does.\n *\n * Always writes the file (possibly empty) so the bind-mount target\n * exists in the container. A host that returns no credentials simply\n * yields a credentials file with no matching entries, and the in-\n * container `git clone` falls back to whatever default git would do\n * (which is to prompt — and there we lose, but the diagnostic is\n * clear).\n */\nexport async function collectGitCredentials(\n devContainerRoot: string,\n hosts: readonly HostWithProvider[],\n options: CollectCredentialsOptions = {},\n): Promise<CollectCredentialsResult> {\n const credsDir = path.join(devContainerRoot, '.monoceros');\n const credentialsPath = path.join(credsDir, 'git-credentials');\n\n const spawnFn = options.spawn ?? realGitCredentialFill;\n const logger = options.logger ?? { info: () => {}, warn: () => {} };\n\n // Callers must filter out 'unknown' providers before invoking this\n // function — those should fail the apply pre-flight earlier with a\n // \"set provider:\" error, never reach the credential helper. We\n // narrow the type here for the renderer's sake.\n const lines: string[] = [];\n const perHost: HostCredentialStatus[] = [];\n for (const { host, provider } of hosts) {\n if (provider === 'unknown') {\n // Defensive: should not happen — pre-flight is supposed to\n // bail before this. Record it anyway with no-credentials so\n // the caller doesn't see a partial success.\n perHost.push({\n host,\n provider: 'github', // placeholder — never rendered because pre-flight already bailed\n status: 'no-credentials',\n detail: 'provider not declared (internal: should not reach here)',\n });\n continue;\n }\n logger.info(`Fetching credentials for ${host} from host git…`);\n const input = `protocol=https\\nhost=${host}\\n\\n`;\n let result;\n try {\n result = await spawnFn(input);\n } catch (err) {\n // No logger.warn here — the caller (apply pre-flight) renders\n // a consolidated, provider-specific error message per failing\n // host. A separate WARN line per host would just add visual\n // noise above the actionable error.\n const detail = err instanceof Error ? err.message : String(err);\n perHost.push({ host, provider, status: 'spawn-error', detail });\n continue;\n }\n if (result.exitCode !== 0) {\n perHost.push({\n host,\n provider,\n status: 'non-zero-exit',\n detail: `exit code ${result.exitCode}`,\n });\n continue;\n }\n const { username, password } = parseCredentialFillOutput(result.stdout);\n if (!username || !password) {\n perHost.push({\n host,\n provider,\n status: 'no-credentials',\n detail: 'host credential helper returned no username/password',\n });\n continue;\n }\n lines.push(formatCredentialLine(host, username, password));\n perHost.push({ host, provider, status: 'ok', detail: '' });\n }\n\n await fs.mkdir(credsDir, { recursive: true });\n await fs.writeFile(\n credentialsPath,\n lines.join('\\n') + (lines.length > 0 ? '\\n' : ''),\n {\n mode: 0o600,\n },\n );\n\n return {\n hostsWritten: lines.length,\n hostsSkipped: perHost.filter((p) => p.status !== 'ok').length,\n perHost,\n credentialsPath,\n };\n}\n\n/**\n * Expose `uniqueHttpsHosts` for callers that need the host list\n * directly (apply uses it to build the pre-flight check input).\n */\nexport { uniqueHttpsHosts };\n\n/**\n * Build the multi-host pre-flight error message that gets thrown when\n * apply discovers missing credentials. Header inlines the provider\n * for single-host cases; body is left-aligned setup instructions.\n *\n * Format:\n *\n * Missing Git credentials: <host> — <Provider>\n *\n * <setup instructions, left-aligned, multi-line>\n *\n * Then re-run `monoceros apply`.\n *\n * For multi-host failures, each block is separated by a blank line\n * and gets its own provider title.\n */\nexport function formatMissingCredentialsError(\n missing: readonly HostCredentialStatus[],\n): string {\n if (missing.length === 1) {\n const m = missing[0]!;\n const hint = providerSetupHint(m.host, m.provider);\n return [\n `Missing Git credentials: ${hint.title}`,\n '',\n hint.body,\n '',\n `Then re-run ${cyan('monoceros apply')}.`,\n ].join('\\n');\n }\n const lines: string[] = [\n `Missing Git credentials for ${missing.length} hosts:`,\n '',\n ];\n for (const m of missing) {\n const hint = providerSetupHint(m.host, m.provider);\n lines.push(hint.title);\n lines.push('');\n lines.push(hint.body);\n lines.push('');\n }\n lines.push(`Then re-run ${cyan('monoceros apply')}.`);\n return lines.join('\\n');\n}\n\n/**\n * Build the pre-flight error for repos whose host has no provider\n * declared and isn't one of the canonical ones (github.com /\n * gitlab.com / bitbucket.org). The builder needs to add a\n * `provider:` field to the yml before apply can continue.\n */\nexport function formatUnknownProviderError(hosts: readonly string[]): string {\n const sorted = [...new Set(hosts)].sort();\n const lines: string[] = [\n sorted.length === 1\n ? `Unknown Git provider for host ${sorted[0]!}.`\n : `Unknown Git provider for ${sorted.length} hosts: ${sorted.join(', ')}.`,\n '',\n 'Monoceros auto-detects only github.com / gitlab.com / bitbucket.org.',\n 'For any other host (self-hosted GitLab, Gitea, Bitbucket Server, …)',\n 'declare the provider explicitly in the yml. Edit the repo entry:',\n '',\n cyan(' repos:'),\n cyan(` - url: https://${sorted[0]!}/…`),\n cyan(' provider: gitlab # or: github, bitbucket, gitea'),\n '',\n `Or re-add with ${cyan('monoceros add-repo <name> <url> --provider=<github|gitlab|bitbucket|gitea>')}.`,\n ];\n return lines.join('\\n');\n}\n\n// Exported for tests.\nexport const _internals = {\n uniqueHttpsHosts,\n parseCredentialFillOutput,\n formatCredentialLine,\n};\n","import { spawn } from 'node:child_process';\nimport { cyan } from '../util/format.js';\n\n/**\n * Apply pre-flight stage 2: after credentials have been collected,\n * verify host-side that each declared repo URL actually resolves and\n * the stored credentials can read from it.\n *\n * The idea: `git ls-remote <url>` is a one-roundtrip probe that\n * exercises exactly the same auth path as the in-container `git clone`\n * would. If it succeeds host-side, the clone inside the container\n * will succeed too (we write the same credentials into\n * `.monoceros/git-credentials`). If it fails — repo doesn't exist,\n * token is wrong, host unreachable — we surface a per-repo error\n * BEFORE the docker build runs, saving 1–2 min of build time on\n * first apply and avoiding a noisy devcontainer-cli stack trace\n * for what's really just a typo in the URL or a missing token scope.\n *\n * This runs AFTER the credential pre-flight (`collectGitCredentials`).\n * Order matters: a missing-creds error wants a provider-specific\n * setup hint (gh / glab / Atlassian token), a present-but-wrong-creds\n * error wants a \"regenerate / fix scope\" hint. The stage-1 check\n * catches the first; this stage-2 check catches the rest.\n */\n\n/**\n * Spawn signature for `git ls-remote <url>`. Returns stdout+stderr\n * plus exit code. Injected by tests. stdout is empty on success\n * (we don't care about the ref list, just whether the call worked).\n */\nexport type ReachabilitySpawn = (url: string) => Promise<{\n stdout: string;\n stderr: string;\n exitCode: number;\n}>;\n\nconst realGitLsRemote: ReachabilitySpawn = (url) => {\n return new Promise((resolve, reject) => {\n // Same env-var hardening as credentials.ts: prevent any kind of\n // interactive prompt (terminal, GUI askpass) so the pre-flight\n // never hangs and always returns a useful exit code + stderr.\n const child = spawn('git', ['ls-remote', '--heads', '--', url], {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: {\n ...process.env,\n GIT_TERMINAL_PROMPT: '0',\n GIT_ASKPASS: '',\n SSH_ASKPASS: '',\n },\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) =>\n resolve({ stdout, stderr, exitCode: code ?? 0 }),\n );\n });\n};\n\n/**\n * Categorization of a reachability failure. Drives the per-host hint\n * in the consolidated error message. Patterns were observed across\n * GitHub, GitLab, and Bitbucket Cloud responses to non-existent /\n * unauthorized repos. We err on the side of grouping rather than\n * fine-grained kinds — the actionable advice is largely the same\n * within each kind.\n */\nexport type ReachabilityFailureKind =\n | 'not-found-or-no-access'\n | 'auth-failed'\n | 'dns'\n | 'unknown';\n\nexport interface RepoReachabilityStatus {\n url: string;\n ok: boolean;\n kind?: ReachabilityFailureKind;\n /** Raw stderr from git, trimmed. Empty when ok. */\n detail: string;\n}\n\n/**\n * Classify a non-zero `git ls-remote` failure by stderr content.\n * Patterns are matched case-insensitively against the union of\n * substrings each provider tends to emit. Order matters: DNS errors\n * sometimes also produce \"Authentication failed\" follow-up lines on\n * some platforms, so we check DNS first.\n */\nfunction classifyStderr(stderr: string): ReachabilityFailureKind {\n const s = stderr.toLowerCase();\n if (\n s.includes('could not resolve host') ||\n s.includes('name or service not known') ||\n s.includes('temporary failure in name resolution') ||\n s.includes('no address associated with hostname')\n ) {\n return 'dns';\n }\n if (\n s.includes('repository not found') ||\n s.includes('may not have access') ||\n s.includes('no longer exists') ||\n s.includes(\"don't have permission\") ||\n s.includes('could not be found') ||\n s.includes('the requested url returned error: 404')\n ) {\n return 'not-found-or-no-access';\n }\n if (\n s.includes('authentication failed') ||\n s.includes('could not read username') ||\n s.includes('incorrect username or password') ||\n s.includes('the requested url returned error: 401') ||\n s.includes('the requested url returned error: 403')\n ) {\n return 'auth-failed';\n }\n return 'unknown';\n}\n\n/**\n * Probe each declared repo URL via host-side `git ls-remote`. Runs\n * sequentially (not parallel) so the output order matches the yml\n * order — easier to reason about when multiple repos fail, and the\n * total time is bounded by ~200 ms per repo against typical SaaS\n * hosts. Spawn-injected for tests.\n */\nexport async function checkRepoReachability(\n repos: readonly { url: string }[],\n options: { spawn?: ReachabilitySpawn } = {},\n): Promise<RepoReachabilityStatus[]> {\n const spawnFn = options.spawn ?? realGitLsRemote;\n const results: RepoReachabilityStatus[] = [];\n for (const repo of repos) {\n // Only HTTPS URLs reach this code path (schema enforces it; pre-\n // flight already filtered). Skip belt-and-suspenders is in\n // credentials.ts — here we trust the input.\n let result: Awaited<ReturnType<ReachabilitySpawn>>;\n try {\n result = await spawnFn(repo.url);\n } catch (err) {\n results.push({\n url: repo.url,\n ok: false,\n kind: 'unknown',\n detail: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n if (result.exitCode === 0) {\n results.push({ url: repo.url, ok: true, detail: '' });\n continue;\n }\n results.push({\n url: repo.url,\n ok: false,\n kind: classifyStderr(result.stderr),\n detail: result.stderr.trim(),\n });\n }\n return results;\n}\n\n/**\n * Render the consolidated pre-flight error for repos that couldn't\n * be reached. Groups failures by kind so each kind's actionable\n * advice appears once, with the failing URLs listed underneath.\n *\n * Layout:\n *\n * Cannot reach <N> declared repo(s):\n *\n * Repository not found (or your credentials don't grant access):\n * • https://...\n * • https://...\n * - <actionable advice>\n * - <actionable advice>\n *\n * Authentication failed:\n * • https://...\n * - <actionable advice>\n *\n * Then re-run `monoceros apply`.\n */\nexport function formatUnreachableReposError(\n failures: readonly RepoReachabilityStatus[],\n): string {\n const byKind = new Map<ReachabilityFailureKind, RepoReachabilityStatus[]>();\n for (const f of failures) {\n const kind = f.kind ?? 'unknown';\n const list = byKind.get(kind) ?? [];\n list.push(f);\n byKind.set(kind, list);\n }\n const totalUrls = failures.length;\n const lines: string[] = [\n totalUrls === 1\n ? `Cannot reach declared repo: ${failures[0]!.url}`\n : `Cannot reach ${totalUrls} declared repos:`,\n '',\n ];\n\n const sectionOrder: ReachabilityFailureKind[] = [\n 'not-found-or-no-access',\n 'auth-failed',\n 'dns',\n 'unknown',\n ];\n for (const kind of sectionOrder) {\n const entries = byKind.get(kind);\n if (!entries || entries.length === 0) continue;\n lines.push(headerForKind(kind));\n for (const e of entries) {\n lines.push(` • ${e.url}`);\n }\n for (const advice of adviceForKind(kind)) {\n lines.push(` - ${advice}`);\n }\n lines.push('');\n }\n lines.push(`Then re-run ${cyan('monoceros apply')}.`);\n return lines.join('\\n');\n}\n\nfunction headerForKind(kind: ReachabilityFailureKind): string {\n switch (kind) {\n case 'not-found-or-no-access':\n return \"Repository not found (or your credentials don't grant access):\";\n case 'auth-failed':\n return 'Authentication failed (credentials are present but rejected):';\n case 'dns':\n return \"Host unreachable (DNS / VPN / offline — git couldn't resolve the hostname):\";\n case 'unknown':\n return 'Unrecognised git error:';\n }\n}\n\nfunction adviceForKind(kind: ReachabilityFailureKind): string[] {\n switch (kind) {\n case 'not-found-or-no-access':\n return [\n 'Re-check the URL for typos (case-sensitive on most hosts).',\n 'Verify the repo still exists / is not archived in a way that hides it.',\n 'Ensure your token covers this org / workspace and has read scope (GitHub: `repo`; GitLab: `read_repository`; Bitbucket: repo read).',\n ];\n case 'auth-failed':\n return [\n 'Token may be expired or revoked — regenerate it from the provider UI.',\n 'Re-run the provider CLI login (gh auth login / glab auth login) — Monoceros picks up the refreshed token on the next apply.',\n ];\n case 'dns':\n return [\n 'Check your internet / VPN — corporate Git hosts often require VPN.',\n 'Verify the hostname spelling in the yml.',\n ];\n case 'unknown':\n return [\n 'Run `git ls-remote <url>` manually on the host to see the raw error.',\n ];\n }\n}\n","import { spawn } from 'node:child_process';\n\n/**\n * Whether the host's docker daemon runs as root (rootful) or under a\n * user namespace (rootless). The mode decides whether bind-mounts\n * need the `idmap` option.\n *\n * Why this matters: in rootless Docker the host user's UID is mapped\n * to container UID 0 (root), and the container's non-root user\n * (uid 1000 inside) lives at a shifted host UID (uid 65536+1000+ via\n * /etc/subuid). Without `idmap`, files written by either side end up\n * with the wrong ownership on the other — host can't read what the\n * container created, container's node user can't write into what the\n * host pre-created (which is what bit M5 testing on Ubuntu rootless).\n *\n * The Linux kernel supports `idmap` as a bind-mount option since 5.12;\n * Docker exposes it since 25.x. Ubuntu 24.04 and other modern distros\n * are well past both. Older kernels (RHEL 8 with 4.18) would fail the\n * mount with an \"unsupported\" error — accepted trade-off, the error\n * surfaces clearly.\n *\n * On macOS / Windows Docker Desktop, idmap is a no-op at best and a\n * mount-error at worst because those platforms use their own\n * file-sharing layer (VirtioFS / WSL2 + Plan9) instead of native\n * Linux bind mounts. We MUST only emit idmap when the daemon is\n * actually rootless on Linux — otherwise we'd break the working\n * Mac/Windows cases.\n */\nexport type DockerMode = 'rootful' | 'rootless';\n\n/**\n * Spawn signature for `docker info`. Returns stdout + exit code.\n * Injected by tests.\n */\nexport type DockerInfoSpawn = () => Promise<{\n stdout: string;\n exitCode: number;\n}>;\n\nconst realDockerInfo: DockerInfoSpawn = () => {\n return new Promise((resolve, reject) => {\n // `--format '{{json .SecurityOptions}}'` returns a JSON array like\n // `[\"name=seccomp,profile=builtin\",\"name=rootless\"]` on rootless,\n // or `[\"name=seccomp,profile=builtin\"]` on rootful. Cheaper to\n // parse than the full human-readable `docker info` output.\n const child = spawn(\n 'docker',\n ['info', '--format', '{{json .SecurityOptions}}'],\n {\n stdio: ['ignore', 'pipe', 'inherit'],\n },\n );\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) => resolve({ stdout, exitCode: code ?? 0 }));\n });\n};\n\n/**\n * Probe the host docker daemon and return its mode. Defaults to\n * `'rootful'` whenever we can't reliably determine otherwise — the\n * downstream `docker run` would surface a clearer error if the\n * daemon is unreachable, so we don't pre-emptively fail here.\n */\nexport async function detectDockerMode(\n options: { spawn?: DockerInfoSpawn } = {},\n): Promise<DockerMode> {\n const spawnFn = options.spawn ?? realDockerInfo;\n try {\n const result = await spawnFn();\n if (result.exitCode !== 0) return 'rootful';\n // Match both the bare `rootless` token and the modern\n // `name=rootless` form. Case-insensitive to be defensive against\n // future docker output tweaks.\n return /\\brootless\\b/i.test(result.stdout) ? 'rootless' : 'rootful';\n } catch {\n return 'rootful';\n }\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\n\n/**\n * Spawn signature for `git config --global --get <key>`: takes the\n * key, returns stdout (trimmed) and exit code. Exit code 1 with empty\n * stdout means \"no value set\" — that's how git signals an unset key.\n * Injected by tests.\n */\nexport type IdentitySpawn = (\n key: string,\n) => Promise<{ value: string; exitCode: number }>;\n\n/**\n * Async prompt for a single identity key. Used as a fallback when the\n * host has no `--global` identity and `.monoceros/gitconfig` has no\n * persisted value from an earlier run. Returns the entered value or\n * `undefined` if the builder skips.\n */\nexport type IdentityPrompt = (\n key: 'user.name' | 'user.email',\n) => Promise<string | undefined>;\n\nconst realGitConfigGet: IdentitySpawn = (key) => {\n return new Promise((resolve, reject) => {\n const child = spawn('git', ['config', '--global', '--get', key], {\n stdio: ['ignore', 'pipe', 'inherit'],\n });\n let stdout = '';\n child.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.on('error', reject);\n child.on('exit', (code) =>\n resolve({ value: stdout.trim(), exitCode: code ?? 0 }),\n );\n });\n};\n\nconst realIdentityPrompt: IdentityPrompt = async (key) => {\n // Non-interactive (CI, scripts): never hang waiting for input. The\n // identity stays unset; builder fixes it later by setting host\n // `git config --global` or editing `.monoceros/gitconfig` directly.\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n return undefined;\n }\n const label =\n key === 'user.name'\n ? 'Git user.name for this dev container (full name)'\n : 'Git user.email for this dev container';\n const value = await consola.prompt(`${label}:`, { type: 'text' });\n if (typeof value !== 'string') return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n};\n\nexport interface CollectIdentityOptions {\n spawn?: IdentitySpawn;\n /**\n * Fallback prompt when the host has no `--global` identity and\n * `.monoceros/gitconfig` has no persisted value either. Tests inject\n * a canned answer; production uses an interactive `consola.prompt`\n * that auto-skips in non-interactive contexts.\n */\n prompt?: IdentityPrompt;\n /**\n * Per-container override from the container's yml `git.user`. Wins\n * over everything else (host global, workbench-wide defaults,\n * persisted state, interactive prompt).\n */\n containerOverride?: { name?: string; email?: string };\n /**\n * Workbench-wide defaults from `<MONOCEROS_HOME>/monoceros-config.yml`\n * `defaults.git.user`. Wins over host global git config (the\n * monoceros-config.yml is an explicit builder choice for Monoceros\n * containers; host global is the catch-all default), loses to the\n * per-container override.\n */\n defaults?: { name?: string; email?: string };\n logger?: { info: (msg: string) => void; warn: (msg: string) => void };\n}\n\nexport interface CollectIdentityResult {\n name?: string;\n email?: string;\n gitconfigPath: string;\n}\n\n/**\n * Extract `user.name` and `user.email` from the host's global git\n * config, write them as `<devContainerRoot>/.monoceros/gitconfig` for\n * the container to include. Done both at `monoceros create` time (so\n * the first `start` has identity) and at every `monoceros apply` (so\n * host changes propagate in).\n *\n * Always writes the file, even when host has nothing set — keeps the\n * include.path target valid (git silently ignores missing files, but\n * present-but-empty is more deterministic).\n *\n * Returns the captured values; the caller can use them for logging.\n * Missing values surface as `undefined`, plus a warn log line.\n */\nexport async function collectGitIdentity(\n devContainerRoot: string,\n options: CollectIdentityOptions = {},\n): Promise<CollectIdentityResult> {\n const gitconfigDir = path.join(devContainerRoot, '.monoceros');\n const gitconfigPath = path.join(gitconfigDir, 'gitconfig');\n const spawnFn = options.spawn ?? realGitConfigGet;\n const promptFn = options.prompt ?? realIdentityPrompt;\n const logger = options.logger ?? { info: () => {}, warn: () => {} };\n\n const existing = await readExistingGitconfig(gitconfigPath);\n\n // Resolution order per key:\n // 1. containerOverride (yml's `git.user`)\n // 2. defaults (monoceros-config.yml's `defaults.git.user`)\n // 3. host `git config --global --get <key>`\n // 4. previously persisted value (.monoceros/gitconfig)\n // 5. interactive prompt (skipped in non-TTY contexts)\n const name = await resolveKey('user.name', {\n override: options.containerOverride?.name,\n defaultValue: options.defaults?.name,\n spawnFn,\n persistedValue: existing.name,\n promptFn,\n logger,\n });\n const email = await resolveKey('user.email', {\n override: options.containerOverride?.email,\n defaultValue: options.defaults?.email,\n spawnFn,\n persistedValue: existing.email,\n promptFn,\n logger,\n });\n\n const lines: string[] = ['[user]'];\n if (name !== undefined) lines.push(`\\tname = ${name}`);\n if (email !== undefined) lines.push(`\\temail = ${email}`);\n\n await fs.mkdir(gitconfigDir, { recursive: true });\n await fs.writeFile(gitconfigPath, lines.join('\\n') + '\\n');\n\n return {\n ...(name !== undefined ? { name } : {}),\n ...(email !== undefined ? { email } : {}),\n gitconfigPath,\n };\n}\n\ninterface ResolveKeyOpts {\n override?: string;\n defaultValue?: string;\n spawnFn: IdentitySpawn;\n persistedValue?: string;\n promptFn: IdentityPrompt;\n logger: { warn: (msg: string) => void };\n}\n\nasync function resolveKey(\n key: 'user.name' | 'user.email',\n opts: ResolveKeyOpts,\n): Promise<string | undefined> {\n if (opts.override !== undefined && opts.override.length > 0) {\n return opts.override;\n }\n if (opts.defaultValue !== undefined && opts.defaultValue.length > 0) {\n return opts.defaultValue;\n }\n const hostValue = await readKeyFromHost(opts.spawnFn, key, opts.logger);\n if (hostValue !== undefined) return hostValue;\n if (opts.persistedValue !== undefined && opts.persistedValue.length > 0) {\n return opts.persistedValue;\n }\n const prompted = await opts.promptFn(key);\n if (prompted !== undefined) return prompted;\n opts.logger.warn(\n `No ${key} resolvable (yml override, monoceros-config.yml defaults, host \\`git config --global\\`, persisted .monoceros/gitconfig, prompt). Container git will have no ${key} until set explicitly.`,\n );\n return undefined;\n}\n\nasync function readKeyFromHost(\n spawnFn: IdentitySpawn,\n key: string,\n logger: { warn: (msg: string) => void },\n): Promise<string | undefined> {\n try {\n const result = await spawnFn(key);\n if (result.exitCode === 0 && result.value.length > 0) {\n return result.value;\n }\n return undefined;\n } catch (err) {\n logger.warn(\n `Host git not runnable (${err instanceof Error ? err.message : String(err)}); identity not captured.`,\n );\n return undefined;\n }\n}\n\nasync function readExistingGitconfig(\n filePath: string,\n): Promise<{ name?: string; email?: string }> {\n try {\n const content = await fs.readFile(filePath, 'utf8');\n const result: { name?: string; email?: string } = {};\n const nameMatch = /^\\s*name\\s*=\\s*(.+?)\\s*$/m.exec(content);\n const emailMatch = /^\\s*email\\s*=\\s*(.+?)\\s*$/m.exec(content);\n if (nameMatch?.[1]) result.name = nameMatch[1];\n if (emailMatch?.[1]) result.email = emailMatch[1];\n return result;\n } catch {\n return {};\n }\n}\n","// Single source of truth for the CLI version: `packages/cli/package.json`.\n//\n// At build time tsup (`tsup.config.ts`) reads `package.json.version` and\n// substitutes the `__CLI_VERSION__` placeholder below. So bumping the\n// version means editing exactly one file — package.json — and rebuilding.\n//\n// In dev (vitest, tsc) the placeholder isn't replaced; the fallback\n// `'dev'` kicks in. Tests don't depend on the exact version string.\n\ndeclare const __CLI_VERSION__: string;\n\nexport const CLI_VERSION =\n typeof __CLI_VERSION__ === 'string' ? __CLI_VERSION__ : 'dev';\n","import { consola } from 'consola';\n\n// Shared exit-code dispatcher: runs the orchestrator, propagates its\n// exit code, and turns thrown errors into a clean console message + exit 1.\nexport async function dispatch(runner: () => Promise<number>): Promise<never> {\n try {\n const exitCode = await runner();\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n","import { defineCommand } from 'citty';\n\n/**\n * `monoceros completion <shell>` — prints a shell-completion script\n * for bash or zsh to stdout. The user redirects it into a file their\n * shell loads at startup.\n *\n * Both scripts complete:\n * - subcommand names at position 1\n * - container names (read from `<MONOCEROS_HOME>/container-configs/`)\n * for the second positional of every command that takes a\n * `<NAME>` argument referring to an existing container — i.e.\n * everything *except* `init` (which expects a fresh name) and the\n * verb-only commands like `list-components` / `completion`.\n *\n * MONOCEROS_HOME respects the same precedence as the CLI itself: env\n * var first, then `$HOME/.monoceros`. Container-name completion in\n * the workbench-checkout dev environment looks at the env var if set;\n * otherwise it falls back to `~/.monoceros/`, which matches the\n * global-install case. A contributor who wants dev-container names\n * completed sets `MONOCEROS_HOME=$PWD/.local` in their shell.\n *\n * Install:\n * bash: monoceros completion bash > ~/.bash_completion.d/monoceros\n * (or any path your shell sources; `source` it from .bashrc)\n * zsh: monoceros completion zsh > \"${fpath[1]}/_monoceros\"\n * (after ensuring compinit is active)\n */\n\n// Keep these arrays in sync with main.ts. Single source of truth\n// would be nice but adds startup cost — citty's subCommands aren't\n// trivial to enumerate from a static context. Tests guard the\n// list in completion.test.ts.\nconst ALL_COMMANDS = [\n 'init',\n 'list-components',\n 'shell',\n 'run',\n 'logs',\n 'start',\n 'stop',\n 'status',\n 'apply',\n 'remove',\n 'restore',\n 'add-service',\n 'add-language',\n 'add-apt-packages',\n 'add-feature',\n 'add-from-url',\n 'add-repo',\n 'remove-service',\n 'remove-language',\n 'remove-apt-packages',\n 'remove-feature',\n 'remove-from-url',\n 'remove-repo',\n 'completion',\n] as const;\n\n// Commands whose first positional is an existing container name.\n// Everything else either takes no positional (`list-components`,\n// `completion`) or expects a fresh name (`init`, `restore`).\nconst COMMANDS_WITH_CONTAINER_ARG = [\n 'shell',\n 'run',\n 'logs',\n 'start',\n 'stop',\n 'status',\n 'apply',\n 'remove',\n 'add-service',\n 'add-language',\n 'add-apt-packages',\n 'add-feature',\n 'add-from-url',\n 'add-repo',\n 'remove-service',\n 'remove-language',\n 'remove-apt-packages',\n 'remove-feature',\n 'remove-from-url',\n 'remove-repo',\n] as const;\n\nconst SHELLS = ['bash', 'zsh', 'pwsh'] as const;\ntype Shell = (typeof SHELLS)[number];\n\nexport function renderCompletionScript(shell: Shell): string {\n const commands = ALL_COMMANDS.join(' ');\n const containerCommandsRegex = COMMANDS_WITH_CONTAINER_ARG.join('|');\n\n if (shell === 'bash') {\n return [\n '# bash completion for monoceros',\n '# install: source this file from .bashrc, e.g.',\n '# monoceros completion bash > ~/.bash_completion.d/monoceros',\n '# echo \"source ~/.bash_completion.d/monoceros\" >> ~/.bashrc',\n '',\n '_monoceros() {',\n ' local cur prev cmd home configs_dir names',\n ' cur=\"${COMP_WORDS[COMP_CWORD]}\"',\n '',\n ' if [[ $COMP_CWORD -eq 1 ]]; then',\n ` COMPREPLY=( $(compgen -W \"${commands}\" -- \"$cur\") )`,\n ' return',\n ' fi',\n '',\n ' cmd=\"${COMP_WORDS[1]}\"',\n ' if [[ $COMP_CWORD -eq 2 ]]; then',\n ' case \"$cmd\" in',\n ` ${containerCommandsRegex})`,\n ' home=\"${MONOCEROS_HOME:-$HOME/.monoceros}\"',\n ' configs_dir=\"$home/container-configs\"',\n ' if [[ -d \"$configs_dir\" ]]; then',\n ` names=$(cd \"$configs_dir\" && ls *.yml 2>/dev/null | sed 's/\\\\.yml$//')`,\n ' COMPREPLY=( $(compgen -W \"$names\" -- \"$cur\") )',\n ' fi',\n ' ;;',\n ' completion)',\n ` COMPREPLY=( $(compgen -W \"${SHELLS.join(' ')}\" -- \"$cur\") )`,\n ' ;;',\n ' esac',\n ' fi',\n '}',\n 'complete -F _monoceros monoceros',\n '',\n ].join('\\n');\n }\n\n if (shell === 'pwsh') {\n return [\n '# PowerShell completion for monoceros',\n '# install: dot-source this file from your $PROFILE, e.g.',\n '# monoceros completion pwsh > $HOME/.config/monoceros/completion.ps1',\n \"# Add-Content $PROFILE '. $HOME/.config/monoceros/completion.ps1'\",\n '',\n 'Register-ArgumentCompleter -Native -CommandName monoceros -ScriptBlock {',\n ' param($wordToComplete, $commandAst, $cursorPosition)',\n '',\n ' $commands = @(',\n ...ALL_COMMANDS.map((c) => ` '${c}'`),\n ' )',\n ` $shells = @('${SHELLS.join(\"', '\")}')`,\n ' $containerCommands = @(',\n ...COMMANDS_WITH_CONTAINER_ARG.map((c) => ` '${c}'`),\n ' )',\n '',\n ' $tokens = $commandAst.CommandElements',\n ' $position = $tokens.Count',\n ' if ($wordToComplete) { $position-- }',\n '',\n ' if ($position -eq 1) {',\n ' $commands | Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' return',\n ' }',\n '',\n ' if ($position -eq 2) {',\n ' $cmd = $tokens[1].Value',\n ' if ($containerCommands -contains $cmd) {',\n ' $home = if ($env:MONOCEROS_HOME) { $env:MONOCEROS_HOME } else { Join-Path $env:USERPROFILE \".monoceros\" }',\n ' $configsDir = Join-Path $home \"container-configs\"',\n ' if (Test-Path $configsDir) {',\n ' Get-ChildItem -Path $configsDir -Filter \"*.yml\" |',\n ' ForEach-Object { $_.BaseName } |',\n ' Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' }',\n ' } elseif ($cmd -eq \"completion\") {',\n ' $shells | Where-Object { $_ -like \"$wordToComplete*\" } |',\n ' ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, \"ParameterValue\", $_) }',\n ' }',\n ' }',\n '}',\n '',\n ].join('\\n');\n }\n\n // zsh\n return [\n '#compdef monoceros',\n '# zsh completion for monoceros',\n '# install: drop this file somewhere on your $fpath as `_monoceros`,',\n '# then start a new shell (or run `compinit`). Example:',\n '# monoceros completion zsh > \"${fpath[1]}/_monoceros\"',\n '',\n '_monoceros() {',\n ' local -a commands shells',\n ' commands=(',\n ...ALL_COMMANDS.map((c) => ` '${c}'`),\n ' )',\n ` shells=(${SHELLS.map((s) => `'${s}'`).join(' ')})`,\n '',\n ' if (( CURRENT == 2 )); then',\n \" _describe 'monoceros command' commands\",\n ' return',\n ' fi',\n '',\n ' local cmd=${words[2]}',\n ' if (( CURRENT == 3 )); then',\n ' case $cmd in',\n ` ${containerCommandsRegex})`,\n ' local home=\"${MONOCEROS_HOME:-$HOME/.monoceros}\"',\n ' local configs_dir=\"$home/container-configs\"',\n ' if [[ -d $configs_dir ]]; then',\n ' local -a names',\n ' names=(${configs_dir}/*.yml(N:t:r))',\n \" _describe 'container' names\",\n ' fi',\n ' ;;',\n ' completion)',\n \" _describe 'shell' shells\",\n ' ;;',\n ' esac',\n ' fi',\n '}',\n '',\n '_monoceros \"$@\"',\n '',\n ].join('\\n');\n}\n\nexport const completionCommand = defineCommand({\n meta: {\n name: 'completion',\n group: 'tooling',\n description:\n 'Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically.',\n },\n args: {\n shell: {\n type: 'positional',\n description: \"Target shell. One of: 'bash', 'zsh', 'pwsh'.\",\n required: true,\n },\n },\n run({ args }) {\n const shell = args.shell as string;\n if (shell !== 'bash' && shell !== 'zsh' && shell !== 'pwsh') {\n process.stderr.write(\n `Unknown shell: ${JSON.stringify(shell)}. Supported: ${SHELLS.join(', ')}.\\n`,\n );\n process.exit(2);\n }\n process.stdout.write(renderCompletionScript(shell));\n },\n});\n\n// Exposed for tests so the static command list stays in sync with\n// what main.ts wires up.\nexport const COMPLETION_COMMANDS_FOR_TEST = ALL_COMMANDS;\nexport const COMPLETION_CONTAINER_COMMANDS_FOR_TEST =\n COMMANDS_WITH_CONTAINER_ARG;\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runInit } from '../init/index.js';\n\nexport const initCommand = defineCommand({\n meta: {\n name: 'init',\n group: 'lifecycle',\n description:\n 'Create a fresh container-config yml at .local/container-configs/<name>.yml. Without --with, the file is a documented default with every component commented out. With --with=<names>, the named components are composed into an active, immediately-applyable yml. Then run `monoceros apply <name>`.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Config name. The yml lands at <MONOCEROS_HOME>/container-configs/<name>.yml and becomes the source-of-truth for `monoceros apply <name>`.',\n required: true,\n },\n with: {\n type: 'string',\n description:\n \"Comma-separated list of component names to compose, e.g. 'node,postgres,github,claude'. Sub-components use a slash, e.g. 'atlassian/twg'. When omitted, init writes a documented default with every catalog component commented out.\",\n required: false,\n },\n 'with-repo': {\n type: 'string',\n description:\n 'Git URL of a repo to clone into projects/ on first apply. Repeatable: pass --with-repo=URL1 --with-repo=URL2 for multiple repos. Folder name derived from URL (foo.git → projects/foo/); use `monoceros add-repo --path=...` post-init for subfolder paths.',\n required: false,\n },\n },\n async run({ args, rawArgs }) {\n try {\n const withList = collectWithList(args.with, rawArgs);\n const withRepoList = collectWithRepoList(rawArgs);\n await runInit({\n name: args.name,\n ...(withList ? { with: withList } : {}),\n ...(withRepoList.length > 0 ? { withRepo: withRepoList } : {}),\n });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n\n/**\n * Collect all `--with-repo=<url>` and `--with-repo <url>` tokens from\n * rawArgs. citty doesn't natively give us repeatable strings (the\n * single `args['with-repo']` only carries the last value), so we\n * scan the original argv. Returns URLs in order of appearance.\n */\nfunction collectWithRepoList(rawArgs: string[]): string[] {\n const urls: string[] = [];\n for (let i = 0; i < rawArgs.length; i += 1) {\n const t = rawArgs[i]!;\n if (t === '--with-repo') {\n const next = rawArgs[i + 1];\n if (typeof next === 'string' && !next.startsWith('-')) {\n urls.push(next);\n i += 1;\n }\n } else if (t.startsWith('--with-repo=')) {\n urls.push(t.slice('--with-repo='.length));\n }\n }\n return urls;\n}\n\n/**\n * Reconstruct the --with list from `args.with` plus any rawArgs\n * tokens that the shell tokenization split off.\n *\n * Background: a user writing\n * monoceros init dummy --with=a, b, c\n * gets shell-tokenized into argv entries:\n * ['init', 'dummy', '--with=a,', 'b,', 'c']\n * citty assigns `args.with = \"a,\"` and the rest float as extra\n * positionals that the `name` arg won't accept. To avoid forcing\n * the user to quote or remove the spaces, we look at rawArgs to\n * find the original --with token and pull in any subsequent non-\n * flag tokens until we hit something that looks like a flag or\n * run out. The collected pieces are joined back with commas and\n * re-split — same parser as before, but now seeing the full list.\n */\nfunction collectWithList(\n withArg: string | undefined,\n rawArgs: string[],\n): string[] | undefined {\n if (typeof withArg !== 'string' || withArg.trim().length === 0) {\n return undefined;\n }\n let combined = withArg.trim();\n // Find where --with starts in rawArgs, then keep eating non-flag\n // tokens. Both forms are supported by citty:\n // --with=value (combined in one token)\n // --with value (two tokens)\n const startIdx = rawArgs.findIndex(\n (t) => t === '--with' || t.startsWith('--with='),\n );\n if (startIdx >= 0) {\n // Skip the with token itself, plus its detached value when\n // `--with` was used in the two-token form.\n let scanFrom = startIdx + 1;\n if (rawArgs[startIdx] === '--with') scanFrom += 1;\n for (let i = scanFrom; i < rawArgs.length; i += 1) {\n const t = rawArgs[i]!;\n if (t.startsWith('--') || t === '-h' || t === '--help') break;\n // Re-join with a comma — the user separated with commas plus\n // (now-eaten) whitespace; comma alone is what our parser wants.\n const sep = combined.endsWith(',') ? '' : ',';\n combined += sep + t;\n }\n }\n const pieces = combined\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return pieces.length > 0 ? pieces : undefined;\n}\n","import { existsSync, promises as fs } from 'node:fs';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerConfigsDir,\n monocerosHome as defaultMonocerosHome,\n workbenchRoot as defaultWorkbenchRoot,\n workbenchCheckoutRoot,\n componentsDir as defaultComponentsDir,\n prettyPath,\n} from '../config/paths.js';\nimport { KNOWN_PROVIDER_HOSTS, REGEX } from '../config/schema.js';\nimport { loadComponentCatalog, resolveComponents } from './components.js';\nimport { generateComposedYml, generateDocumentedYml } from './generator.js';\nimport { loadFeatureManifestSummary } from './manifest.js';\n\n/**\n * `monoceros init <name> [--with=<components>]` — produce a fresh\n * container-config yml at `<MONOCEROS_HOME>/container-configs/<name>.yml`.\n *\n * Two modes:\n *\n * - With `--with=node,postgres,github,claude` (or any comma-list\n * of component names from the catalog): the listed components\n * are merged and the result written as an active, immediately-\n * applyable yml. Per-feature option hints (auth/credentials\n * from the feature manifest) appear as commented lines next to\n * the active options so the builder can see what's available\n * without leaving the file.\n *\n * - Without `--with`: a documented-default yml is written. Every\n * section is commented out, every catalog component appears as\n * a suggestion with prose describing what it adds. Builder\n * un-comments what they want, then `monoceros apply <name>`.\n *\n * Errors loudly if:\n *\n * - the target config already exists (delete it first if you want\n * to start over — protects hand-edits)\n * - a `--with` name is not in the catalog (the error message\n * lists what *is* available)\n * - the chosen container name is shape-invalid\n */\n\nexport interface RunInitOptions {\n name: string;\n /**\n * Component names to compose. When empty/undefined → documented\n * mode (every component commented out). When set → composed mode\n * with exactly these components active.\n */\n with?: string[];\n /**\n * Git URLs to clone into `projects/` on the first apply. Each URL\n * lands at `projects/<URL-derived-leaf>/` (e.g.\n * `https://.../foo.git` → `projects/foo/`). For nested destination\n * paths (`projects/apps/web/`) use `monoceros add-repo --path=...`\n * post-init — the init flag intentionally keeps the syntax minimal.\n */\n withRepo?: string[];\n /** Override of the CLI-bundle root that holds `templates/components/`. */\n workbenchRoot?: string;\n /** Override of the user-data home that owns `container-configs/`. */\n monocerosHome?: string;\n logger?: {\n success: (msg: string) => void;\n info: (msg: string) => void;\n };\n}\n\nexport interface RunInitResult {\n configPath: string;\n /** True when the documented-default mode was used. */\n documented: boolean;\n}\n\nexport async function runInit(opts: RunInitOptions): Promise<RunInitResult> {\n const workbench = opts.workbenchRoot ?? defaultWorkbenchRoot();\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n success: (msg) => consola.success(msg),\n info: (msg) => consola.info(msg),\n };\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const dest = containerConfigPath(opts.name, home);\n if (existsSync(dest)) {\n throw new Error(\n `Config already exists: ${dest}. Delete it manually before re-running \\`monoceros init\\` — this protects any hand-edits.`,\n );\n }\n\n const catalog = await loadComponentCatalog(defaultComponentsDir(workbench));\n if (catalog.size === 0) {\n throw new Error(\n `No components found under ${defaultComponentsDir(workbench)}. The workbench checkout is incomplete.`,\n );\n }\n\n // Feature manifests live at the workbench-checkout root, not in\n // the CLI bundle. In tests the fixture sets `workbenchRoot` to a\n // dir that happens to hold both the templates *and* an\n // `images/features/` tree; honour that override. In real use we\n // fall back to `workbenchCheckoutRoot()` which returns null when\n // the CLI is run from an npm install — manifest lookups then\n // return undefined and init renders without optionHints.\n const checkoutRoot = opts.workbenchRoot ?? workbenchCheckoutRoot();\n const lookup = (ref: string) => loadFeatureManifestSummary(ref, checkoutRoot);\n\n // --with-repo URL validation: only canonical hosts. Non-canonical\n // hosts (self-hosted GitLab, Gitea, …) need `provider:` in the yml,\n // and init has no --provider flag, so the builder takes the\n // `monoceros init` + `monoceros add-repo … --provider=…` path\n // instead.\n // Dedupe input URLs (preserve insertion order) — same URL passed\n // twice from the CLI is a no-op, matching how `monoceros add-repo`\n // treats the second-add case.\n const reposRaw = (opts.withRepo ?? [])\n .map((u) => u.trim())\n .filter((u) => u.length > 0);\n const repos: string[] = [];\n const seenRepoUrls = new Set<string>();\n for (const url of reposRaw) {\n if (seenRepoUrls.has(url)) continue;\n seenRepoUrls.add(url);\n repos.push(url);\n }\n if (repos.length > 0) {\n const offending: string[] = [];\n for (const url of repos) {\n let host: string | undefined;\n try {\n host = url.startsWith('https://') ? new URL(url).hostname : undefined;\n } catch {\n host = undefined;\n }\n if (!host || !KNOWN_PROVIDER_HOSTS[host.toLowerCase()]) {\n offending.push(url);\n }\n }\n if (offending.length > 0) {\n throw new Error(\n [\n `--with-repo only supports github.com / gitlab.com / bitbucket.org URLs.`,\n `These are not canonical: ${offending.join(', ')}`,\n `For other hosts, run \\`monoceros init <name>\\` first, then`,\n `\\`monoceros add-repo <name> <url> --provider=github|gitlab|bitbucket\\`.`,\n ].join('\\n'),\n );\n }\n }\n\n // Both generators take the URL list directly — no AST round-trip\n // after the fact. That lets each generator decide how to render the\n // repos block (commented hints in documented mode, active entries\n // with commented per-entry hint lines in composed mode), keeping\n // the \"all available options visible\" rule consistent across\n // sections.\n let text: string;\n const requested = opts.with ?? [];\n if (requested.length === 0) {\n text = generateDocumentedYml(opts.name, catalog, lookup, repos);\n } else {\n const components = resolveComponents(catalog, requested);\n text = generateComposedYml(opts.name, components, lookup, repos);\n }\n\n await fs.mkdir(containerConfigsDir(home), { recursive: true });\n await fs.writeFile(dest, text, 'utf8');\n\n const documented = requested.length === 0;\n const displayPath = prettyPath(dest);\n if (documented) {\n logger.success(\n `Wrote documented default to ${displayPath}. Un-comment what you need, then \\`monoceros apply ${opts.name}\\`.`,\n );\n } else {\n logger.success(\n `Composed ${requested.length} component(s) into ${displayPath}: ${requested.join(', ')}`,\n );\n logger.info(\n `Edit the file if you need to tweak, then \\`monoceros apply ${opts.name}\\`.`,\n );\n }\n\n return { configPath: dest, documented };\n}\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport { parse as parseYaml } from 'yaml';\nimport { componentsDir as defaultComponentsDir } from '../config/paths.js';\nimport { FeatureOptionValueSchema, REGEX } from '../config/schema.js';\n\n/**\n * Components catalog — small, composable yml snippets that\n * `monoceros init` can merge into a container config.\n *\n * Each file under `templates/components/` is one component:\n *\n * - `templates/components/node.yml` → component name `node`\n * - `templates/components/atlassian/twg.yml` → component name\n * `atlassian/twg`\n *\n * Sub-components live inside a directory whose name matches a parent\n * component (and which may itself have a top-level `<group>.yml`,\n * e.g. `atlassian.yml` for the \"both tools\" preset). The convention\n * is: a sub-component sets every sibling boolean option explicitly\n * (`true` for its own feature, `false` for the others), and the\n * merge applies OR-semantics on booleans so combining\n * `--with=atlassian/rovodev,atlassian/twg` correctly yields both\n * `true`. See `templates/components/README.md` for the full design.\n */\n\nconst CategorySchema = z.enum(['language', 'service', 'feature']);\nexport type ComponentCategory = z.infer<typeof CategorySchema>;\n\nconst FeatureContributionSchema = z.object({\n ref: z.string().regex(REGEX.featureRef),\n options: z.record(z.string(), FeatureOptionValueSchema).optional(),\n});\n\n/**\n * Shape validation for one component file. The contributes section is\n * deliberately narrow — exactly one of languages/services/features may\n * be set, and it must line up with the declared category.\n */\nconst ComponentFileSchema = z\n .object({\n displayName: z.string().min(1),\n description: z.string().min(1),\n category: CategorySchema,\n contributes: z.object({\n languages: z.array(z.string().min(1)).optional(),\n services: z.array(z.string().min(1)).optional(),\n features: z.array(FeatureContributionSchema).optional(),\n }),\n })\n .superRefine((data, ctx) => {\n const c = data.contributes;\n const filled = [\n c.languages && c.languages.length > 0 ? 'languages' : null,\n c.services && c.services.length > 0 ? 'services' : null,\n c.features && c.features.length > 0 ? 'features' : null,\n ].filter((x): x is string => x !== null);\n\n if (filled.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message:\n 'contributes must set at least one of languages/services/features',\n });\n return;\n }\n if (filled.length > 1) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `contributes must set exactly one of languages/services/features, got: ${filled.join(', ')}`,\n });\n return;\n }\n const expected =\n data.category === 'language'\n ? 'languages'\n : data.category === 'service'\n ? 'services'\n : 'features';\n if (filled[0] !== expected) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `category '${data.category}' requires contributes.${expected}, got contributes.${filled[0]}`,\n });\n }\n });\n\nexport type ComponentFile = z.infer<typeof ComponentFileSchema>;\n\nexport interface Component {\n /** Catalog name, e.g. `node`, `atlassian/twg`. Always slash-form. */\n name: string;\n /** Absolute filesystem path of the source yml — useful for errors. */\n sourcePath: string;\n file: ComponentFile;\n}\n\n/**\n * Walk `templates/components/` recursively, parse every `.yml` file,\n * validate it, return as a name-keyed map. README files and other\n * non-yml files are silently skipped.\n *\n * Throws on the first invalid component file with a path-anchored\n * error — better to refuse than to load an inconsistent catalog.\n */\nexport async function loadComponentCatalog(\n rootDir: string = defaultComponentsDir(),\n): Promise<Map<string, Component>> {\n if (!existsSync(rootDir)) {\n return new Map();\n }\n const out = new Map<string, Component>();\n await walk(rootDir, rootDir, out);\n return out;\n}\n\nasync function walk(\n baseDir: string,\n currentDir: string,\n out: Map<string, Component>,\n): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(baseDir, full, out);\n continue;\n }\n if (!entry.isFile() || !entry.name.endsWith('.yml')) continue;\n const relative = path.relative(baseDir, full);\n const name = relative\n .replace(/\\.yml$/, '')\n .split(path.sep)\n .join('/');\n const text = await fs.readFile(full, 'utf8');\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (err) {\n throw new Error(\n `Failed to parse component ${name} (${full}): ${(err as Error).message}`,\n );\n }\n const parsed = ComponentFileSchema.safeParse(raw);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((issue) => {\n const where = issue.path.length > 0 ? issue.path.join('.') : '(root)';\n return ` - ${where}: ${issue.message}`;\n })\n .join('\\n');\n throw new Error(`Invalid component ${name} (${full}):\\n${issues}`);\n }\n out.set(name, { name, sourcePath: full, file: parsed.data });\n }\n}\n\n/**\n * A `SolutionConfig`-shaped fragment produced by merging the\n * `contributes` of one or more components. Caller wraps this into\n * a full config (adds schemaVersion + name) before writing the yml.\n */\nexport interface MergedComponents {\n languages: string[];\n services: string[];\n features: Array<{\n ref: string;\n options: Record<string, string | number | boolean>;\n }>;\n}\n\n/**\n * Merge the contributions of the given components into a single\n * fragment.\n *\n * Rules:\n * - `languages`/`services`: concat + dedupe (insertion order kept\n * stable; first occurrence wins).\n * - `features`: deduped by `ref`. When two components contribute\n * the same ref, their options are merged with the per-key rules\n * below.\n * - Per-key feature option merge:\n * - booleans: OR (true wins)\n * - strings + numbers: later component overrides (rare in\n * practice — components should set activation flags, not\n * credentials; credentials come from monoceros-config.yml\n * defaults.features or the user editing the yml directly).\n *\n * The OR-merge for booleans is what makes\n * `--with=atlassian/rovodev,atlassian/twg` yield both `true` even\n * though each sub-component sets the sibling flag to `false`.\n */\n/**\n * One entry of the resolved-components list. The optional `version`\n * is the `<name>:<version>` suffix from the CLI flag; today it\n * only applies to language components (we append it to each\n * contributed language string so the scaffold passes it as the\n * upstream feature's `version` option). For other categories,\n * providing a version is a builder error and resolveComponents\n * rejects it up front.\n */\nexport interface ResolvedComponent {\n component: Component;\n version?: string;\n}\n\nexport function mergeComponents(\n resolved: Array<Component | ResolvedComponent>,\n): MergedComponents {\n const languages: string[] = [];\n const services: string[] = [];\n const featureByRef = new Map<\n string,\n { ref: string; options: Record<string, string | number | boolean> }\n >();\n\n for (const entry of resolved) {\n const c = isResolvedComponent(entry) ? entry.component : entry;\n const version = isResolvedComponent(entry) ? entry.version : undefined;\n const ct = c.file.contributes;\n for (const lang of ct.languages ?? []) {\n // Language components can carry a `:version` suffix from the\n // CLI. We emit `<lang>:<version>` in the final yml; the\n // scaffold parses it back to the upstream feature's\n // `version` option at apply time.\n const value = version !== undefined ? `${lang}:${version}` : lang;\n if (!languages.includes(value)) languages.push(value);\n }\n for (const svc of ct.services ?? []) {\n if (!services.includes(svc)) services.push(svc);\n }\n for (const f of ct.features ?? []) {\n const existing = featureByRef.get(f.ref);\n if (!existing) {\n featureByRef.set(f.ref, {\n ref: f.ref,\n options: { ...(f.options ?? {}) },\n });\n continue;\n }\n existing.options = mergeFeatureOptions(existing.options, f.options ?? {});\n }\n }\n\n return {\n languages,\n services,\n features: [...featureByRef.values()],\n };\n}\n\nfunction isResolvedComponent(\n x: Component | ResolvedComponent,\n): x is ResolvedComponent {\n return 'component' in x;\n}\n\nfunction mergeFeatureOptions(\n a: Record<string, string | number | boolean>,\n b: Record<string, string | number | boolean>,\n): Record<string, string | number | boolean> {\n const result = { ...a };\n for (const [key, valueB] of Object.entries(b)) {\n const valueA = result[key];\n if (typeof valueA === 'boolean' && typeof valueB === 'boolean') {\n result[key] = valueA || valueB;\n continue;\n }\n result[key] = valueB;\n }\n return result;\n}\n\n/**\n * Resolve `--with=…` names against the catalog. Accepts plain\n * names (`node`) and language-version pairs (`node:20`). Splits\n * the `:version` off, looks up the bare name in the catalog, and\n * carries the version forward only for language components — a\n * version on any other category is rejected with a clear error.\n *\n * Throws with the full list of unknown names so the builder fixes\n * them all at once rather than running into them one at a time.\n */\nexport function resolveComponents(\n catalog: Map<string, Component>,\n names: string[],\n): ResolvedComponent[] {\n const unknown: string[] = [];\n const out: ResolvedComponent[] = [];\n for (const raw of names) {\n const colon = raw.indexOf(':');\n const name = colon === -1 ? raw : raw.slice(0, colon);\n const version = colon === -1 ? undefined : raw.slice(colon + 1);\n\n const c = catalog.get(name);\n if (!c) {\n // The unknown-name message reports the form the user typed\n // (including the :version) so it's easy to spot the typo.\n unknown.push(raw);\n continue;\n }\n if (version !== undefined && c.file.category !== 'language') {\n throw new Error(\n `Component '${name}' is a ${c.file.category}, not a language — a ':${version}' suffix has no meaning here.`,\n );\n }\n out.push({ component: c, ...(version !== undefined ? { version } : {}) });\n }\n if (unknown.length > 0) {\n const available = [...catalog.keys()].sort();\n throw new Error(\n `Unknown component${unknown.length > 1 ? 's' : ''}: ${unknown.join(', ')}.\\n` +\n `Available: ${available.join(', ')}.`,\n );\n }\n return out;\n}\n","import type { Component, ResolvedComponent } from './components.js';\nimport { mergeComponents } from './components.js';\nimport type { FeatureManifestSummary } from './manifest.js';\n\n/**\n * Renderer for the container yml that `monoceros init` produces.\n *\n * Two modes:\n *\n * - **Composed** (`monoceros init <name> --with=node,…`): all\n * listed components are active. The output is a clean,\n * immediately-applyable yml. Per-feature option hints\n * (auth/credentials from the feature manifest) are appended as\n * commented lines beneath the active options block, so a builder\n * reading the yml can see at a glance which keys exist without\n * leaving the file.\n *\n * - **Documented** (`monoceros init <name>` without `--with`):\n * every section is commented out. The output is a self-explaining\n * reference; the builder un-comments what they need and runs\n * `monoceros apply`.\n *\n * Both modes share the per-feature block rendering. The only\n * difference is whether the section is commented out at the top\n * level or not.\n *\n * We hand-render the yml as a string instead of going through the\n * yaml library's AST. The shape is narrow enough that the explicit\n * line-by-line approach is shorter and easier to reason about than\n * juggling Document + Pair + Scalar nodes with attached comments.\n */\n\nexport type ManifestLookup = (\n ref: string,\n) => FeatureManifestSummary | undefined;\n\nconst SCHEMA_HEADER = [\n '# Monoceros solution-config. Edit freely, then run',\n '# `monoceros apply <name>` to materialize a dev-container.',\n '#',\n '# Schema reference: see the workbench `templates/components/README.md`',\n '# and `docs/konzept.md` for what each section does. Each feature',\n '# under `features:` also accepts options not shown here — check',\n \"# the feature's `devcontainer-feature.json` for the full list.\",\n] as const;\n\n/**\n * Render the active-mode yml for the given components.\n */\nexport function generateComposedYml(\n name: string,\n components: ResolvedComponent[],\n lookupManifest: ManifestLookup,\n repoUrls: readonly string[] = [],\n): string {\n const merged = mergeComponents(components);\n const lines: string[] = [];\n for (const h of SCHEMA_HEADER) lines.push(h);\n lines.push('');\n lines.push('schemaVersion: 1');\n lines.push(`name: ${name}`);\n lines.push('');\n\n if (merged.languages.length > 0) {\n lines.push('languages:');\n for (const lang of merged.languages) lines.push(` - ${lang}`);\n lines.push('');\n }\n if (merged.services.length > 0) {\n lines.push('services:');\n for (const svc of merged.services) lines.push(` - ${svc}`);\n lines.push('');\n }\n if (merged.features.length > 0) {\n lines.push('features:');\n for (const f of merged.features) {\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ false,\n );\n }\n lines.push('');\n }\n // Composed mode only emits a repos block when --with-repo provided\n // URLs. The \"show all available options\" requirement applies on a\n // per-entry basis there: each active repo entry carries commented\n // hints for its optional fields. With no URLs, composed mode stays\n // schlank — repos are surfaced via `monoceros add-repo` post-init.\n if (repoUrls.length > 0) {\n renderReposBlock(lines, repoUrls, /* commented */ false);\n }\n\n return ensureTrailingNewline(lines.join('\\n'));\n}\n\n/**\n * Render the documented-default yml: every component listed but\n * commented out, with section headers carrying short prose so a\n * fresh builder can read the file and figure out what to enable.\n */\nexport function generateDocumentedYml(\n name: string,\n catalog: Map<string, Component>,\n lookupManifest: ManifestLookup,\n repoUrls: readonly string[] = [],\n): string {\n const byCategory = groupByCategory(catalog);\n const lines: string[] = [];\n for (const h of SCHEMA_HEADER) lines.push(h);\n lines.push('#');\n lines.push('# Below is the full set of components shipped with this');\n lines.push('# workbench, every one commented out. Un-comment the lines');\n lines.push('# you want active. The same effect (and a cleaner yml) is');\n lines.push('# achievable by running `monoceros init <name> --with=…`');\n lines.push('# with a comma-separated list of component names.');\n lines.push('');\n lines.push('schemaVersion: 1');\n lines.push(`name: ${name}`);\n lines.push('');\n\n if (byCategory.language.length > 0) {\n const items = byCategory.language.flatMap((c) =>\n (c.file.contributes.languages ?? []).map((lang) => ({\n value: lang,\n label: c.file.displayName,\n })),\n );\n const width = Math.max(...items.map((i) => i.value.length)) + 2;\n lines.push('# Languages — runtime toolchains.');\n lines.push('# languages:');\n for (const item of items) {\n const pad = ' '.repeat(width - item.value.length);\n lines.push(`# - ${item.value}${pad}# ${item.label}`);\n }\n lines.push('');\n }\n if (byCategory.service.length > 0) {\n const items = byCategory.service.flatMap((c) =>\n (c.file.contributes.services ?? []).map((svc) => ({\n value: svc,\n label: c.file.displayName,\n })),\n );\n const width = Math.max(...items.map((i) => i.value.length)) + 2;\n lines.push('# Services — compose-mode siblings of the workspace');\n lines.push('# container (compose mode kicks in as soon as at least');\n lines.push('# one service is active).');\n lines.push('# services:');\n for (const item of items) {\n const pad = ' '.repeat(width - item.value.length);\n lines.push(`# - ${item.value}${pad}# ${item.label}`);\n }\n lines.push('');\n }\n if (byCategory.feature.length > 0) {\n lines.push('# Features — devcontainer features installed inside the');\n lines.push('# container. Each entry has an OCI-style `ref` plus an');\n lines.push('# optional `options` map. Credentials/auth keys appear');\n lines.push('# as commented hints; set them here per container, or');\n lines.push('# globally in monoceros-config.yml under');\n lines.push('# `defaults.features.<ref>`.');\n lines.push('#');\n lines.push('# Catalog:');\n lines.push('#');\n const nameColumnWidth =\n Math.max(...byCategory.feature.map((c) => c.name.length)) + 2;\n for (const c of byCategory.feature) {\n const pad = ' '.repeat(nameColumnWidth - c.name.length);\n lines.push(`# ${c.name}${pad}${c.file.displayName}`);\n }\n lines.push('#');\n lines.push('# Below: one block per feature ref. Un-comment what');\n lines.push(\"# you want active. Sub-components share their parent's\");\n lines.push('# block — pick the parent for the full preset, swap to');\n lines.push('# a sub-component name for a partial install.');\n lines.push('#');\n lines.push('# features:');\n\n // Render one feature block per unique ref. Prefer the top-level\n // component (e.g. `atlassian` over `atlassian/twg`) as the source\n // of the rendered options, since the top-level carries the\n // \"everything on\" default users typically want first.\n const renderedRefs = new Set<string>();\n const topLevel = byCategory.feature.filter((c) => !c.name.includes('/'));\n\n for (const c of topLevel) {\n for (const f of c.file.contributes.features ?? []) {\n if (renderedRefs.has(f.ref)) continue;\n renderedRefs.add(f.ref);\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ true,\n );\n }\n }\n // Any feature ref only mentioned through a sub-component (no\n // top-level component for the same ref) — render it from the\n // first sub-component.\n for (const c of byCategory.feature) {\n if (!c.name.includes('/')) continue;\n for (const f of c.file.contributes.features ?? []) {\n if (renderedRefs.has(f.ref)) continue;\n renderedRefs.add(f.ref);\n renderFeatureBlock(\n lines,\n f,\n lookupManifest(f.ref),\n /* commented */ true,\n );\n }\n }\n lines.push('');\n }\n // Repos section — always rendered in documented mode (commented out\n // when no --with-repo URLs were provided, active when they were).\n // The block shows the full set of per-entry options so a builder\n // reading the yml sees what's possible without leaving the file —\n // same \"all available options are visible\" rule that drives the\n // features block above.\n renderReposBlock(lines, repoUrls, /* commented */ repoUrls.length === 0);\n\n return ensureTrailingNewline(lines.join('\\n'));\n}\n\ninterface RenderableFeature {\n ref: string;\n options?: Record<string, string | number | boolean>;\n}\n\n// Target column width for rendered comment lines. Description text\n// and usage notes get word-wrapped against this width so the output\n// stays readable in a standard editor without horizontal scrolling.\nconst COMMENT_WIDTH = 72;\n\nfunction renderFeatureBlock(\n out: string[],\n feature: RenderableFeature,\n summary: FeatureManifestSummary | undefined,\n commented: boolean,\n): void {\n const c = commented ? '# ' : ' ';\n const optionHints = summary?.optionHints ?? [];\n const optionDescriptions = summary?.optionDescriptions ?? {};\n const usageNotes = summary?.usageNotes ?? [];\n\n // Per-feature usage notes — rendered as a wrapped comment block\n // right before the `- ref:` line. Multiple notes are separated\n // by an empty comment line so they read as distinct paragraphs.\n for (let i = 0; i < usageNotes.length; i++) {\n if (i > 0) out.push(`${c}#`);\n for (const line of wrapToComment(\n usageNotes[i]!,\n COMMENT_WIDTH - c.length,\n )) {\n out.push(`${c}# ${line}`);\n }\n }\n\n out.push(`${c}- ref: ${feature.ref}`);\n const options = feature.options ?? {};\n const activeOptions = Object.entries(options);\n const remainingHints = optionHints.filter((h) => !(h in options));\n\n // When there are active options, emit a real `options:` block.\n // When there are only hints (no active options), skip `options:`\n // entirely and emit the hints as plain comments at the same depth\n // — yaml parsers see `options:` with no content under it as\n // `null`, which fails our schema.\n if (activeOptions.length > 0) {\n out.push(`${c} options:`);\n for (const [key, value] of activeOptions) {\n out.push(`${c} ${key}: ${renderScalarValue(value)}`);\n }\n if (remainingHints.length > 0) {\n out.push(\n `${c} # Optional — override monoceros-config.yml defaults.features:`,\n );\n for (const hint of remainingHints) {\n emitHint(out, hint, optionDescriptions[hint], `${c} `);\n }\n }\n } else if (remainingHints.length > 0) {\n out.push(\n `${c} # Optional — override monoceros-config.yml defaults.features:`,\n );\n out.push(`${c} # options:`);\n for (const hint of remainingHints) {\n emitHint(out, hint, optionDescriptions[hint], `${c} # `);\n }\n }\n}\n\n/**\n * Emit a single option-hint line, optionally preceded by its\n * description as wrapped comment lines. `linePrefix` is the full\n * prefix (indent + any commented-out `# ` chars) that every\n * emitted line should start with; the hint itself is suffixed\n * with `: ` so the user can fill in a value.\n */\nfunction emitHint(\n out: string[],\n hint: string,\n description: string | undefined,\n linePrefix: string,\n): void {\n if (description) {\n for (const line of wrapToComment(\n description,\n COMMENT_WIDTH - linePrefix.length,\n )) {\n out.push(`${linePrefix}# ${line}`);\n }\n }\n out.push(`${linePrefix}${hint}:`);\n}\n\n/**\n * Render the `repos:` section. When `commented` is true the entire\n * block is prefixed with `# ` (documented mode, no --with-repo).\n * When false the `url:` lines are active but the optional fields\n * (path, provider, git.user) stay commented per entry so the\n * builder sees what else can go there.\n *\n * The \"all available options visible\" rule: every per-entry field\n * the schema accepts appears on every rendered entry, either active\n * (rare — only `url` is required) or as a commented hint.\n */\nfunction renderReposBlock(\n out: string[],\n urls: readonly string[],\n commented: boolean,\n): void {\n // Prose intro — always a comment block, regardless of whether the\n // section below is active or fully commented.\n out.push('# Repos — git repositories cloned into projects/ during');\n out.push('# post-create. HTTPS-only (ADR 0006). Provider auto-detected');\n out.push('# for github.com, gitlab.com, bitbucket.org; for any other host');\n out.push('# (self-hosted GitLab, Bitbucket Data Center, Gitea/Forgejo,');\n out.push('# GitHub Enterprise, …) declare provider explicitly.');\n out.push('#');\n\n if (commented) {\n // Pure documented mode (no --with-repo): a single example entry,\n // everything commented. Shows the full per-entry surface.\n out.push('# repos:');\n out.push('# - url: https://github.com/<org>/<repo>.git');\n out.push(\n '# # path: <folder> # subfolder under projects/; default: URL-derived',\n );\n out.push(\n '# # provider: github # github | gitlab | bitbucket | gitea',\n );\n out.push(\n '# # git: # per-repo committer identity override',\n );\n out.push('# # user:');\n out.push('# # name: Your Name');\n out.push('# # email: you@example.com');\n out.push('');\n return;\n }\n\n // Active entries from --with-repo. Each entry repeats the\n // commented hints for the optional fields so they're discoverable\n // without docs.\n out.push('repos:');\n for (const url of urls) {\n const derivedPath = deriveDefaultPath(url);\n out.push(` - url: ${url}`);\n out.push(\n ` # path: ${derivedPath} # subfolder under projects/; default: URL-derived (${derivedPath})`,\n );\n out.push(\n ' # provider: github # github | gitlab | bitbucket | gitea',\n );\n out.push(\n ' # git: # per-repo committer identity override',\n );\n out.push(' # user:');\n out.push(' # name: Your Name');\n out.push(' # email: you@example.com');\n }\n out.push('');\n}\n\n/**\n * URL-derived default for the `path:` hint. Mirrors what\n * `deriveRepoName` in scaffold.ts does at apply time — inlined\n * here to keep the generator self-contained (no cross-module\n * dependency from init/ → create/).\n */\nfunction deriveDefaultPath(url: string): string {\n let last = url;\n const slash = url.lastIndexOf('/');\n if (slash >= 0) last = url.slice(slash + 1);\n if (last.endsWith('.git')) last = last.slice(0, -4);\n return last || 'repo';\n}\n\n/**\n * Word-wrap a single paragraph of plain text to `width` columns.\n * The returned strings do NOT include any prefix — the caller is\n * expected to prepend a comment marker (`# `) and any indent.\n * Long words that exceed `width` are emitted on their own line\n * rather than split mid-word.\n */\nfunction wrapToComment(text: string, width: number): string[] {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n if (words.length === 0) return [''];\n const usable = Math.max(width, 20);\n const lines: string[] = [];\n let current = '';\n for (const w of words) {\n if (current.length === 0) {\n current = w;\n continue;\n }\n if (current.length + 1 + w.length <= usable) {\n current += ' ' + w;\n } else {\n lines.push(current);\n current = w;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\nfunction renderScalarValue(value: string | number | boolean): string {\n if (typeof value === 'string') {\n // Quote anything that could be ambiguous to a yaml parser\n // (leading numerics, special chars). Quoting strings is always\n // safe; we only avoid it for booleans/numbers so they keep\n // their types.\n return /^[A-Za-z_][A-Za-z0-9._-]*$/.test(value)\n ? value\n : JSON.stringify(value);\n }\n return String(value);\n}\n\nfunction groupByCategory(catalog: Map<string, Component>): {\n language: Component[];\n service: Component[];\n feature: Component[];\n} {\n const out: ReturnType<typeof groupByCategory> = {\n language: [],\n service: [],\n feature: [],\n };\n // Stable sort by component name for deterministic output.\n const sorted = [...catalog.values()].sort((a, b) =>\n a.name.localeCompare(b.name),\n );\n for (const c of sorted) {\n out[c.file.category].push(c);\n }\n return out;\n}\n\nfunction ensureTrailingNewline(s: string): string {\n return s.endsWith('\\n') ? s : s + '\\n';\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { bundledFeaturesDir, workbenchCheckoutRoot } from '../config/paths.js';\nimport { matchMonocerosFeature } from '../util/ref.js';\n\n/**\n * Loader for the parts of a Monoceros devcontainer-feature manifest\n * that init's yml-generator wants to surface as inline guidance:\n *\n * - `optionHints` — names of feature options to render as\n * commented-out lines below the active options block, so a\n * builder reading the yml sees at a glance which keys exist\n * without going to the feature's docs.\n * - `optionDescriptions` — the `description` string from each\n * option in the manifest, keyed by option name. Init prints\n * these as a wrapped comment block above the matching hint\n * line so the builder knows what the option does without\n * opening the feature docs.\n * - `usageNotes` — free-text per-feature paragraphs from\n * `x-monoceros.usageNotes`. Init renders them as a comment\n * block right above the `- ref:` line. Use for things that\n * aren't tied to one option — e.g. \"alternative auth flow X\n * works inside the running container\".\n *\n * Only Monoceros-owned refs\n * (`ghcr.io/getmonoceros/monoceros-features/<name>:<tag>`) are\n * resolved — for third-party features we don't have the manifest on\n * disk at init time. The fallback is \"no hints\", which is right:\n * we don't speculate about other people's feature options.\n *\n * Manifest lookup order:\n * 1. Workbench checkout — `<checkoutRoot>/images/features/<name>/`.\n * Dev edits to the source-of-truth manifest are visible\n * immediately, no rebuild step required.\n * 2. CLI bundle — `<workbenchRoot>/features/<name>/`. Populated\n * by `pnpm manifests:sync` (runs as `prebuild`), shipped in\n * the npm tarball. Production fallback for builders without a\n * workbench checkout.\n *\n * Both paths missing → `undefined` and init renders without hints.\n * Never throws.\n */\n\nexport interface FeatureManifestSummary {\n /** Names of options to render as commented hints in the init output. */\n optionHints: string[];\n /** `description` from each option in the manifest, keyed by name. */\n optionDescriptions: Record<string, string>;\n /** Free-text per-feature notes rendered above the `- ref:` line. */\n usageNotes: string[];\n}\n\ninterface RawManifest {\n options?: Record<string, { description?: string }>;\n 'x-monoceros'?: {\n optionHints?: unknown;\n usageNotes?: unknown;\n };\n}\n\nfunction resolveManifestPath(\n name: string,\n checkoutRoot: string | null,\n): string | null {\n if (checkoutRoot) {\n const checkoutPath = path.join(\n checkoutRoot,\n 'images',\n 'features',\n name,\n 'devcontainer-feature.json',\n );\n if (existsSync(checkoutPath)) return checkoutPath;\n }\n const bundlePath = path.join(\n bundledFeaturesDir(),\n name,\n 'devcontainer-feature.json',\n );\n if (existsSync(bundlePath)) return bundlePath;\n return null;\n}\n\nexport function loadFeatureManifestSummary(\n ref: string,\n checkoutRoot: string | null = workbenchCheckoutRoot(),\n): FeatureManifestSummary | undefined {\n const match = matchMonocerosFeature(ref);\n if (!match) return undefined;\n const manifestPath = resolveManifestPath(match.name, checkoutRoot);\n if (!manifestPath) return undefined;\n try {\n const text = readFileSync(manifestPath, 'utf8');\n const parsed = JSON.parse(text) as RawManifest;\n\n const rawHints = parsed['x-monoceros']?.optionHints;\n const optionHints = Array.isArray(rawHints)\n ? rawHints.filter(\n (x): x is string => typeof x === 'string' && x.length > 0,\n )\n : [];\n\n const rawNotes = parsed['x-monoceros']?.usageNotes;\n const usageNotes = Array.isArray(rawNotes)\n ? rawNotes.filter(\n (x): x is string => typeof x === 'string' && x.length > 0,\n )\n : [];\n\n const optionDescriptions: Record<string, string> = {};\n if (parsed.options) {\n for (const [key, opt] of Object.entries(parsed.options)) {\n if (\n opt &&\n typeof opt === 'object' &&\n typeof opt.description === 'string' &&\n opt.description.length > 0\n ) {\n optionDescriptions[key] = opt.description;\n }\n }\n }\n\n return { optionHints, optionDescriptions, usageNotes };\n } catch {\n return undefined;\n }\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { loadComponentCatalog } from '../init/components.js';\nimport { colorsFor } from '../util/format.js';\n\n// Category-key → human-readable section label. Same order is used\n// for rendering — languages first (most common), services next,\n// features last.\nconst CATEGORY_LABELS = {\n language: 'Languages',\n service: 'Services',\n feature: 'Features',\n} as const;\nconst CATEGORY_ORDER: ReadonlyArray<keyof typeof CATEGORY_LABELS> = [\n 'language',\n 'service',\n 'feature',\n];\n\nexport const listComponentsCommand = defineCommand({\n meta: {\n name: 'list-components',\n group: 'discovery',\n description:\n 'Print the components catalog used by `monoceros init --with=…`, grouped by category (Languages, Services, Features). Component names render in cyan, descriptions in default colour; when piped, the formatting drops out and lines become `name<TAB>description` for grep/awk-friendly consumption.',\n },\n args: {},\n async run() {\n try {\n const catalog = await loadComponentCatalog();\n if (catalog.size === 0) {\n consola.warn(\n 'No components found. The workbench checkout looks incomplete.',\n );\n process.exit(0);\n }\n\n const fmt = colorsFor(process.stdout);\n const isTty = process.stdout.isTTY ?? false;\n\n // Group entries by category for sectioned rendering.\n const byCategory = new Map<\n string,\n Array<{ name: string; desc: string }>\n >();\n for (const c of catalog.values()) {\n const list = byCategory.get(c.file.category) ?? [];\n list.push({ name: c.name, desc: c.file.displayName });\n byCategory.set(c.file.category, list);\n }\n for (const list of byCategory.values()) {\n list.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n // Piped (non-TTY) output: stay machine-friendly with the\n // historical `name<TAB>description` shape, one category at a\n // time. No ANSI, no alignment padding — grep/awk consumers\n // want predictable columns.\n if (!isTty) {\n let first = true;\n for (const cat of CATEGORY_ORDER) {\n const items = byCategory.get(cat);\n if (!items || items.length === 0) continue;\n if (!first) process.stdout.write('\\n');\n first = false;\n process.stdout.write(`# ${cat}\\n`);\n for (const { name, desc } of items) {\n process.stdout.write(`${name}\\t${desc}\\n`);\n }\n }\n process.exit(0);\n }\n\n // Interactive (TTY) output: section headers + aligned\n // columns, same visual vocabulary as the help renderer and\n // the apply/install structured output. Cyan name column\n // padded to the widest entry in its section so the\n // description column lines up.\n let first = true;\n for (const cat of CATEGORY_ORDER) {\n const items = byCategory.get(cat);\n if (!items || items.length === 0) continue;\n if (!first) process.stdout.write('\\n');\n first = false;\n process.stdout.write(`${fmt.sectionLine(CATEGORY_LABELS[cat])}\\n\\n`);\n const nameWidth = Math.max(...items.map((i) => i.name.length));\n const gutter = 2;\n for (const { name, desc } of items) {\n const pad = ' '.repeat(nameWidth - name.length + gutter);\n process.stdout.write(` ${fmt.cyan(name)}${pad}${desc}\\n`);\n }\n }\n process.exit(0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runLogs } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const logsCommand = defineCommand({\n meta: {\n name: 'logs',\n group: 'run',\n description:\n 'Tail logs from the compose services of the named dev-container. Pass --no-follow for a one-shot dump.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n follow: {\n type: 'boolean',\n description:\n 'Follow log output (default: true). Use --no-follow to disable.',\n alias: ['f'],\n default: true,\n },\n },\n run({ args }) {\n return dispatch(() =>\n runLogs({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n follow: args.follow,\n }),\n );\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { getInnerArgs } from '../inner-args.js';\nimport { runRemoveAptPackages } from '../modify/index.js';\n\nexport const removeAptPackagesCommand = defineCommand({\n meta: {\n name: 'remove-apt-packages',\n group: 'edit',\n description:\n 'Remove apt packages from the container config. Pass package names after `--` (e.g. `monoceros remove-apt-packages sandbox -- make jq`). Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n const packages = [...getInnerArgs()];\n if (packages.length === 0) {\n consola.error(\n 'No package names given. Usage: `monoceros remove-apt-packages <containername> [--yes] -- <pkg> [<pkg> …]`.',\n );\n process.exit(1);\n }\n try {\n const result = await runRemoveAptPackages({\n name: args.name,\n packages,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveFeature } from '../modify/index.js';\n\nexport const removeFeatureCommand = defineCommand({\n meta: {\n name: 'remove-feature',\n group: 'edit',\n description:\n 'Remove a devcontainer feature from the container config (by its OCI ref). Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n ref: {\n type: 'positional',\n description:\n 'Feature ref (e.g. ghcr.io/devcontainers/features/docker-in-docker:2).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveFeature({\n name: args.name,\n ref: args.ref,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { createInterface } from 'node:readline/promises';\nimport { runRemove } from '../remove/index.js';\n\nexport const removeCommand = defineCommand({\n meta: {\n name: 'remove',\n group: 'lifecycle',\n description:\n 'Wipe everything belonging to a container: stop and remove the docker objects, back up the container-configs yml + container directory (incl. home/, projects/, data/), then delete them from disk. Shared docker images stay. By default the destructive step is confirmed interactively; pass -y to skip.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n backup: {\n type: 'boolean',\n // citty turns a default-true boolean automatically into a\n // `--no-X` flag for negation, so the builder gets the natural\n // `monoceros remove <name> --no-backup` form without us\n // needing to special-case the parsing. Defining the arg as\n // `no-backup` directly conflicts with citty's prefix logic\n // and silently fails to bind, so we always go through the\n // positive form.\n description:\n 'Write a backup of <container-dir> and the yml under container-backups/ before deleting. Default on; use `--no-backup` to skip.',\n default: true,\n },\n yes: {\n type: 'boolean',\n alias: 'y',\n description:\n 'Skip the interactive confirmation prompt. Useful in scripts.',\n default: false,\n },\n },\n async run({ args }) {\n try {\n const noBackup = args.backup === false;\n const skipPrompt = args.yes === true;\n\n if (!skipPrompt) {\n const warning = noBackup\n ? `About to remove '${args.name}' WITHOUT a backup. Docker objects, container-configs entry, and container directory will all be deleted.`\n : `About to remove '${args.name}'. A backup will be written to container-backups/ first, then docker objects, container-configs entry, and container directory will all be deleted.`;\n consola.warn(warning);\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n const answer = await rl.question('Continue? [y/N] ');\n rl.close();\n if (!/^y(es)?$/i.test(answer.trim())) {\n consola.info('Aborted. Nothing changed.');\n process.exit(0);\n }\n }\n\n await runRemove({\n name: args.name,\n ...(noBackup ? { noBackup: true } : {}),\n });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\nimport { REGEX } from '../config/schema.js';\nimport {\n composeProjectName,\n spawnBash,\n type ComposeSpawn,\n} from '../devcontainer/compose.js';\n\n/**\n * `monoceros remove <name>` — wipe everything belonging to one\n * container.\n *\n * What \"everything\" means in practice (in this order):\n *\n * 1. Stop and remove docker objects scoped to the container:\n * - compose containers (label `com.docker.compose.project=<project>`)\n * - any image-mode container matching `vsc-<name>-*`\n * - the project network `<project>_default`\n * Named docker volumes are no longer used as of fea2b3f (DB data\n * is bind-mounted onto `<container-dir>/data/<svc>/`), so they\n * go away with the directory delete below.\n *\n * 2. Optionally back up the host-side state:\n * - `container-configs/<name>.yml`\n * - `container/<name>/` (entire scaffold incl. `home/`,\n * `projects/`, `.monoceros/`, `data/`)\n * Lands at `container-backups/<name>-<timestamp>/`. Plain\n * directory copy — readable with normal filesystem tools.\n *\n * 3. Delete the host-side state.\n *\n * Shared docker images (`monoceros-runtime:dev`, feature build\n * images, postgres/mysql/redis base images) are NOT removed — they\n * are shared across containers and pruning them is a separate\n * operation the builder can do with `docker image prune` when they\n * actually want to free that disk.\n */\n\nexport interface RunRemoveOptions {\n name: string;\n /** When true, skip the backup step. */\n noBackup?: boolean;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n /** Override the timestamp embedded in the backup directory name. */\n now?: Date;\n /** Bash spawn for the docker cleanup script. Tests inject a stub. */\n dockerSpawn?: ComposeSpawn;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n warn?: (msg: string) => void;\n };\n}\n\nexport interface RunRemoveResult {\n /** Path the yml was at before deletion, or `null` if it didn't exist. */\n configPath: string | null;\n /** Path the container scaffold was at before deletion, or `null`. */\n containerPath: string | null;\n /** Directory of the backup, or `null` when --no-backup was passed. */\n backupPath: string | null;\n /** Exit code of the docker cleanup step (0 on success). */\n dockerExitCode: number;\n}\n\nexport async function runRemove(\n opts: RunRemoveOptions,\n): Promise<RunRemoveResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n warn: (msg) => consola.warn(msg),\n };\n\n if (!REGEX.solutionName.test(opts.name)) {\n throw new Error(\n `Invalid config name: ${JSON.stringify(opts.name)}. Use letters, digits, '.', '_' or '-'.`,\n );\n }\n\n const ymlPath = containerConfigPath(opts.name, home);\n const containerPath = containerDir(opts.name, home);\n const hasYml = existsSync(ymlPath);\n const hasContainer = existsSync(containerPath);\n\n if (!hasYml && !hasContainer) {\n throw new Error(\n `Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`,\n );\n }\n\n // ── Step 1: stop + remove docker objects ────────────────────────\n const projectName = composeProjectName(containerPath);\n const dockerSpawn = opts.dockerSpawn ?? spawnBash;\n const script = [\n `set -u`,\n `echo \"[remove] tearing down docker project ${projectName}…\"`,\n // Compose-mode containers, identified by the compose project label.\n `by_label=$(docker ps -aq --filter \"label=com.docker.compose.project=${projectName}\" 2>/dev/null || true)`,\n // Devcontainer-cli containers (image-mode workspace + feature-\n // build intermediates) all carry this label, value = the absolute\n // container-dir path. Most reliable anchor we have, because\n // @devcontainers/cli lets Docker assign random names like\n // 'kind_cerf' — neither the project-name nor the vsc-<name>-\n // prefix filters below catch those.\n `by_dc_label=$(docker ps -aq --filter \"label=devcontainer.local_folder=${containerPath}\" 2>/dev/null || true)`,\n // Container-name prefix fallback (catches half-broken state).\n `by_compose_name=$(docker ps -aq --filter \"name=^${projectName}-\" 2>/dev/null || true)`,\n // Image-mode devcontainer-cli name fallback (only kicks in when\n // the cli used a deterministic name — modern versions don't).\n `by_image_name=$(docker ps -aq --filter \"name=^vsc-${opts.name}-\" 2>/dev/null || true)`,\n `to_remove=$(printf \"%s\\\\n%s\\\\n%s\\\\n%s\\\\n\" \"$by_label\" \"$by_dc_label\" \"$by_compose_name\" \"$by_image_name\" | sort -u | grep -v \"^$\" || true)`,\n `if [ -n \"$to_remove\" ]; then echo \"[remove] removing containers: $(echo $to_remove | tr \"\\\\n\" \" \")\"; docker rm -f $to_remove >/dev/null || true; else echo \"[remove] no containers found\"; fi`,\n `docker network rm ${projectName}_default 2>/dev/null && echo \"[remove] network ${projectName}_default removed\" || true`,\n `echo \"[remove] docker cleanup done\"`,\n ].join('; ');\n const dockerExitCode = await dockerSpawn(['-c', script], home);\n\n // ── Step 2: optional backup ────────────────────────────────────\n let backupPath: string | null = null;\n if (!opts.noBackup && (hasYml || hasContainer)) {\n const ts = (opts.now ?? new Date()).toISOString().replace(/[:.]/g, '-');\n backupPath = path.join(home, 'container-backups', `${opts.name}-${ts}`);\n await fs.mkdir(backupPath, { recursive: true });\n if (hasYml) {\n await fs.copyFile(ymlPath, path.join(backupPath, `${opts.name}.yml`));\n }\n if (hasContainer) {\n await fs.cp(containerPath, path.join(backupPath, 'container'), {\n recursive: true,\n });\n }\n logger.info(`Backup written to ${prettyPath(backupPath)}.`);\n }\n\n // ── Step 3: delete host-side state ─────────────────────────────\n if (hasYml) {\n await fs.rm(ymlPath, { force: true });\n }\n if (hasContainer) {\n await fs.rm(containerPath, { recursive: true, force: true });\n }\n\n logger.success(\n `Removed '${opts.name}': docker objects gone, container-configs entry deleted, container directory deleted.`,\n );\n if (!backupPath) {\n logger.warn?.(\n 'No backup created (--no-backup). The host-side state is gone for good.',\n );\n }\n\n return {\n configPath: hasYml ? ymlPath : null,\n containerPath: hasContainer ? containerPath : null,\n backupPath,\n dockerExitCode,\n };\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRestore } from '../restore/index.js';\n\nexport const restoreCommand = defineCommand({\n meta: {\n name: 'restore',\n group: 'lifecycle',\n description:\n \"Restore a container's host-side state from a backup written by `monoceros remove`. Copies the yml and the container directory back into $MONOCEROS_HOME. Refuses to overwrite an existing config or container — remove the in-place container first if you need to clobber. Run `monoceros apply <name>` afterwards to bring it back up.\",\n },\n args: {\n 'backup-path': {\n type: 'positional',\n description:\n 'Path to a backup directory (typically `<MONOCEROS_HOME>/container-backups/<name>-<timestamp>/`).',\n required: true,\n },\n },\n async run({ args }) {\n try {\n await runRestore({ backupPath: args['backup-path'] });\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync, promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { consola } from 'consola';\nimport {\n containerConfigPath,\n containerConfigsDir,\n containerDir,\n monocerosHome as defaultMonocerosHome,\n prettyPath,\n} from '../config/paths.js';\n\n/**\n * `monoceros restore <backup-path>` — re-instantiate the host-side\n * state of a previously-removed container from a backup written by\n * `monoceros remove`.\n *\n * Backup layout (produced by runRemove):\n *\n * <backup>/<name>.yml ← container-configs source\n * <backup>/container/ ← full container scaffold\n * (home/, projects/, data/, .monoceros/, …)\n *\n * Restore copies both back into `$MONOCEROS_HOME`:\n *\n * $MONOCEROS_HOME/container-configs/<name>.yml\n * $MONOCEROS_HOME/container/<name>/\n *\n * Refuses to clobber an existing config or container dir — the\n * builder must remove the in-place container first (or pick a\n * different target name).\n *\n * Restore does NOT recreate the docker objects: builder runs\n * `monoceros apply <name>` afterwards. That keeps restore a\n * pure filesystem operation, safe to dry-run, with no side-\n * effects on the docker daemon.\n */\n\nexport interface RunRestoreOptions {\n /** Path to a `<MONOCEROS_HOME>/container-backups/<name>-<ts>/` dir. */\n backupPath: string;\n /** Override of the user-data home. Tests inject a tmpdir. */\n monocerosHome?: string;\n logger?: {\n info: (msg: string) => void;\n success: (msg: string) => void;\n };\n}\n\nexport interface RunRestoreResult {\n /** Container name detected from the backup contents. */\n name: string;\n /** Where the yml was restored to. */\n configPath: string;\n /** Where the container directory was restored to (or `null` when\n * the backup didn't carry one — e.g. a remove that ran before any\n * apply had materialized the container dir). */\n containerPath: string | null;\n}\n\nexport async function runRestore(\n opts: RunRestoreOptions,\n): Promise<RunRestoreResult> {\n const home = opts.monocerosHome ?? defaultMonocerosHome();\n const logger = opts.logger ?? {\n info: (msg) => consola.info(msg),\n success: (msg) => consola.success(msg),\n };\n\n const backup = path.resolve(opts.backupPath);\n if (!existsSync(backup)) {\n throw new Error(`Backup not found: ${backup}.`);\n }\n const stat = await fs.stat(backup);\n if (!stat.isDirectory()) {\n throw new Error(`Backup path is not a directory: ${backup}.`);\n }\n\n // Detect the container name from the single `.yml` file in the\n // backup root. runRemove writes `<name>.yml`; we don't depend on\n // the backup-directory name (`<name>-<timestamp>`) because the\n // builder might have renamed/moved the backup folder.\n const entries = await fs.readdir(backup);\n const ymlFiles = entries.filter((f) => f.endsWith('.yml'));\n if (ymlFiles.length === 0) {\n throw new Error(\n `Backup at ${backup} doesn't contain a *.yml — expected a single config file at the root.`,\n );\n }\n if (ymlFiles.length > 1) {\n throw new Error(\n `Backup at ${backup} contains multiple .yml files (${ymlFiles.join(', ')}). Expected exactly one.`,\n );\n }\n const ymlFile = ymlFiles[0]!;\n const name = ymlFile.replace(/\\.yml$/, '');\n\n const containerInBackup = path.join(backup, 'container');\n const hasContainer = existsSync(containerInBackup);\n\n // Refuse to overwrite live state.\n const destYml = containerConfigPath(name, home);\n const destContainer = containerDir(name, home);\n if (existsSync(destYml)) {\n throw new Error(\n `Refusing to restore: ${destYml} already exists. Remove the current container first (\\`monoceros remove ${name}\\`) or rename the existing config.`,\n );\n }\n if (hasContainer && existsSync(destContainer)) {\n throw new Error(\n `Refusing to restore: ${destContainer} already exists. Remove the current container first (\\`monoceros remove ${name}\\`).`,\n );\n }\n\n // Copy back into place.\n await fs.mkdir(containerConfigsDir(home), { recursive: true });\n await fs.copyFile(path.join(backup, ymlFile), destYml);\n if (hasContainer) {\n await fs.cp(containerInBackup, destContainer, { recursive: true });\n }\n\n logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);\n logger.info(\n `Run \\`monoceros apply ${name}\\` to bring the container back up.`,\n );\n\n return {\n name,\n configPath: destYml,\n containerPath: hasContainer ? destContainer : null,\n };\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveFromUrl } from '../modify/index.js';\n\nexport const removeFromUrlCommand = defineCommand({\n meta: {\n name: 'remove-from-url',\n group: 'edit',\n description:\n 'Remove a previously-added install URL from the container config. Idempotent, prints a diff before writing. The URL is dropped from post-create.sh on the next `monoceros apply`.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n url: {\n type: 'positional',\n description: 'Install URL to remove (must match the original exactly).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveFromUrl({\n name: args.name,\n url: args.url,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveLanguage } from '../modify/index.js';\n\nexport const removeLanguageCommand = defineCommand({\n meta: {\n name: 'remove-language',\n group: 'edit',\n description:\n 'Remove a language toolchain from the container config. Idempotent, prints a diff before writing.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n language: {\n type: 'positional',\n description: 'Language identifier (e.g. python, java, rust).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveLanguage({\n name: args.name,\n language: args.language,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveRepo } from '../modify/index.js';\n\nexport const removeRepoCommand = defineCommand({\n meta: {\n name: 'remove-repo',\n group: 'edit',\n description:\n 'Remove a repo from the container config (matches by URL or by its projects/<folder> name). Does NOT delete the existing projects/<folder> directory — local edits are preserved; clean it up manually.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n target: {\n type: 'positional',\n description: 'Repo URL or its projects/<folder> name. Either works.',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveRepo({\n name: args.name,\n target: args.target,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { runRemoveService } from '../modify/index.js';\n\nexport const removeServiceCommand = defineCommand({\n meta: {\n name: 'remove-service',\n group: 'edit',\n description:\n 'Remove a compose service from the container config. Idempotent, prints a diff before writing. Note: data volumes (e.g. postgres-data) are NOT cleaned up automatically.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'positional',\n description: 'Service identifier (e.g. postgres, redis).',\n required: true,\n },\n yes: {\n type: 'boolean',\n description: 'Skip the interactive confirmation and apply the diff.',\n alias: ['y'],\n default: false,\n },\n },\n async run({ args }) {\n try {\n const result = await runRemoveService({\n name: args.name,\n service: args.service,\n yes: args.yes,\n });\n process.exit(result.status === 'aborted' ? 1 : 0);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { containerDir } from '../config/paths.js';\nimport { runInContainer } from '../devcontainer/run.js';\nimport { getInnerArgs } from '../inner-args.js';\n\nexport const runCommand = defineCommand({\n meta: {\n name: 'run',\n group: 'run',\n description:\n 'Run a one-off command inside the named dev-container. Use `--` to separate monoceros flags from the inner command.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n async run({ args }) {\n const command = [...getInnerArgs()];\n if (command.length === 0) {\n consola.error(\n 'No command provided. Usage: `monoceros run <containername> -- <cmd> [args…]`.',\n );\n process.exit(1);\n }\n try {\n const exitCode = await runInContainer({\n root: containerDir(args.name),\n command,\n });\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\n\nexport interface ShellLogger {\n info: (message: string) => void;\n}\n\nexport interface RunShellOptions {\n /** Container root: `<MONOCEROS_HOME>/container/<name>/`. */\n root: string;\n spawn?: DevcontainerSpawn;\n}\n\nexport async function runShell(opts: RunShellOptions): Promise<number> {\n assertContainerExists(opts.root);\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n\n const upCode = await spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n { quiet: true },\n );\n if (upCode !== 0) return upCode;\n\n return spawnFn(\n [\n 'exec',\n '--workspace-folder',\n opts.root,\n '--mount-workspace-git-root=false',\n 'bash',\n ],\n opts.root,\n { interactive: true },\n );\n}\n\nexport function assertContainerExists(root: string): void {\n if (!existsSync(path.join(root, '.devcontainer'))) {\n throw new Error(\n `No .devcontainer/ at ${root}. Run \\`monoceros apply <name>\\` first.`,\n );\n }\n}\n","import { spawnDevcontainer, type DevcontainerSpawn } from './cli.js';\nimport { assertContainerExists } from './shell.js';\n\nexport interface RunInContainerOptions {\n /** Container root: `<MONOCEROS_HOME>/container/<name>/`. */\n root: string;\n command: string[];\n spawn?: DevcontainerSpawn;\n}\n\n// Run a one-off command inside the named container. Brings the\n// container up if needed (silently — only the inner command's stdio is\n// passed through), then forwards the command verbatim to\n// `devcontainer exec`. The inner command's exit code is propagated.\nexport async function runInContainer(\n opts: RunInContainerOptions,\n): Promise<number> {\n if (opts.command.length === 0) {\n throw new Error(\n 'No command provided. Usage: `monoceros run <containername> -- <cmd> [args…]`.',\n );\n }\n assertContainerExists(opts.root);\n const spawnFn = opts.spawn ?? spawnDevcontainer;\n\n const upCode = await spawnFn(\n ['up', '--workspace-folder', opts.root, '--mount-workspace-git-root=false'],\n opts.root,\n { quiet: true },\n );\n if (upCode !== 0) return upCode;\n\n return spawnFn(\n [\n 'exec',\n '--workspace-folder',\n opts.root,\n '--mount-workspace-git-root=false',\n ...opts.command,\n ],\n opts.root,\n { interactive: true },\n );\n}\n","import { defineCommand } from 'citty';\nimport { consola } from 'consola';\nimport { containerDir } from '../config/paths.js';\nimport { runShell } from '../devcontainer/shell.js';\n\nexport const shellCommand = defineCommand({\n meta: {\n name: 'shell',\n group: 'run',\n description:\n 'Open an interactive bash session inside the named dev-container.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n async run({ args }) {\n try {\n const exitCode = await runShell({ root: containerDir(args.name) });\n process.exit(exitCode);\n } catch (err) {\n consola.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStart } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const startCommand = defineCommand({\n meta: {\n name: 'start',\n group: 'run',\n description:\n 'Bring the named dev-container up via `devcontainer up` (workspace + runServices, postCreate, features).',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n },\n run({ args }) {\n return dispatch(() => runStart({ root: containerDir(args.name) }));\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStatus } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const statusCommand = defineCommand({\n meta: {\n name: 'status',\n group: 'run',\n description:\n 'Show whether the compose services for the named dev-container are running.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n },\n run({ args }) {\n return dispatch(() =>\n runStatus({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n }),\n );\n },\n});\n","import { defineCommand } from 'citty';\nimport { containerDir } from '../config/paths.js';\nimport { runStop } from '../devcontainer/compose.js';\nimport { dispatch } from './_dispatch.js';\n\nexport const stopCommand = defineCommand({\n meta: {\n name: 'stop',\n group: 'run',\n description:\n 'Stop the compose services for the named dev-container. Volumes are preserved.',\n },\n args: {\n name: {\n type: 'positional',\n description:\n 'Container name (yml in $MONOCEROS_HOME/container-configs/).',\n required: true,\n },\n service: {\n type: 'string',\n description:\n 'Restrict to a single compose service (e.g. postgres). Defaults to all.',\n },\n },\n run({ args }) {\n return dispatch(() =>\n runStop({\n root: containerDir(args.name),\n ...(typeof args.service === 'string' ? { service: args.service } : {}),\n }),\n );\n },\n});\n"],"mappings":"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","names":["entry","main","path","defineCommand","consola","fs","existsSync","fs","path","APT_PACKAGE_NAME_RE","FEATURE_REF_RE","INSTALL_URL_RE","REPO_URL_RE","REPO_PATH_RE","entry","path","existsSync","fs","entry","path","path","entry","fs","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","existsSync","fs","consola","fs","z","parseDocument","z","fs","parseDocument","fs","path","path","fs","entry","ANSI_BOLD","ANSI_UNDERLINE","ANSI_CYAN","ANSI_GREY","ANSI_RESET","isTty","ANSI_RESET","ANSI_BOLD","ANSI_UNDERLINE","ANSI_CYAN","ANSI_GREY","bold","underline","cyan","spawn","existsSync","path","consola","readFileSync","path","readFileSync","path","child","spawn","path","existsSync","consola","spawn","fs","path","spawn","cyan","path","fs","cyan","spawn","spawn","cyan","spawn","spawn","fs","path","consola","consola","existsSync","fs","cyan","entry","consola","defineCommand","defineCommand","defineCommand","consola","existsSync","fs","consola","existsSync","fs","path","z","z","existsSync","fs","entry","path","existsSync","readFileSync","path","path","existsSync","readFileSync","consola","existsSync","fs","defineCommand","consola","defineCommand","consola","defineCommand","consola","isTty","first","defineCommand","defineCommand","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","existsSync","fs","path","consola","consola","existsSync","path","fs","defineCommand","consola","defineCommand","consola","existsSync","fs","path","consola","consola","path","existsSync","fs","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","consola","existsSync","path","existsSync","path","defineCommand","consola","defineCommand","consola","defineCommand","consola","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand","defineCommand"]}