paratix 0.10.0 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/chunk-ZXRWWALU.js +19071 -0
- package/dist/chunk-ZXRWWALU.js.map +1 -0
- package/dist/cli.js +5091 -224
- package/dist/cli.js.map +1 -1
- package/dist/{user-CJDqZC8n.d.ts → index-udpAybq3.d.ts} +637 -36
- package/dist/index.d.ts +51 -7
- package/dist/index.js +965 -73
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +1 -119
- package/dist/modules/index.js +1 -2
- package/llm-guide.md +176 -35
- package/package.json +10 -8
- package/dist/chunk-47PTUZZR.js +0 -495
- package/dist/chunk-47PTUZZR.js.map +0 -1
- package/dist/chunk-M7GETOJ5.js +0 -6237
- package/dist/chunk-M7GETOJ5.js.map +0 -1
- package/dist/chunk-NRDLYHJL.js +0 -1866
- package/dist/chunk-NRDLYHJL.js.map +0 -1
- package/dist/cli.d.ts +0 -62
- package/dist/types-Cl2Muw1x.d.ts +0 -254
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/conditionalModules.ts","../src/builtins.ts","../src/recipe.ts","../src/server.ts"],"sourcesContent":["import { mergeEnvironmentFromMeta } from \"./meta.js\"\nimport { detectPackageManager, isPackageInstalled } from \"./modules/package.js\"\nimport { shellQuote } from \"./ssh.js\"\nimport {\n type Environment,\n type Module,\n type ModuleMetaEntry,\n type ModuleResult,\n NEEDS_APPLY,\n type SshConnection,\n} from \"./types.js\"\n\ntype ConditionalApplyState = {\n environment: Environment\n flushSignals?: true\n meta: ModuleMetaEntry[]\n status: \"changed\" | \"ok\" | \"skipped\"\n stopRun?: true\n}\n\nfunction createConditionalApplyState(environment: Environment): ConditionalApplyState {\n return { environment: { ...environment }, meta: [], status: \"ok\" }\n}\n\nfunction markConditionalApplyChanged(state: ConditionalApplyState): ConditionalApplyState {\n return { ...state, status: \"changed\" }\n}\n\nasync function executeConditionalApply(parameters: {\n dryRun: boolean\n environment: Environment\n module: Module\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun, environment, module, ssh } = parameters\n if (dryRun && module._applyDryRun != null) {\n return module._applyDryRun(ssh, environment)\n }\n return module.apply(ssh, environment)\n}\n\nfunction shouldExecuteConditionalApply(module: Module, dryRun: boolean): boolean {\n if (!dryRun) return true\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nasync function mergeConditionalApplyState(\n state: ConditionalApplyState,\n result: ModuleResult\n): Promise<ConditionalApplyState> {\n const environment = await mergeEnvironmentFromMeta(state.environment, result.meta)\n return {\n environment,\n flushSignals: result._flushSignals === true ? true : state.flushSignals,\n meta: result.meta == null ? state.meta : [...state.meta, ...result.meta],\n status: result.status === \"changed\" ? \"changed\" : state.status,\n stopRun: result._stopRun === true ? true : state.stopRun,\n }\n}\n\nasync function applyConditionalModules(parameters: {\n dryRun?: boolean\n environment: Environment\n modules: Module[]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun = false, modules, ssh } = parameters\n let state = createConditionalApplyState(parameters.environment)\n\n for (const currentModule of modules) {\n // eslint-disable-next-line no-await-in-loop\n const checkResult = await currentModule.check(ssh, state.environment)\n if (checkResult === \"ok\") continue\n\n if (!shouldExecuteConditionalApply(currentModule, dryRun)) {\n state = markConditionalApplyChanged(state)\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop -- conditional modules must preserve ordered env propagation\n const result = await executeConditionalApply({\n dryRun,\n environment: state.environment,\n module: currentModule,\n ssh,\n })\n if (result.status === \"failed\") return result\n // eslint-disable-next-line no-await-in-loop -- downstream env must see each module's meta in order\n state = await mergeConditionalApplyState(state, result)\n if (state.stopRun === true) break\n }\n\n return {\n _flushSignals: state.flushSignals,\n _stopRun: state.stopRun,\n meta: state.meta.length === 0 ? undefined : state.meta,\n status: state.status,\n }\n}\n\nfunction shouldExecuteConditionalDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction whenNeedsDryRunApply(modules: Module[]): boolean {\n return modules.some((module) => shouldExecuteConditionalDryRun(module))\n}\n\nasync function checkConditionalModules(\n modules: Module[],\n ssh: null | SshConnection,\n environment: Environment\n): Promise<\"needs-apply\" | \"ok\"> {\n const currentEnvironment = { ...environment }\n for (const currentModule of modules) {\n // eslint-disable-next-line no-await-in-loop\n const result = await currentModule.check(ssh, currentEnvironment)\n if (result === NEEDS_APPLY) {\n return NEEDS_APPLY\n }\n }\n return \"ok\"\n}\n\ntype Condition = (ssh: null | SshConnection, environment: Environment) => boolean | Promise<boolean>\n\nfunction createWhenDryRunApply(\n condition: Condition,\n modules: Module[],\n needsDryRunApply: boolean\n): ((ssh: null | SshConnection, environment: Environment) => Promise<ModuleResult>) | undefined {\n if (!needsDryRunApply) return undefined\n return async (ssh: null | SshConnection, environment: Environment) => {\n if (!(await condition(ssh, environment))) {\n return { status: \"skipped\" as const }\n }\n return applyConditionalModules({ dryRun: true, environment, modules, ssh })\n }\n}\n\nexport function createConditionalModule(parameters: {\n condition: Condition\n modules: Module[]\n name: string\n}): Module {\n const needsDryRunApply = whenNeedsDryRunApply(parameters.modules)\n const applyDryRun = createWhenDryRunApply(\n parameters.condition,\n parameters.modules,\n needsDryRunApply\n )\n\n return {\n ...(parameters.modules.some((module) => module._dryRunBlocker === true)\n ? { _dryRunBlocker: true as const }\n : {}),\n ...(parameters.modules.some((module) => module._dryRunMetaProducer === true)\n ? { _dryRunMetaProducer: true as const }\n : {}),\n ...(applyDryRun == null ? {} : { _applyDryRun: applyDryRun }),\n async apply(ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (!(await parameters.condition(ssh, environment))) {\n return { status: \"skipped\" }\n }\n return applyConditionalModules({ environment, modules: parameters.modules, ssh })\n },\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n if (!(await parameters.condition(ssh, environment))) {\n return \"ok\"\n }\n return checkConditionalModules(parameters.modules, ssh, environment)\n },\n name: parameters.name,\n }\n}\n\nfunction filesystemTypeName(testFlag: \"-d\" | \"-f\" | \"-L\" | \"-S\"): string {\n switch (testFlag) {\n case \"-d\": {\n return \"path\"\n }\n case \"-f\": {\n return \"file\"\n }\n case \"-L\": {\n return \"symlink\"\n }\n case \"-S\": {\n return \"socket\"\n }\n }\n}\n\nexport function createFilesystemGuard(parameters: {\n invert: boolean\n modules: Module[]\n path: string\n testFlag: \"-d\" | \"-f\" | \"-L\" | \"-S\"\n}): Module {\n const typeName = filesystemTypeName(parameters.testFlag)\n return createConditionalModule({\n condition: async (ssh) => {\n if (ssh == null) return false\n const exists = await ssh.test(`test ${parameters.testFlag} ${shellQuote(parameters.path)}`)\n return parameters.invert ? !exists : exists\n },\n modules: parameters.modules,\n name: `when.${typeName}${parameters.invert ? \"Missing\" : \"Exists\"}: ${parameters.path}`,\n })\n}\n\nexport function createCommandGuard(\n commandName: string,\n invert: boolean,\n modules: Module[]\n): Module {\n return createConditionalModule({\n condition: async (ssh) => {\n if (ssh == null) return false\n const exists = await ssh.test(`command -v ${shellQuote(commandName)} >/dev/null 2>&1`)\n return invert ? !exists : exists\n },\n modules,\n name: `when.command${invert ? \"Missing\" : \"Exists\"}: ${commandName}`,\n })\n}\n\nexport function createPackageGuard(\n packageName: string,\n invert: boolean,\n modules: Module[]\n): Module {\n return createConditionalModule({\n condition: async (ssh) => {\n if (ssh == null) return false\n const pm = await detectPackageManager(ssh)\n if (pm == null) return false\n const installed = await isPackageInstalled(ssh, pm, packageName)\n return invert ? !installed : installed\n },\n modules,\n name: `when.package${invert ? \"Absent\" : \"Installed\"}: ${packageName}`,\n })\n}\n","import {\n createCommandGuard,\n createConditionalModule,\n createFilesystemGuard,\n createPackageGuard,\n} from \"./conditionalModules.js\"\nimport { failed } from \"./moduleFailure.js\"\nimport {\n type Environment,\n type Module,\n type ModuleResult,\n NEEDS_APPLY,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Fail the run if a condition on the current env is not satisfied.\n *\n * The check phase evaluates the condition; if it returns `false`, apply\n * marks the module as failed, which aborts the parent recipe.\n *\n * @param condition - A predicate receiving the current env at run time.\n * @param message - Human-readable description shown in the run output.\n * @returns A Module that asserts the condition.\n *\n * @example\n * assert(env => !!env[\"APP_SECRET\"], \"APP_SECRET must be set\")\n */\nexport function assert(condition: (environment: Environment) => boolean, message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (condition(environment)) {\n return { status: \"ok\" }\n }\n return failed(`[assert] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return condition(environment) ? \"ok\" : NEEDS_APPLY\n },\n name: `assert: ${message}`,\n }\n}\n\n/**\n * Print a static debug message to the console during the apply phase.\n * Always runs (never skipped by the check phase).\n *\n * @param message - The message to print, prefixed with `[debug]`.\n * @returns A Module that prints a debug message.\n */\nexport function debug(message: string): Module {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n console.log(` [debug] ${message}`)\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `debug: ${message}`,\n }\n}\n\n/**\n * Unconditionally abort the run with a failed status and an error message.\n * Useful as a sentinel at the end of a conditional branch.\n *\n * @param message - The message to print, prefixed with `[fail]`.\n * @returns A Module that fails the run.\n */\nexport function fail(message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return failed(`[fail] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `fail: ${message}`,\n }\n}\n\n/**\n * Pause execution and wait for the operator to press Enter.\n * Useful for interactive confirmation during a run.\n *\n * @param message - Prompt shown to the operator. Defaults to `\"Press enter to continue...\"`.\n * @returns A Module that pauses execution.\n */\nexport function pause(message?: string): Module {\n return {\n async apply(): Promise<ModuleResult> {\n const promptText = message ?? \"Press enter to continue...\"\n process.stdout.write(` [pause] ${promptText} `)\n\n await new Promise<void>((resolve) => {\n process.stdin.once(\"data\", () => {\n process.stdin.pause()\n resolve()\n })\n })\n\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: message == null ? \"pause\" : `pause: ${message}`,\n }\n}\n\nfunction isFirstRunEnabled(environment: Environment): boolean {\n return environment.PARATIX_FIRST_RUN === \"true\" || environment.FIRST_RUN === true\n}\n\n/**\n * Built-ins related to the explicit first-run bootstrap stage.\n */\nexport const firstRun = {\n /**\n * Stop the current run successfully when Paratix was invoked with `--first-run`.\n * Useful as an explicit stage boundary in scaffolded playbooks.\n *\n * @param message - Optional note shown in the module name.\n * @returns A local module that stops the run only during first-run execution.\n */\n stop(message?: string): Module {\n const moduleName = message == null ? \"firstRun.stop\" : `firstRun.stop: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (!isFirstRunEnabled(environment)) {\n return { status: \"ok\" }\n }\n\n return {\n _dryRunDetail: \"(first-run stop)\",\n _stopRun: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return isFirstRunEnabled(environment) ? NEEDS_APPLY : \"ok\"\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\n/**\n * Built-ins for explicit signal checkpoints.\n */\nexport const signals = {\n /**\n * Flush all currently pending signals for the active scope.\n * Signals remain scope-local:\n * - in a recipe, this flushes that recipe's signals\n * - at top level, this flushes `server(...).signals`\n *\n * @param message - Optional note shown in the module name.\n * @returns A local control module that requests an immediate signal flush.\n */\n flush(message?: string): Module {\n const moduleName = message == null ? \"signals.flush\" : `signals.flush: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return {\n _dryRunDetail: \"(dry-run, pending signals not executed)\",\n _flushSignals: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\n/**\n * Run one or more modules only when a runtime condition is met.\n * When the condition is `false`, the whole group is skipped without running\n * any child checks or applies.\n *\n * @param condition - A predicate evaluated against the current env at run time.\n * @param modules - One or more modules to run when the condition is `true`.\n * @returns A Module that conditionally runs the inner modules.\n *\n * @example\n * when(env => env[\"DEPLOY_ENV\"] === \"production\", service.enabled(\"fail2ban\"))\n */\nfunction baseWhen(condition: (environment: Environment) => boolean, ...modules: Module[]): Module {\n return createConditionalModule({\n condition: (_ssh, environment) => condition(environment),\n modules,\n name: `when: conditional (${modules.length} modules)`,\n })\n}\n\ntype WhenFunction = {\n commandExists: (commandName: string, ...modules: Module[]) => Module\n commandMissing: (commandName: string, ...modules: Module[]) => Module\n fileExists: (path: string, ...modules: Module[]) => Module\n fileMissing: (path: string, ...modules: Module[]) => Module\n packageAbsent: (packageName: string, ...modules: Module[]) => Module\n packageInstalled: (packageName: string, ...modules: Module[]) => Module\n pathExists: (path: string, ...modules: Module[]) => Module\n pathMissing: (path: string, ...modules: Module[]) => Module\n socketExists: (path: string, ...modules: Module[]) => Module\n socketMissing: (path: string, ...modules: Module[]) => Module\n symlinkExists: (path: string, ...modules: Module[]) => Module\n symlinkMissing: (path: string, ...modules: Module[]) => Module\n} & typeof baseWhen\n\nexport const when: WhenFunction = Object.assign(baseWhen, {\n commandExists: (commandName: string, ...modules: Module[]) =>\n createCommandGuard(commandName, false, modules),\n commandMissing: (commandName: string, ...modules: Module[]) =>\n createCommandGuard(commandName, true, modules),\n fileExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-f\" }),\n fileMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-f\" }),\n packageAbsent: (packageName: string, ...modules: Module[]) =>\n createPackageGuard(packageName, true, modules),\n packageInstalled: (packageName: string, ...modules: Module[]) =>\n createPackageGuard(packageName, false, modules),\n pathExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-d\" }),\n pathMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-d\" }),\n socketExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-S\" }),\n socketMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-S\" }),\n symlinkExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-L\" }),\n symlinkMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-L\" }),\n})\n","/* eslint-disable max-lines -- recipe orchestration intentionally stays co-located */\nimport { isEnvironmentMetaEntry, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n printRecipeModuleResult,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\nimport { runSignalModules, type SignalHooks } from \"./signalOrchestration.js\"\nimport { CommandError } from \"./sshHelpers.js\"\nimport {\n type Environment,\n type Module,\n type ModuleMetaEntry,\n type ModuleResult,\n type ModuleStatus,\n NEEDS_APPLY,\n type OrchestrationStep,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Internal representation of a recipe module.\n * The `_isRecipe` flag lets the runner distinguish recipes from leaf modules.\n * @internal\n */\nexport type RecipeModule = {\n _isRecipe: true\n _modules: Module[]\n _signals?: Module[]\n apply: (\n ssh: null | SshConnection,\n environment: Environment,\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ) => Promise<ModuleResult>\n} & Module\n\ntype RecipeState = {\n env: Environment\n meta?: ModuleMetaEntry[]\n signalsPending: boolean\n status: Exclude<ModuleStatus, \"skipped\">\n stopRun?: true\n}\n\ntype ExecuteModulesParameters = {\n environment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals?: Module[]\n verbose?: boolean\n}\n\ntype RecipeLoopStepResult =\n | { kind: \"break\"; state: RecipeState }\n | { kind: \"continue\"; state: RecipeState }\n\nconst INTERRUPTED_BEFORE_APPLY = Symbol(\"recipe-interrupted-before-apply\")\n\nfunction isRecipeModuleLike(module: Module): boolean {\n return (module as { _isRecipe?: boolean } & Module)._isRecipe === true\n}\n\nfunction printRecipeChildResult(module: Module, result: ModuleResult): void {\n if (isRecipeModuleLike(module)) {\n printRecipeModuleResult(module.name, result.status, result.detail)\n return\n }\n\n printModuleResult(module.name, result.status, result.detail)\n}\n\nfunction applyRecipeStepToState(\n state: RecipeState,\n step: OrchestrationStep,\n preserveControlPlaneMeta: boolean\n): RecipeState {\n let stepMeta: ModuleMetaEntry[] | undefined\n if (step.meta == null) {\n stepMeta = undefined\n } else if (preserveControlPlaneMeta) {\n stepMeta = step.meta\n } else {\n stepMeta = step.meta.filter(isEnvironmentMetaEntry)\n }\n const nextMeta = stepMeta == null ? (state.meta ?? []) : [...(state.meta ?? []), ...stepMeta]\n let nextStatus = state.status\n if (step.status === \"failed\") nextStatus = \"failed\"\n else if (step.status === \"changed\") nextStatus = \"changed\"\n\n return {\n env: step.env,\n meta: nextMeta,\n signalsPending: step.status === \"changed\" ? true : state.signalsPending,\n status: nextStatus,\n stopRun: step._stopRun === true ? true : state.stopRun,\n }\n}\n\n/**\n * Recipes run their own check→apply loop, separate from the runner's\n * `runModuleLoop` in runner.ts. This is intentional: recipes execute as a\n * single nested module inside the runner, so SSH-lifecycle concerns\n * (port-change reconnects, reboot handling), shutdown-signal guards,\n * dry-run mode and stats tracking are the runner's responsibility and\n * must not be duplicated here.\n *\n * @param parameters - Parameters for executing one child module.\n * @param parameters.targetModule - The module to check and conditionally apply.\n * @param parameters.ssh - Active SSH connection, or `null` for local modules.\n * @param parameters.currentEnvironment - Environment values available to the module.\n * @param parameters.shutdownSignal - Optional shutdown getter used to suppress new apply steps.\n * @param parameters.verbose - Whether verbose command diagnostics should be printed.\n * @returns The updated environment and status, or `null` if the module was already ok.\n */\nasync function executeOneModule(parameters: {\n currentEnvironment: Environment\n shutdownSignal?: () => NodeJS.Signals | null\n ssh: null | SshConnection\n targetModule: Module\n verbose?: boolean\n}): Promise<null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY> {\n const { currentEnvironment, ssh, targetModule } = parameters\n const verbose = parameters.verbose ?? false\n const connection = targetModule.local === true ? null : ssh\n const checkResult = await checkRecipeChild(targetModule, connection, currentEnvironment)\n\n if (checkResult === \"ok\") {\n printModuleResult(targetModule.name, \"ok\")\n return null\n }\n\n if ((parameters.shutdownSignal?.() ?? null) != null) {\n return INTERRUPTED_BEFORE_APPLY\n }\n\n const result = await targetModule.apply(connection, currentEnvironment)\n printRecipeChildResult(targetModule, result)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n\n const environment = await mergeEnvironmentFromMeta(currentEnvironment, result.meta)\n return {\n _flushSignals: result._flushSignals,\n _stopRun: result._stopRun,\n env: environment,\n meta: result.meta,\n status: result.status,\n }\n}\n\nasync function checkRecipeChild(\n targetModule: Module,\n connection: null | SshConnection,\n currentEnvironment: Environment\n): Promise<\"needs-apply\" | \"ok\"> {\n startModuleSpinner(targetModule.name)\n return targetModule.check(connection, currentEnvironment)\n}\n\nasync function applyExecutedRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n state: RecipeState\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n}): Promise<null | RecipeState> {\n if (parameters.step == null) return parameters.state\n if (parameters.step === INTERRUPTED_BEFORE_APPLY) return null\n\n if (parameters.onChildStep != null) {\n await parameters.onChildStep(parameters.step)\n }\n\n return applyRecipeStepToState(\n parameters.state,\n parameters.step,\n parameters.preserveControlPlaneMeta\n )\n}\n\nfunction failedRecipeState(environment: Environment): RecipeState {\n return {\n env: environment,\n meta: undefined,\n signalsPending: false,\n status: \"failed\",\n }\n}\n\nfunction annotateRecipeChildError(moduleName: string, error: unknown): Error {\n const prefix = `[${moduleName}] `\n if (error instanceof CommandError) {\n return new CommandError(`${prefix}${error.message}`, error.fullStdout, error.fullStderr)\n }\n if (error instanceof Error) {\n return new Error(`${prefix}${error.message}`)\n }\n return new Error(`${prefix}${String(error)}`)\n}\n\ntype RecipeChildExecution =\n | { kind: \"failed\"; state: RecipeState }\n | {\n kind: \"step\"\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n }\n\nasync function executeRecipeChildStep(parameters: {\n currentEnvironment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n targetModule: Module\n verbose: boolean\n}): Promise<RecipeChildExecution> {\n try {\n return { kind: \"step\", step: await executeOneModule(parameters) }\n } catch (error) {\n if (parameters.shutdownSignal() != null) {\n return { kind: \"step\", step: INTERRUPTED_BEFORE_APPLY }\n }\n printModuleResult(parameters.targetModule.name, \"failed\")\n printCommandFailure(error, parameters.verbose)\n return { kind: \"failed\", state: failedRecipeState(parameters.currentEnvironment) }\n }\n}\n\nasync function processRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals?: Module[]\n ssh: null | SshConnection\n state: RecipeState\n step: RecipeChildExecution\n verbose: boolean\n}): Promise<RecipeLoopStepResult> {\n if (parameters.step.kind === \"failed\") {\n return { kind: \"break\", state: parameters.step.state }\n }\n\n const nextState = await applyExecutedRecipeStep({\n onChildStep: parameters.onChildStep,\n preserveControlPlaneMeta: parameters.preserveControlPlaneMeta,\n state: parameters.state,\n step: parameters.step.step,\n })\n if (nextState == null) {\n return { kind: \"break\", state: parameters.state }\n }\n\n let state = nextState\n if (shouldFlushRecipeSignals(parameters.step.step, state, parameters.signals)) {\n const signalStatus = await flushPendingRecipeSignals({\n environment: state.env,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n state = applyRecipeSignalStatus(state, signalStatus)\n }\n\n if (state.status === \"failed\" || state.stopRun === true) {\n return { kind: \"break\", state }\n }\n\n return { kind: \"continue\", state }\n}\n\nasync function executeModules(\n modules: Module[],\n ssh: null | SshConnection,\n parameters: ExecuteModulesParameters\n): Promise<RecipeState> {\n const onChildStep = parameters.onChildStep\n const preserveControlPlaneMeta = onChildStep == null\n const shutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let state: RecipeState = {\n env: { ...parameters.environment },\n meta: undefined,\n signalsPending: false,\n status: \"ok\",\n }\n\n for (const currentModule of modules) {\n if (shutdownSignal() != null) break\n // eslint-disable-next-line no-await-in-loop\n const step = await executeRecipeChildStep({\n currentEnvironment: state.env,\n shutdownSignal,\n ssh,\n targetModule: currentModule,\n verbose,\n })\n // eslint-disable-next-line no-await-in-loop\n const processedStep = await processRecipeStep({\n onChildStep,\n onSignalStep: parameters.onSignalStep,\n preserveControlPlaneMeta,\n shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh,\n state,\n step,\n verbose,\n })\n state = processedStep.state\n if (processedStep.kind === \"break\") return state\n }\n\n return state\n}\n\nasync function triggerSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return runSignalModules({\n environment: parameters.environment,\n hooks: parameters.signalHooks,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n}\n\nfunction shouldFlushRecipeSignals(\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY,\n state: RecipeState,\n signals?: Module[]\n): signals is Module[] {\n return (\n step != null &&\n step !== INTERRUPTED_BEFORE_APPLY &&\n step._flushSignals === true &&\n state.signalsPending &&\n signals != null\n )\n}\n\nfunction applyRecipeSignalStatus(\n state: RecipeState,\n signalStatus: \"changed\" | \"failed\"\n): RecipeState {\n return {\n ...state,\n signalsPending: false,\n status: signalStatus === \"failed\" ? \"failed\" : state.status,\n }\n}\n\nasync function flushPendingRecipeSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return triggerSignals(parameters)\n}\n\nasync function applyRecipe(parameters: {\n environment: Environment\n modules: Module[]\n name: string\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n signals?: Module[]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n return withRecipeOutputScope(async () => {\n const shutdownSignal = parameters.options?.shutdownSignal\n const verbose = parameters.options?.verbose ?? false\n printRecipeHeader(parameters.name)\n const state = await executeModules(parameters.modules, parameters.ssh, {\n environment: parameters.environment,\n onChildStep: parameters.options?.onChildStep,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n verbose,\n })\n\n if (shouldRunRecipeSignalsAtEnd(state, parameters.signals)) {\n state.status = await triggerSignals({\n environment: state.env,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose,\n })\n }\n\n return {\n _stopRun: state.stopRun,\n meta: state.meta,\n status: state.status,\n }\n })\n}\n\nfunction shouldRunRecipeSignalsAtEnd(state: RecipeState, signals?: Module[]): signals is Module[] {\n return state.signalsPending && state.status === \"changed\" && signals != null\n}\n\n/**\n * Group a list of modules into a named, self-contained recipe.\n *\n * The recipe runs each child module in order, short-circuits on the first\n * failure, and propagates `meta` env values from one module to all subsequent\n * ones. If any child reports `\"changed\"`, the optional `signals` are triggered\n * at the end of the run.\n *\n * @param name - Display name shown in the run output header.\n * @param modules - Ordered list of modules to execute.\n * @param options - Optional recipe configuration.\n * @param options.signals - Modules to fire when at least one child changed state.\n * @returns A RecipeModule that groups the child modules.\n *\n * @example\n * export const nginxRecipe = recipe(\"nginx\", [\n * apt.installed(\"nginx\"),\n * file.template(\"/etc/nginx/nginx.conf\", \"./files/nginx.conf.tmpl\"),\n * service.enabled(\"nginx\"),\n * ], {\n * signals: [service.reload(\"nginx\")],\n * });\n */\nexport function recipe(\n name: string,\n modules: Module[],\n options?: { signals?: Module[] }\n): RecipeModule {\n return {\n _isRecipe: true,\n _modules: modules,\n _signals: options?.signals,\n async apply(\n ssh: null | SshConnection,\n environment: Environment,\n parameters?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ): Promise<ModuleResult> {\n return applyRecipe({\n environment,\n modules,\n name,\n options: parameters,\n signals: options?.signals,\n ssh,\n })\n },\n\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n // Each child receives the original environment — no meta propagation,\n // because check() never calls apply() and therefore produces no meta.\n for (const childModule of modules) {\n const connection = childModule.local === true ? null : ssh\n let result: \"needs-apply\" | \"ok\"\n try {\n // eslint-disable-next-line no-await-in-loop\n result = await childModule.check(connection, environment)\n } catch (error) {\n throw annotateRecipeChildError(childModule.name, error)\n }\n if (result === NEEDS_APPLY) return NEEDS_APPLY\n }\n return \"ok\"\n },\n\n name,\n }\n}\n","import type { ServerDefinition } from \"./types.js\"\n\nimport { validateSshConfig } from \"./serverDefinitionValidation.js\"\n\n/**\n * Define a server and validate its configuration at construction time.\n *\n * This is a thin identity function whose only purpose is to provide\n * type-safe validation with descriptive error messages before the runner\n * ever attempts an SSH connection.\n *\n * @param config - The server definition to validate and return.\n * @returns The validated server definition.\n * @throws {Error} When any required field is missing or empty.\n *\n * @example\n * export default server({\n * name: \"web-01\",\n * host: \"10.0.0.1\",\n * ssh: { user: \"root\", ports: [22], privateKey: \"~/.ssh/id_ed25519\" }, // \"~\" is expanded\n * run: [apt.installed(\"nginx\")],\n * });\n */\nexport function server(config: ServerDefinition): ServerDefinition {\n if (config.host.length === 0) {\n throw new Error(\"ServerDefinition: host is required\")\n }\n if (config.name.length === 0) {\n throw new Error(\"ServerDefinition: name is required\")\n }\n validateSshConfig(config.ssh)\n if (config.run.length === 0) {\n throw new Error(\"ServerDefinition: run must contain at least one module\")\n }\n\n return config\n}\n"],"mappings":"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","names":["ssh","ssh","signals"]}
|
|
1
|
+
{"version":3,"sources":["../src/conditionalModules.ts","../src/conditionalGuards.ts","../src/builtins.ts","../src/firstRunContext.ts","../src/dryRunDispatch.ts","../src/output.ts","../src/outputFormatting.ts","../src/dryRunRecipe.ts","../src/signalOrchestration.ts","../src/recipe.ts","../src/server.ts"],"sourcesContent":["import { createNullPrototypeEnvironment } from \"./environment.js\"\nimport { isEnvironmentMetaEntry, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n type Environment,\n type Module,\n type ModuleApplyOptions,\n type ModuleMetaEntry,\n type ModuleResult,\n NEEDS_APPLY,\n type SshConnection,\n} from \"./types.js\"\n\ntype ConditionalApplyState = {\n environment: Environment\n flushSignals?: true\n meta: ModuleMetaEntry[]\n status: \"changed\" | \"ok\" | \"skipped\"\n stopRun?: true\n}\n\ntype ConditionalApplyStepResult =\n | { kind: \"break\"; state: ConditionalApplyState }\n | { kind: \"continue\"; state: ConditionalApplyState }\n | { kind: \"failed\"; result: ModuleResult }\n\nfunction createConditionalApplyState(environment: Environment): ConditionalApplyState {\n // R-0000087: preserve the null-prototype guarantee that\n // R-0000069/R-0000070/R-0000074 establish for the runner-level\n // environment. Plain object spread (`{ ...environment }`) would create a\n // map with `Object.prototype`, exposing the first child module of every\n // when(...) guard to a polluted-fähige environment. Mirror the\n // recipe.ts:302 / meta.ts:214 approach.\n return {\n environment: Object.assign(createNullPrototypeEnvironment(), environment),\n meta: [],\n status: \"ok\",\n }\n}\n\nfunction markConditionalApplyChanged(state: ConditionalApplyState): ConditionalApplyState {\n return { ...state, status: \"changed\" }\n}\n\nasync function executeConditionalApply(parameters: {\n dryRun: boolean\n environment: Environment\n module: Module\n onChildStep?: ModuleApplyOptions[\"onChildStep\"]\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun, environment, module, ssh } = parameters\n if (dryRun && module._applyDryRun != null) {\n return module._applyDryRun(ssh, environment, {\n shutdownSignal: parameters.shutdownSignal,\n })\n }\n if (module._supportsChildStepHook === true) {\n return module.apply(ssh, environment, {\n onChildStep: parameters.onChildStep,\n shutdownSignal: parameters.shutdownSignal,\n })\n }\n return module.apply(ssh, environment)\n}\n\nfunction shouldExecuteConditionalApply(module: Module, dryRun: boolean): boolean {\n if (!dryRun) return true\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction getConditionalChildConnection(\n module: Module,\n ssh: null | SshConnection\n): null | SshConnection {\n return module.local === true ? null : ssh\n}\n\nasync function mergeConditionalApplyState(\n preserveControlPlaneMeta: boolean,\n state: ConditionalApplyState,\n result: ModuleResult\n): Promise<ConditionalApplyState> {\n const environment = await mergeEnvironmentFromMeta(state.environment, result.meta)\n const resultMeta =\n result.meta == null || preserveControlPlaneMeta\n ? result.meta\n : result.meta.filter(isEnvironmentMetaEntry)\n return {\n environment,\n flushSignals: result._flushSignals === true ? true : state.flushSignals,\n meta: resultMeta == null ? state.meta : [...state.meta, ...resultMeta],\n status: result.status === \"changed\" ? \"changed\" : state.status,\n stopRun: result._stopRun === true ? true : state.stopRun,\n }\n}\n\nasync function notifyConditionalChildStep(parameters: {\n environment: Environment\n onChildStep?: ModuleApplyOptions[\"onChildStep\"]\n result: ModuleResult\n}): Promise<void> {\n if (parameters.onChildStep == null) return\n await parameters.onChildStep({\n _flushSignals: parameters.result._flushSignals,\n _stopRun: parameters.result._stopRun,\n env: parameters.environment,\n meta: parameters.result.meta,\n status: parameters.result.status,\n })\n}\n\nasync function processConditionalApplyResult(parameters: {\n onChildStep?: ModuleApplyOptions[\"onChildStep\"]\n preserveControlPlaneMeta: boolean\n result: ModuleResult\n state: ConditionalApplyState\n}): Promise<ConditionalApplyState> {\n const state = await mergeConditionalApplyState(\n parameters.preserveControlPlaneMeta,\n parameters.state,\n parameters.result\n )\n await notifyConditionalChildStep({\n environment: state.environment,\n onChildStep: parameters.onChildStep,\n result: parameters.result,\n })\n return state\n}\n\nasync function applyConditionalModules(parameters: {\n dryRun?: boolean\n environment: Environment\n modules: Module[]\n onChildStep?: ModuleApplyOptions[\"onChildStep\"]\n shutdownSignal?: ModuleApplyOptions[\"shutdownSignal\"]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun = false, modules, ssh } = parameters\n const preserveControlPlaneMeta = parameters.onChildStep == null\n const shutdownSignal = parameters.shutdownSignal ?? (() => null)\n let state = createConditionalApplyState(parameters.environment)\n\n for (const currentModule of modules) {\n // eslint-disable-next-line no-await-in-loop\n const step = await applyConditionalModuleStep({\n currentModule,\n dryRun,\n onChildStep: parameters.onChildStep,\n preserveControlPlaneMeta,\n shutdownSignal,\n ssh,\n state,\n })\n if (step.kind === \"failed\") return step.result\n state = step.state\n if (step.kind === \"break\") break\n }\n\n return {\n _flushSignals: state.flushSignals,\n _stopRun: state.stopRun,\n meta: state.meta.length === 0 ? undefined : state.meta,\n status: state.status,\n }\n}\n\nasync function applyConditionalModuleStep(parameters: {\n currentModule: Module\n dryRun: boolean\n onChildStep?: ModuleApplyOptions[\"onChildStep\"]\n preserveControlPlaneMeta: boolean\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n state: ConditionalApplyState\n}): Promise<ConditionalApplyStepResult> {\n if (parameters.shutdownSignal() != null) {\n return { kind: \"break\", state: parameters.state }\n }\n const connection = getConditionalChildConnection(parameters.currentModule, parameters.ssh)\n const checkResult = await parameters.currentModule.check(connection, parameters.state.environment)\n if (checkResult === \"ok\") return { kind: \"continue\", state: parameters.state }\n if (parameters.shutdownSignal() != null) return { kind: \"break\", state: parameters.state }\n if (!shouldExecuteConditionalApply(parameters.currentModule, parameters.dryRun)) {\n return { kind: \"continue\", state: markConditionalApplyChanged(parameters.state) }\n }\n const result = await executeConditionalApply({\n dryRun: parameters.dryRun,\n environment: parameters.state.environment,\n module: parameters.currentModule,\n onChildStep: parameters.onChildStep,\n shutdownSignal: parameters.shutdownSignal,\n ssh: connection,\n })\n if (result.status === \"failed\") return { kind: \"failed\", result }\n const state = await processConditionalApplyResult({\n onChildStep: parameters.onChildStep,\n preserveControlPlaneMeta: parameters.preserveControlPlaneMeta,\n result,\n state: parameters.state,\n })\n return { kind: state.stopRun === true ? \"break\" : \"continue\", state }\n}\n\nfunction shouldExecuteConditionalDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction whenNeedsDryRunApply(modules: Module[]): boolean {\n return modules.some((module) => shouldExecuteConditionalDryRun(module))\n}\n\nasync function checkConditionalModules(\n modules: Module[],\n ssh: null | SshConnection,\n environment: Environment\n): Promise<\"needs-apply\" | \"ok\"> {\n // R-0000087: same null-prototype preservation as createConditionalApplyState\n // for the check-side traversal of when(...) guards.\n const currentEnvironment = Object.assign(createNullPrototypeEnvironment(), environment)\n for (const currentModule of modules) {\n const connection = getConditionalChildConnection(currentModule, ssh)\n // eslint-disable-next-line no-await-in-loop\n const result = await currentModule.check(connection, currentEnvironment)\n if (result === NEEDS_APPLY) {\n return NEEDS_APPLY\n }\n }\n return \"ok\"\n}\n\ntype Condition = (ssh: null | SshConnection, environment: Environment) => boolean | Promise<boolean>\n\nfunction createWhenDryRunApply(\n condition: Condition,\n modules: Module[],\n needsDryRunApply: boolean\n): Module[\"_applyDryRun\"] | undefined {\n if (!needsDryRunApply) return undefined\n return async (\n ssh: null | SshConnection,\n environment: Environment,\n options?: ModuleApplyOptions\n ) => {\n if (!(await condition(ssh, environment))) {\n return { status: \"skipped\" as const }\n }\n return applyConditionalModules({\n dryRun: true,\n environment,\n modules,\n shutdownSignal: options?.shutdownSignal,\n ssh,\n })\n }\n}\n\nexport function createConditionalModule(parameters: {\n condition: Condition\n modules: Module[]\n name: string\n}): Module {\n const needsDryRunApply = whenNeedsDryRunApply(parameters.modules)\n const applyDryRun = createWhenDryRunApply(\n parameters.condition,\n parameters.modules,\n needsDryRunApply\n )\n\n return {\n _supportsChildStepHook: true as const,\n ...(parameters.modules.some((module) => module._dryRunBlocker === true)\n ? { _dryRunBlocker: true as const }\n : {}),\n ...(parameters.modules.some((module) => module._dryRunMetaProducer === true)\n ? { _dryRunMetaProducer: true as const }\n : {}),\n ...(applyDryRun == null ? {} : { _applyDryRun: applyDryRun }),\n async apply(\n ssh: null | SshConnection,\n environment: Environment,\n options?: ModuleApplyOptions\n ): Promise<ModuleResult> {\n if (!(await parameters.condition(ssh, environment))) {\n return { status: \"skipped\" }\n }\n return applyConditionalModules({\n environment,\n modules: parameters.modules,\n onChildStep: options?.onChildStep,\n shutdownSignal: options?.shutdownSignal,\n ssh,\n })\n },\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n if (!(await parameters.condition(ssh, environment))) {\n return \"ok\"\n }\n return checkConditionalModules(parameters.modules, ssh, environment)\n },\n name: parameters.name,\n }\n}\n","import type { Module } from \"./types.js\"\n\nimport { createConditionalModule } from \"./conditionalModules.js\"\nimport { detectPackageManager, isPackageInstalled } from \"./modules/package.js\"\nimport { shellQuote } from \"./ssh.js\"\n\nfunction filesystemTypeName(testFlag: \"-d\" | \"-f\" | \"-L\" | \"-S\"): string {\n switch (testFlag) {\n case \"-d\": {\n return \"path\"\n }\n case \"-f\": {\n return \"file\"\n }\n case \"-L\": {\n return \"symlink\"\n }\n case \"-S\": {\n return \"socket\"\n }\n }\n}\n\nexport function createFilesystemGuard(parameters: {\n invert: boolean\n modules: Module[]\n path: string\n testFlag: \"-d\" | \"-f\" | \"-L\" | \"-S\"\n}): Module {\n const typeName = filesystemTypeName(parameters.testFlag)\n return createConditionalModule({\n async condition(ssh) {\n if (ssh == null) return false\n const exists = await ssh.test(`test ${parameters.testFlag} ${shellQuote(parameters.path)}`)\n return parameters.invert ? !exists : exists\n },\n modules: parameters.modules,\n name: `when.${typeName}${parameters.invert ? \"Missing\" : \"Exists\"}: ${parameters.path}`,\n })\n}\n\nexport function createCommandGuard(\n commandName: string,\n invert: boolean,\n modules: Module[]\n): Module {\n return createConditionalModule({\n async condition(ssh) {\n if (ssh == null) return false\n const exists = await ssh.test(`command -v ${shellQuote(commandName)} >/dev/null 2>&1`)\n return invert ? !exists : exists\n },\n modules,\n name: `when.command${invert ? \"Missing\" : \"Exists\"}: ${commandName}`,\n })\n}\n\nexport function createPackageGuard(\n packageName: string,\n invert: boolean,\n modules: Module[]\n): Module {\n return createConditionalModule({\n async condition(ssh) {\n if (ssh == null) return false\n const pm = await detectPackageManager(ssh)\n if (pm == null) return false\n const installed = await isPackageInstalled(ssh, pm, packageName)\n return invert ? !installed : installed\n },\n modules,\n name: `when.package${invert ? \"Absent\" : \"Installed\"}: ${packageName}`,\n })\n}\n","import {\n createCommandGuard,\n createFilesystemGuard,\n createPackageGuard,\n} from \"./conditionalGuards.js\"\nimport { createConditionalModule } from \"./conditionalModules.js\"\nimport { failed } from \"./moduleFailure.js\"\nimport { getRunnerAbortSignal, setRunnerAbortSignal } from \"./runnerAbortSignal.js\"\nimport {\n type Environment,\n type Module,\n type ModuleResult,\n NEEDS_APPLY,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Backward-compatible wrapper for {@link setRunnerAbortSignal}.\n *\n * R-0000027 introduced this function for the {@link pause} builtin; R-0000052\n * generalized the underlying holder so polling modules (e.g. `net.waitFor`)\n * can observe the same abort signal. The export is preserved so external\n * callers and tests that already imported `setPauseAbortSignal` continue to\n * work; new code should call {@link setRunnerAbortSignal} directly.\n *\n * @param signal - The abort signal whose `abort` event cancels active waits.\n */\nexport function setPauseAbortSignal(signal: AbortSignal | undefined): void {\n setRunnerAbortSignal(signal)\n}\n\n/**\n * Fail the run if a condition on the current env is not satisfied.\n *\n * The check phase evaluates the condition; if it returns `false`, apply\n * marks the module as failed, which aborts the parent recipe.\n *\n * @param condition - A predicate receiving the current env at run time.\n * @param message - Human-readable description shown in the run output.\n * @returns A Module that asserts the condition.\n *\n * @example\n * assert(env => !!env[\"APP_SECRET\"], \"APP_SECRET must be set\")\n */\nexport function assert(condition: (environment: Environment) => boolean, message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (condition(environment)) {\n return { status: \"ok\" }\n }\n return failed(`[assert] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return condition(environment) ? \"ok\" : NEEDS_APPLY\n },\n name: `assert: ${message}`,\n }\n}\n\n/**\n * Print a static debug message to the console during the apply phase.\n * Always runs (never skipped by the check phase).\n *\n * @param message - The message to print, prefixed with `[debug]`.\n * @returns A Module that prints a debug message.\n */\nexport function debug(message: string): Module {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n console.log(` [debug] ${message}`)\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `debug: ${message}`,\n }\n}\n\n/**\n * Unconditionally abort the run with a failed status and an error message.\n * Useful as a sentinel at the end of a conditional branch.\n *\n * @param message - The message to print, prefixed with `[fail]`.\n * @returns A Module that fails the run.\n */\nexport function fail(message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return failed(`[fail] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `fail: ${message}`,\n }\n}\n\n/**\n * Coerce an unknown abort reason into an `Error` instance.\n *\n * Falls back to `\"pause aborted\"` when the reason is `undefined`/`null`,\n * preserves any thrown `Error`, and wraps anything else with a string\n * representation that does not depend on `Object.prototype.toString`.\n *\n * @param reason - The {@link AbortSignal.reason}, if any.\n * @returns An `Error` with a meaningful message.\n */\nfunction normalizePauseAbortReason(reason: unknown): Error {\n if (reason instanceof Error) return reason\n if (reason === undefined || reason === null) return new Error(\"pause aborted\")\n if (typeof reason === \"string\") return new Error(reason)\n return new Error(\"pause aborted\")\n}\n\n/**\n * Wait for the operator to press Enter, observing an optional abort signal.\n *\n * Cleans up the stdin `data` listener and the abort listener regardless of\n * which one fires first, mirroring the cancellation semantics of\n * {@link import(\"./terminal.js\").promptTerminal}.\n *\n * @param abortSignal - Optional signal that, when aborted, rejects the wait.\n * @returns A promise that resolves on Enter or rejects on abort.\n */\nasync function waitForEnterOrAbort(abortSignal: AbortSignal | undefined): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let settled = false\n\n const onData = (chunk: unknown): void => {\n const input = Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : String(chunk)\n if (!input.includes(\"\\n\") && !input.includes(\"\\r\")) return\n if (settled) return\n settled = true\n cleanup()\n process.stdin.pause()\n resolve()\n }\n\n const onAbort = (): void => {\n if (settled) return\n settled = true\n cleanup()\n process.stdin.pause()\n reject(normalizePauseAbortReason(abortSignal?.reason))\n }\n\n const onClosed = (): void => {\n if (settled) return\n settled = true\n cleanup()\n process.stdin.pause()\n reject(new Error(\"pause input closed before Enter\"))\n }\n\n const onError = (error: unknown): void => {\n if (settled) return\n settled = true\n cleanup()\n process.stdin.pause()\n reject(error instanceof Error ? error : new Error(\"pause input error\"))\n }\n\n function cleanup(): void {\n process.stdin.removeListener(\"data\", onData)\n process.stdin.removeListener(\"end\", onClosed)\n process.stdin.removeListener(\"close\", onClosed)\n process.stdin.removeListener(\"error\", onError)\n abortSignal?.removeEventListener(\"abort\", onAbort)\n }\n\n if (abortSignal?.aborted === true) {\n onAbort()\n return\n }\n\n process.stdin.on(\"end\", onClosed)\n process.stdin.on(\"close\", onClosed)\n process.stdin.on(\"error\", onError)\n process.stdin.on(\"data\", onData)\n // R-0000149: explicitly resume stdin before relying on \"data\" events.\n // After repeated pause/resume cycles, process.stdin may still be paused;\n // the \"data\" listener would otherwise miss the very first keystroke until\n // something else flips the stream into flowing mode.\n process.stdin.resume()\n abortSignal?.addEventListener(\"abort\", onAbort, { once: true })\n })\n}\n\n/**\n * Pause execution and wait for the operator to press Enter.\n * Useful for interactive confirmation during a run.\n *\n * Honors the runner's prompt abort signal: a SIGINT during the pause cancels\n * the wait, removes the stdin `data` listener, and rejects the apply promise\n * with the signal's abort reason.\n *\n * @param message - Prompt shown to the operator. Defaults to `\"Press enter to continue...\"`.\n * @returns A Module that pauses execution.\n */\nexport function pause(message?: string): Module {\n return {\n async apply(): Promise<ModuleResult> {\n const promptText = message ?? \"Press enter to continue...\"\n process.stdout.write(` [pause] ${promptText} `)\n\n await waitForEnterOrAbort(getRunnerAbortSignal())\n\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: message == null ? \"pause\" : `pause: ${message}`,\n }\n}\n\nfunction isFirstRunEnabled(environment: Environment): boolean {\n return environment.PARATIX_FIRST_RUN === \"true\" || environment.FIRST_RUN === true\n}\n\n/**\n * Built-ins related to the explicit first-run bootstrap stage.\n */\nexport const firstRun = {\n /**\n * Stop the current run successfully when Paratix was invoked with `--first-run`.\n * Useful as an explicit stage boundary in scaffolded playbooks.\n *\n * @param message - Optional note shown in the module name.\n * @returns A local module that stops the run only during first-run execution.\n */\n stop(message?: string): Module {\n const moduleName = message == null ? \"firstRun.stop\" : `firstRun.stop: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (!isFirstRunEnabled(environment)) {\n return { status: \"ok\" }\n }\n\n return {\n _dryRunDetail: \"(first-run stop)\",\n _stopRun: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return isFirstRunEnabled(environment) ? NEEDS_APPLY : \"ok\"\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\n/**\n * Built-ins for explicit signal checkpoints.\n */\nexport const signals = {\n /**\n * Flush all currently pending signals for the active scope.\n * Signals remain scope-local:\n * - in a recipe, this flushes that recipe's signals\n * - at top level, this flushes `server(...).signals`\n *\n * @param message - Optional note shown in the module name.\n * @returns A local control module that requests an immediate signal flush.\n */\n flush(message?: string): Module {\n const moduleName = message == null ? \"signals.flush\" : `signals.flush: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return {\n _dryRunDetail: \"(dry-run, pending signals not executed)\",\n _flushSignals: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\n/**\n * Run one or more modules only when a runtime condition is met.\n * When the condition is `false`, the whole group is skipped without running\n * any child checks or applies.\n *\n * @param condition - A predicate evaluated against the current env at run time.\n * @param modules - One or more modules to run when the condition is `true`.\n * @returns A Module that conditionally runs the inner modules.\n *\n * @example\n * when(env => env[\"DEPLOY_ENV\"] === \"production\", service.enabled(\"fail2ban\"))\n */\nfunction baseWhen(condition: (environment: Environment) => boolean, ...modules: Module[]): Module {\n return createConditionalModule({\n condition: (_ssh, environment) => condition(environment),\n modules,\n name: `when: conditional (${modules.length} modules)`,\n })\n}\n\ntype WhenFunction = {\n commandExists: (commandName: string, ...modules: Module[]) => Module\n commandMissing: (commandName: string, ...modules: Module[]) => Module\n fileExists: (path: string, ...modules: Module[]) => Module\n fileMissing: (path: string, ...modules: Module[]) => Module\n packageAbsent: (packageName: string, ...modules: Module[]) => Module\n packageInstalled: (packageName: string, ...modules: Module[]) => Module\n pathExists: (path: string, ...modules: Module[]) => Module\n pathMissing: (path: string, ...modules: Module[]) => Module\n socketExists: (path: string, ...modules: Module[]) => Module\n socketMissing: (path: string, ...modules: Module[]) => Module\n symlinkExists: (path: string, ...modules: Module[]) => Module\n symlinkMissing: (path: string, ...modules: Module[]) => Module\n} & typeof baseWhen\n\nexport const when: WhenFunction = Object.assign(baseWhen, {\n commandExists: (commandName: string, ...modules: Module[]) =>\n createCommandGuard(commandName, false, modules),\n commandMissing: (commandName: string, ...modules: Module[]) =>\n createCommandGuard(commandName, true, modules),\n fileExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-f\" }),\n fileMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-f\" }),\n packageAbsent: (packageName: string, ...modules: Module[]) =>\n createPackageGuard(packageName, true, modules),\n packageInstalled: (packageName: string, ...modules: Module[]) =>\n createPackageGuard(packageName, false, modules),\n pathExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-d\" }),\n pathMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-d\" }),\n socketExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-S\" }),\n socketMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-S\" }),\n symlinkExists: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: false, modules, path, testFlag: \"-L\" }),\n symlinkMissing: (path: string, ...modules: Module[]) =>\n createFilesystemGuard({ invert: true, modules, path, testFlag: \"-L\" }),\n})\n","import { AsyncLocalStorage } from \"node:async_hooks\"\n\n/**\n * R-0000695: the first-run flag propagates through an\n * {@link AsyncLocalStorage} context instead of mutating `process.env`.\n *\n * The flag tells a freshly bootstrapped playbook whether the CLI was\n * invoked with `--first-run`. Two real-world hazards motivated lifting it\n * out of `process.env`:\n *\n * - Every other code path running in the same Node process — vitest\n * worker-pool fixtures, embedded runners, unrelated tooling — observed\n * the synthetic value too. Tests had to manually clean up\n * `process.env.PARATIX_FIRST_RUN` to avoid cross-test contamination.\n * - The mutation/restore dance trusted every caller to wire up\n * try/finally semantics correctly. A missed restore in any code path\n * left the flag stuck on the process for the rest of its lifetime.\n *\n * The AsyncLocalStorage-based variant solves both problems: the value is\n * scoped to the async chain that owns the wrapped body and is automatically\n * released when that chain finishes, regardless of how it resolves.\n * Playbooks now read the flag via the {@link isFirstRun} helper (exported\n * as public API) which queries the same async-local store.\n *\n * R-0000729: the helper lives in its own module so the package entry\n * `dist/index.js` can re-export `isFirstRun` without dragging `cli.ts` into\n * the library bundle. `cli.ts` uses `import.meta.url` for its direct-run\n * detection, which interferes with esbuild chunk splitting when the\n * library entries pull the CLI module transitively.\n */\nconst firstRunContext = new AsyncLocalStorage<boolean>()\n\n/**\n * Public API helper that returns the current first-run flag.\n *\n * Returns `true` only when called from inside a\n * `withCliProcessEnvironment` body whose `firstRun` option was `true`.\n * Outside of a CLI invocation, or when the flag was not set, the helper\n * returns `false`. The helper is async-context aware: a playbook that\n * schedules its own microtasks/timers within the CLI body keeps observing\n * the same flag, while concurrent work outside that body sees `false`.\n *\n * @returns `true` when the current async context is a first-run CLI body.\n */\nexport function isFirstRun(): boolean {\n return firstRunContext.getStore() === true\n}\n\n/**\n * Runs `body` while the CLI-derived first-run flag is observable through\n * {@link isFirstRun}. The flag is cleared automatically when the wrapped\n * body settles, regardless of whether `body` resolves or rejects.\n *\n * @param body - Async work to run while the flag is installed. Its\n * resolved value is forwarded; rejections propagate normally.\n * @returns The value resolved by `body`.\n */\nexport async function runWithFirstRunFlag<T>(body: () => Promise<T>): Promise<T> {\n return firstRunContext.run(true, body)\n}\n\n/**\n * R-0000796: open a dedicated clear-scope where {@link isFirstRun} observes\n * `false` for the duration of `body`. Required when a nested CLI invocation\n * sets `firstRun: false` while running inside an outer scope that had set\n * it to `true`: without this helper the nested body would inherit the\n * outer-context flag and silently observe `true` even though the operator\n * explicitly disabled it. Mirrors the success-path of {@link runWithFirstRunFlag}\n * but installs `false` instead of `true`.\n *\n * @param body - Async work to run while the flag is forced to `false`.\n * @returns The value resolved by `body`.\n */\nexport async function runWithoutFirstRunFlag<T>(body: () => Promise<T>): Promise<T> {\n return firstRunContext.run(false, body)\n}\n","import type { Module } from \"./types.js\"\n\n/**\n * Decide whether the dry-run runner should execute a module's `_applyDryRun`\n * hook in addition to its `check()`.\n *\n * The decision is shared between the top-level runner loop\n * (`runner.runRegularModule`) and the recipe-scoped runner\n * (`dryRunRecipe.executeDryRunChildModule`). Centralizing it here keeps the\n * two paths in lockstep: a future marker tweak only needs one edit.\n *\n * Semantics:\n * - Modules marked as `_dryRunBlocker` (e.g. the first-run-stop module) or\n * `_dryRunMetaProducer` (modules that emit downstream-visible meta even\n * in dry-run mode) always run their `_applyDryRun` / `apply` so the\n * blocker fires and meta keeps propagating.\n * - `_dryRunDiffProducer` modules only run their `_applyDryRun` when the\n * user passed `--diff` (`diffEnabled === true`). Without `--diff`, the\n * dry-run keeps its pre-existing behaviour and pays no extra remote\n * round-trip.\n * - Modules that ship a custom `_applyDryRun` without any of the markers\n * above keep the legacy behaviour: their hook always runs (e.g. the\n * sshd module's config validation has to run regardless of `--diff`).\n *\n * @param module - The candidate module.\n * @param diffEnabled - Whether the user passed `--diff` on the CLI.\n * @returns `true` when the runner should dispatch `_applyDryRun` (or\n * `apply` as the fallback path) instead of treating the module as a\n * passive `changed (dry-run)` result.\n */\nexport function shouldExecuteApplyDuringDryRun(module: Module, diffEnabled: boolean): boolean {\n if (module._dryRunBlocker === true || module._dryRunMetaProducer === true) return true\n if (diffEnabled && module._dryRunDiffProducer === true && module._applyDryRun != null) return true\n return module._applyDryRun != null && module._dryRunDiffProducer !== true\n}\n","/* eslint-disable max-lines -- CLI output rendering is intentionally kept together */\nimport pc from \"picocolors\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\nimport { inspectRedactedDiagnosticValue } from \"./errorRedaction.js\"\nimport { fitAnimatedModuleLine, formatDisplayModule } from \"./outputFormatting.js\"\nimport { maskRegisteredSecrets } from \"./secretSink.js\"\nimport { CommandError } from \"./sshHelpers.js\"\nimport { sanitizeTerminalText } from \"./terminalSanitizer.js\"\n\n// R-0000580: bounds for `util.inspect` when rendering non-Error cause values so\n// a runaway plain object cannot dump unbounded text into stderr.\nconst CAUSE_INSPECT_DEPTH = 2\nconst CAUSE_INSPECT_MAX_STRING_LENGTH = 1024\nconst CAUSE_REDACT_BINARY_MAX_DEPTH = CAUSE_INSPECT_DEPTH + 1\n\nconst MODULE_NAME_WIDTH = 56\nconst MIN_MODULE_NAME_WIDTH = 12\nconst OUTPUT_INDENT_UNIT = \" \"\nconst SPINNER_FRAME_INTERVAL_MS = 80\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst STATUS_ICONS: Record<DisplayStatus, string> = {\n changed: pc.yellow(\"\\u21ba\"),\n failed: pc.red(\"\\u2717\"),\n ok: pc.green(\"\\u2713\"),\n skipped: pc.dim(\"\\u2298\"),\n waiting: pc.cyan(\"\\u23f8\"),\n}\n\nconst CLI_HEADER_LINES = [\n \" _ _ \",\n \" | | (_) \",\n \" _ __ __ _ _ __ __ _| |_ ___ __\",\n \" | '_ \\\\ / _` | '__/ _` | __| \\\\ \\\\/ /\",\n \" | |_) | (_| | | | (_| | |_| |> < \",\n \" | .__/ \\\\__,_|_| \\\\__,_|\\\\__|_/_/\\\\_\\\\\",\n \" | | \",\n \" |_| \",\n]\n\ntype ActiveSpinner = {\n detail?: string\n frameIndex: number\n interval: NodeJS.Timeout\n}\n\nlet activeSpinner: ActiveSpinner | null = null\nlet activeRecipeGuideDepths: number[] = []\nlet pendingRecipeClosureGuideDepths: number[] = []\nlet recipeOutputDepth = -1\n\nexport function renderCliHeader(version: string): string {\n const versionText = pc.dim(`v${version}`)\n return `${pc.cyan(CLI_HEADER_LINES.join(\"\\n\"))}${versionText}\\n`\n}\n\nexport function printCliHeader(version: string): void {\n console.log(renderCliHeader(version))\n}\n\nfunction supportsAnimatedModuleOutput(): boolean {\n return (\n process.stdout.isTTY &&\n typeof process.stdout.clearLine === \"function\" &&\n typeof process.stdout.cursorTo === \"function\"\n )\n}\n\nfunction getModuleIcon(status: DisplayStatus, waitingFrame?: string): string {\n return status === \"waiting\" ? pc.cyan(waitingFrame ?? \"|\") : STATUS_ICONS[status]\n}\n\nfunction getModuleStatusText(status: DisplayStatus): string {\n switch (status) {\n case \"changed\": {\n return pc.yellow(status)\n }\n case \"failed\": {\n return pc.red(status)\n }\n case \"ok\": {\n return pc.green(status)\n }\n case \"skipped\": {\n return pc.dim(status)\n }\n case \"waiting\": {\n return pc.cyan(\"running\")\n }\n }\n}\n\nfunction getCurrentOutputDepth(): number {\n return Math.max(recipeOutputDepth, 0)\n}\n\nfunction getGuideDot(depth: number): string {\n return depth % 2 === 0 ? pc.gray(\"·\") : pc.cyan(\"·\")\n}\n\nfunction buildGuideIndent(\n baseIndent: string,\n options?: {\n activeGuideDepths?: number[]\n colorize?: boolean\n extraGuideDepths?: number[]\n }\n): string {\n const indentCharacters = Array.from(baseIndent)\n const guideDepths = [\n ...(options?.activeGuideDepths ?? activeRecipeGuideDepths),\n ...(options?.extraGuideDepths ?? []),\n ]\n\n for (const guideDepth of guideDepths) {\n const guideCharacterIndex = OUTPUT_INDENT_UNIT.length * (guideDepth + 1)\n if (guideCharacterIndex >= indentCharacters.length) continue\n indentCharacters[guideCharacterIndex] =\n options?.colorize === false ? \"·\" : getGuideDot(guideDepth)\n }\n\n return indentCharacters.join(\"\")\n}\n\nfunction clearPendingRecipeClosureGuides(): void {\n pendingRecipeClosureGuideDepths = []\n}\n\nfunction getModuleIndent(): string {\n if (recipeOutputDepth < 0) {\n return OUTPUT_INDENT_UNIT\n }\n\n return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2)\n}\n\nfunction getRecipeHeaderIndent(): string {\n if (recipeOutputDepth < 0) return \"\"\n return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1)\n}\n\nfunction getErrorIndent(): string {\n return `${getModuleIndent()}│ `\n}\n\nfunction getContinuationIndent(): string {\n return `${getModuleIndent()} `\n}\n\nexport async function withRecipeOutputScope<T>(\n scopedOperation: () => Promise<T> | T\n): Promise<T> {\n recipeOutputDepth += 1\n try {\n return await scopedOperation()\n } finally {\n activeRecipeGuideDepths = activeRecipeGuideDepths.filter((depth) => depth !== recipeOutputDepth)\n pendingRecipeClosureGuideDepths = [recipeOutputDepth]\n recipeOutputDepth -= 1\n }\n}\n\nfunction renderModuleLine(parameters: {\n detail?: string\n extraGuideDepths?: number[]\n name: string\n status: DisplayStatus\n waitingFrame?: string\n}): string {\n const { detail, extraGuideDepths = [], name, status, waitingFrame } = parameters\n const baseIndent = getModuleIndent()\n const indent = buildGuideIndent(baseIndent, { extraGuideDepths })\n const icon = getModuleIcon(status, waitingFrame)\n const statusText = getModuleStatusText(status)\n const detailSuffix = detail == null ? \"\" : ` ${pc.dim(detail)}`\n const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - baseIndent.length, MIN_MODULE_NAME_WIDTH)\n return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`\n}\n\nfunction writeAnimatedModuleLine(line: string): void {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n process.stdout.write(fitAnimatedModuleLine(line, process.stdout.columns))\n}\n\nfunction stopAnimatedModuleLine(clearCurrentLine = false): void {\n if (activeSpinner == null) return\n\n clearInterval(activeSpinner.interval)\n activeSpinner = null\n\n if (clearCurrentLine && supportsAnimatedModuleOutput()) {\n process.stdout.clearLine(0)\n process.stdout.cursorTo(0)\n }\n}\n\nexport function stopLiveModuleOutput(clearCurrentLine = false): void {\n stopAnimatedModuleLine(clearCurrentLine)\n}\n\nexport function startModuleSpinner(name: string, detail?: string): void {\n if (!supportsAnimatedModuleOutput()) return\n\n clearPendingRecipeClosureGuides()\n stopAnimatedModuleLine()\n const maskedName = maskRegisteredSecrets(name)\n const maskedDetail = detail == null ? undefined : maskRegisteredSecrets(detail)\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail: maskedDetail,\n name: maskedName,\n status: \"waiting\",\n terminalColumns: process.stdout.columns,\n })\n\n const spinner: ActiveSpinner = {\n detail: displayModule.detail,\n frameIndex: 0,\n interval: setInterval(() => {\n spinner.frameIndex = (spinner.frameIndex + 1) % SPINNER_FRAMES.length\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: spinner.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[spinner.frameIndex],\n })\n )\n }, SPINNER_FRAME_INTERVAL_MS),\n }\n // Avoid keeping the event loop alive solely for the spinner timer:\n // if stopAnimatedModuleLine/stopLiveModuleOutput is missed in an\n // unhappy-path (uncaughtException), the process should still be able\n // to exit. Mirrors the pattern used by sleepRespectingShutdown in runner.ts.\n if (typeof spinner.interval.unref === \"function\") spinner.interval.unref()\n\n activeSpinner = spinner\n writeAnimatedModuleLine(\n renderModuleLine({\n detail: displayModule.detail,\n name: displayModule.name,\n status: \"waiting\",\n waitingFrame: SPINNER_FRAMES[0],\n })\n )\n}\n\nexport function resetLiveOutputForTests(): void {\n stopAnimatedModuleLine()\n activeRecipeGuideDepths = []\n clearPendingRecipeClosureGuides()\n recipeOutputDepth = -1\n}\n\n/**\n * Print a bold, colored header line marking the start of a recipe run.\n * @param name - The recipe or server name to display.\n */\nexport function printRecipeHeader(name: string): void {\n stopAnimatedModuleLine(true)\n clearPendingRecipeClosureGuides()\n const header = pc.bold(pc.blue(`[${name}]`))\n console.log(`${buildGuideIndent(getRecipeHeaderIndent())}${header}`)\n if (recipeOutputDepth >= 0) {\n activeRecipeGuideDepths = [...activeRecipeGuideDepths, recipeOutputDepth]\n }\n}\n\nexport function printRunContext(parameters: {\n dryRun: boolean\n host: string\n name: string\n ports: number[]\n}): void {\n const mode = parameters.dryRun ? pc.yellow(\"dry-run\") : pc.green(\"apply\")\n const ports = parameters.ports.join(\", \")\n console.log(\n pc.dim(`Run ${parameters.name} · host ${parameters.host} · ports ${ports} · mode ${mode}`)\n )\n}\n\nfunction colorizeDiffLine(line: string): string {\n if (line.startsWith(\"---\") || line.startsWith(\"+++\") || line.startsWith(\"@@\")) {\n return pc.dim(line)\n }\n if (line.startsWith(\"-\")) return pc.red(line)\n if (line.startsWith(\"+\")) return pc.green(line)\n return pc.dim(line)\n}\n\nfunction renderDiffLines(diff: string): string[] {\n if (diff.trim() === \"\") return []\n const sanitized = sanitizeTerminalText(maskRegisteredSecrets(diff))\n return sanitized.split(\"\\n\").map((line) => `│ ${colorizeDiffLine(line)}`)\n}\n\nfunction writeContinuationLine(\n line: string,\n extraGuideDepths: number[],\n via: \"console\" | \"stdout\"\n): void {\n const composed = `${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${line}`\n if (via === \"stdout\") {\n process.stdout.write(`${composed}\\n`)\n } else {\n console.log(composed)\n }\n}\n\nfunction writeContinuationBlock(parameters: {\n detailLines: string[]\n diffLines: string[]\n extraGuideDepths: number[]\n via: \"console\" | \"stdout\"\n}): void {\n for (const detailLine of parameters.detailLines) {\n writeContinuationLine(pc.dim(detailLine), parameters.extraGuideDepths, parameters.via)\n }\n for (const diffLine of parameters.diffLines) {\n writeContinuationLine(diffLine, parameters.extraGuideDepths, parameters.via)\n }\n}\n\nfunction printRenderedModuleResult(parameters: {\n detail?: string\n diff?: string\n extraGuideDepths?: number[]\n name: string\n status: DisplayStatus\n}): void {\n const extraGuideDepths = parameters.extraGuideDepths ?? []\n const maskedName = maskRegisteredSecrets(parameters.name)\n const maskedDetail =\n parameters.detail == null ? undefined : maskRegisteredSecrets(parameters.detail)\n const displayModule = formatDisplayModule({\n continuationIndentWidth: `${buildGuideIndent(\n OUTPUT_INDENT_UNIT.repeat(Math.max(getCurrentOutputDepth() + 2, 1)),\n { extraGuideDepths }\n )} `.length,\n detail: maskedDetail,\n name: maskedName,\n status: parameters.status,\n terminalColumns: process.stdout.columns,\n })\n const line = renderModuleLine({\n detail: displayModule.detail,\n extraGuideDepths,\n name: displayModule.name,\n status: parameters.status,\n })\n const diffLines = parameters.diff == null ? [] : renderDiffLines(parameters.diff)\n const usesSpinner = supportsAnimatedModuleOutput() && activeSpinner != null\n if (usesSpinner) {\n stopAnimatedModuleLine()\n writeAnimatedModuleLine(line)\n process.stdout.write(\"\\n\")\n } else {\n console.log(line)\n }\n writeContinuationBlock({\n detailLines: displayModule.detailLines,\n diffLines,\n extraGuideDepths,\n via: usesSpinner ? \"stdout\" : \"console\",\n })\n}\n\n/**\n * Print a single module result row with a status icon, name, and colored status label.\n *\n * @param name - The module name shown in the left column.\n * @param status - One of the known status strings (`ok`, `changed`, `skipped`, `failed`).\n * @param detail - Optional short detail appended in dim text after the status.\n * @param diff - Optional unified-diff text rendered as a guarded multi-line block\n * below the status line. The block is rendered only when the runner forwards\n * a non-empty diff (i.e. the user passed `--diff` and the module produced one).\n * Every diff line is masked through `maskRegisteredSecrets` and\n * `sanitizeTerminalText` before printing.\n */\n// eslint-disable-next-line max-params -- positional API kept for source compatibility with downstream callers; diff is a leaf-level optional follow-up to detail\nexport function printModuleResult(\n name: string,\n status: DisplayStatus,\n detail?: string,\n diff?: string\n): void {\n clearPendingRecipeClosureGuides()\n printRenderedModuleResult({ detail, diff, name, status })\n}\n\n// eslint-disable-next-line max-params -- mirrors printModuleResult; positional API kept for source compatibility\nexport function printRecipeModuleResult(\n name: string,\n status: DisplayStatus,\n detail?: string,\n diff?: string\n): void {\n printRenderedModuleResult({\n detail,\n diff,\n extraGuideDepths: pendingRecipeClosureGuideDepths,\n name,\n status,\n })\n clearPendingRecipeClosureGuides()\n}\n\n/**\n * Print captured stderr and stdout from a failed command in a red bordered block.\n * Outputs nothing if both streams are empty.\n *\n * @param stdout - Captured standard output of the failed command.\n * @param stderr - Captured standard error of the failed command.\n */\nexport function printCommandError(stdout: string, stderr: string): void {\n // R-0000789: defense-in-depth — apply the process-wide secret sink to the\n // captured stdout/stderr immediately before they reach stderr. Every\n // documented caller already masks via `maskRegisteredSecrets`, but a\n // future caller that forgets the wrapper would otherwise leak verbatim\n // through this terminal write. The double-masking is idempotent.\n const maskedStdout = sanitizeTerminalText(maskRegisteredSecrets(stdout))\n const maskedStderr = sanitizeTerminalText(maskRegisteredSecrets(stderr))\n const lines: string[] = []\n if (maskedStderr.trim()) {\n lines.push(...maskedStderr.trim().split(\"\\n\"))\n }\n if (maskedStdout.trim()) {\n lines.push(...maskedStdout.trim().split(\"\\n\"))\n }\n if (lines.length > 0) {\n console.error(pc.red(`${getErrorIndent()}Error output:`))\n for (const line of lines) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\n/**\n * Print the full (untruncated) stdout and stderr of a failed command.\n * Used in verbose mode to show the complete output that was truncated in the error message.\n *\n * @param stdout - Full standard output of the failed command.\n * @param stderr - Full standard error of the failed command.\n */\nexport function printVerboseCommandError(stdout: string, stderr: string): void {\n // R-0000789: defense-in-depth — apply the process-wide secret sink before\n // the verbose dump reaches stderr so a caller that forgets to pre-mask the\n // capture buffers still has its output redacted. The double-masking is\n // idempotent for callers that already passed pre-masked strings.\n const maskedStdout = sanitizeTerminalText(maskRegisteredSecrets(stdout))\n const maskedStderr = sanitizeTerminalText(maskRegisteredSecrets(stderr))\n if (maskedStderr.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stderr:`))\n for (const line of maskedStderr.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n if (maskedStdout.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stdout:`))\n for (const line of maskedStdout.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n}\n\nfunction printVerboseErrorBlock(label: string, content: string): void {\n if (!content.trim()) {\n return\n }\n\n console.error(pc.red(`${getErrorIndent()}${label}`))\n for (const line of content.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n}\n\nfunction getErrorCause(error: Error): unknown {\n return (error as { cause?: unknown } & Error).cause\n}\n\nfunction printVerboseErrorCause(\n cause: unknown,\n depth: number,\n visitedCauses: WeakSet<Error>\n): void {\n const label = `Cause ${depth}:`\n if (cause instanceof Error) {\n if (visitedCauses.has(cause)) {\n printVerboseErrorBlock(label, \"<cycle detected>\")\n return\n }\n visitedCauses.add(cause)\n const stack = cause.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(cause)\n printVerboseErrorBlock(label, maskRegisteredSecrets(stackOrMessage))\n const nestedCause = getErrorCause(cause)\n if (nestedCause !== undefined) {\n printVerboseErrorCause(nestedCause, depth + 1, visitedCauses)\n }\n return\n }\n\n printVerboseErrorBlock(label, maskRegisteredSecrets(formatCauseValue(cause)))\n}\n\nfunction printVerboseGenericError(error: Error): void {\n const stack = error.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(error)\n // R-0000041: redact any registered secrets that may have flowed into the\n // stack trace through Error wrapping or third-party libraries before it\n // reaches stderr.\n printVerboseErrorBlock(\"Full stack:\", maskRegisteredSecrets(stackOrMessage))\n const cause = getErrorCause(error)\n if (cause !== undefined) {\n // Track every Error seen while walking the cause chain to detect cycles\n // produced by third-party error wrapping (e.g. err.cause === err) so the\n // recursion always terminates.\n const visitedCauses = new WeakSet<Error>()\n visitedCauses.add(error)\n printVerboseErrorCause(cause, 1, visitedCauses)\n }\n}\n\n/**\n * Format the textual representation of a single cause-chain link. `Error`\n * values surface their message; other values are rendered through\n * a bounded inspector so primitives and plain objects still carry diagnostic\n * context without dumping unbounded text.\n */\nfunction formatCauseValue(cause: unknown): string {\n if (cause instanceof Error) return cause.message\n // R-0000580: replace `String(cause)` with `util.inspect` so plain objects\n // produce useful output (\"[object Object]\" → `{ key: \"…\" }`) and the result\n // is capped via depth/string-length bounds.\n return inspectRedactedDiagnosticValue(cause, {\n depth: CAUSE_INSPECT_DEPTH,\n maxStringLength: CAUSE_INSPECT_MAX_STRING_LENGTH,\n redactMaxDepth: CAUSE_REDACT_BINARY_MAX_DEPTH,\n })\n}\n\n/**\n * Walk the `Error.cause` chain of `error` and emit one `Cause: …` block per\n * level on stderr. Used by {@link printCommandFailure} so generic (non-\n * {@link CommandError}) failures surface their wrapped root cause even in\n * non-verbose mode — without this, only `Error.message` would reach stderr\n * and the actual reason (a wrapped `ECONNREFUSED`, a parse failure, …)\n * would stay hidden until the operator re-ran with `--verbose`.\n *\n * The walker keeps a {@link WeakSet} of already-visited `Error` references\n * so a self-referencing or cyclic `cause` chain — which a misbehaving\n * library can construct — cannot loop forever.\n *\n * @param error - The root `Error` whose `.cause` chain should be printed.\n */\nfunction printCauseChain(error: Error): void {\n const visited = new WeakSet<Error>()\n visited.add(error)\n let cause = getErrorCause(error)\n while (cause !== undefined) {\n if (cause instanceof Error) {\n if (visited.has(cause)) return\n visited.add(cause)\n }\n console.error(pc.red(`${getErrorIndent()}Cause: ${maskRegisteredSecrets(formatCauseValue(cause))}`))\n // R-0000580: when a `CommandError` appears as a cause it carries the full\n // stdout/stderr of the failed remote command. Emit those streams the same\n // way `printVerboseCommandError` does so the operator does not lose the\n // command output once it has been wrapped into an outer error. Stdout and\n // stderr go through `maskRegisteredSecrets` like the existing verbose path\n // in `printCommandFailure`.\n if (cause instanceof CommandError) {\n printVerboseCommandError(\n maskRegisteredSecrets(cause.fullStdout),\n maskRegisteredSecrets(cause.fullStderr)\n )\n }\n cause = cause instanceof Error ? getErrorCause(cause) : undefined\n }\n}\n\n/**\n * Print the error message of a failed command and, when verbose mode is active\n * and the error is a {@link CommandError}, the full untruncated output.\n *\n * R-0000041: every string written to stderr is passed through\n * {@link maskRegisteredSecrets} so resolved op values, sudo passwords, user\n * password hashes, and download URL tokens never leak through generic\n * `Error.message` or stack-trace paths. Modules register their secrets via\n * `secretSink.registerSecret` (or `withRegisteredSecrets`) for the duration\n * of the work that produces them.\n *\n * @param error - The caught error value.\n * @param verbose - Whether to show full stdout/stderr.\n */\nexport function printCommandFailure(error: unknown, verbose: boolean): void {\n if (verbose && error instanceof CommandError) {\n // Print only the exit-code line, skip the truncated output and hint\n const summaryLine = error.message.split(\"\\n\")[0]\n printCommandError(\"\", maskRegisteredSecrets(summaryLine))\n printVerboseCommandError(\n maskRegisteredSecrets(error.fullStdout),\n maskRegisteredSecrets(error.fullStderr)\n )\n return\n }\n\n printCommandError(\"\", maskRegisteredSecrets(String(error)))\n if (error instanceof Error) {\n if (!(error instanceof CommandError)) {\n // R-0000520: for generic Errors, surface the Error.cause chain even\n // without --verbose so the wrapped root cause (ECONNREFUSED behind a\n // wrapper Error, a parser failure behind a domain Error, …) reaches\n // stderr instead of being hidden behind the top-level message. Cycle-\n // safe via WeakSet. CommandError has its own structured diagnostic\n // surface (full stdout/stderr) and is handled in verbose mode above.\n printCauseChain(error)\n }\n if (verbose) {\n printVerboseGenericError(error)\n }\n }\n}\n\n/**\n * Print a run summary line with counts for each status category.\n *\n * @param stats - Aggregated counts from the completed run.\n * @param stats.changed - Number of modules that changed state.\n * @param stats.ok - Number of modules already in desired state.\n * @param stats.skipped - Number of modules skipped.\n * @param stats.failed - Number of modules that failed.\n * @param stats.signals - Number of signals triggered.\n */\nexport function printSummary(stats: {\n changed: number\n failed: number\n ok: number\n signals: number\n skipped: number\n}): void {\n stopAnimatedModuleLine(true)\n const parts = [\n pc.yellow(`${stats.changed} changed`),\n pc.green(`${stats.ok} ok`),\n pc.dim(`${stats.skipped} skipped`),\n stats.failed > 0 ? pc.red(`${stats.failed} failed`) : `${stats.failed} failed`,\n pc.cyan(`${stats.signals} signals triggered`),\n ]\n console.log(`\\n${parts.join(pc.dim(\" \\u00b7 \"))}`)\n}\n","import { stripVTControlCharacters } from \"node:util\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\ntype DisplayStatus = \"waiting\" | ModuleStatus\n\nconst PACKAGE_MODULE_SUFFIX_LENGTH = 2\nconst PACKAGE_COLUMN_GAP_WIDTH = 2\nconst DEFAULT_TERMINAL_COLUMNS = 100\nconst MIN_PACKAGE_COLUMNS_WIDTH = 24\nconst MIN_PACKAGE_COLUMN_WIDTH = 18\nconst MIN_ANIMATED_LINE_COLUMNS = 8\n\nexport type DisplayModule = {\n detail?: string\n detailLines: string[]\n name: string\n}\n\nfunction splitPackageModuleName(name: string): { packages: string[]; summaryName: string } | null {\n for (const prefix of [\"package.installed: \", \"package.absent: \"]) {\n if (!name.startsWith(prefix)) continue\n\n const packages = name\n .slice(prefix.length)\n .split(\",\")\n .map((entry) => entry.trim())\n .filter(Boolean)\n if (packages.length === 0) return null\n\n return {\n packages,\n summaryName: prefix.slice(0, -PACKAGE_MODULE_SUFFIX_LENGTH),\n }\n }\n\n return null\n}\n\nfunction getPackageColumns(parameters: {\n continuationIndentWidth: number\n packages: string[]\n terminalColumns?: number\n}): string[] {\n if (parameters.packages.length <= 1) return parameters.packages\n\n const availableWidth = Math.max(\n (parameters.terminalColumns ?? DEFAULT_TERMINAL_COLUMNS) - parameters.continuationIndentWidth,\n MIN_PACKAGE_COLUMNS_WIDTH\n )\n const widestPackage = Math.max(...parameters.packages.map((entry) => entry.length))\n const columnWidth = Math.max(widestPackage + PACKAGE_COLUMN_GAP_WIDTH, MIN_PACKAGE_COLUMN_WIDTH)\n const columnCount = Math.max(Math.floor(availableWidth / columnWidth), 1)\n const rowCount = Math.ceil(parameters.packages.length / columnCount)\n\n return Array.from({ length: rowCount }, (_row, rowIndex) =>\n Array.from({ length: columnCount }, (_column, columnIndex) => {\n const packageIndex = rowIndex + columnIndex * rowCount\n const packageName = parameters.packages.at(packageIndex)\n if (packageName == null) return \"\"\n const isLastVisibleColumn =\n columnIndex === columnCount - 1 || packageIndex + rowCount >= parameters.packages.length\n return isLastVisibleColumn ? packageName : packageName.padEnd(columnWidth)\n })\n .filter(Boolean)\n .join(\"\")\n )\n}\n\nfunction getPackageSummaryDetail(packageCount: number, detail?: string): string {\n const packageLabel = packageCount === 1 ? \"1 package\" : `${packageCount} packages`\n return detail == null ? packageLabel : `${packageLabel} · ${detail}`\n}\n\nexport function fitAnimatedModuleLine(line: string, columns?: number): string {\n if (columns === undefined || columns < MIN_ANIMATED_LINE_COLUMNS) return line\n\n const plainLine = stripVTControlCharacters(line)\n if (plainLine.length < columns) return line\n\n return `${plainLine.slice(0, columns - 1)}\\u2026`\n}\n\nexport function formatDisplayModule(parameters: {\n continuationIndentWidth: number\n detail?: string\n name: string\n status: DisplayStatus\n terminalColumns?: number\n}): DisplayModule {\n const packageModule = splitPackageModuleName(parameters.name)\n if (packageModule == null) {\n return {\n detail: parameters.detail,\n detailLines: [],\n name: parameters.name,\n }\n }\n\n return {\n detail: getPackageSummaryDetail(packageModule.packages.length, parameters.detail),\n detailLines:\n parameters.status === \"waiting\"\n ? []\n : getPackageColumns({\n continuationIndentWidth: parameters.continuationIndentWidth,\n packages: packageModule.packages,\n terminalColumns: parameters.terminalColumns,\n }),\n name: packageModule.summaryName,\n }\n}\n","import type { RecipeModule } from \"./recipe.js\"\nimport type { Environment, ModuleMetaEntry, ModuleStatus, SshConnection } from \"./types.js\"\n\nimport { shouldExecuteApplyDuringDryRun } from \"./dryRunDispatch.js\"\nimport { mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\n\ntype StepResult = {\n env: Environment\n meta?: ModuleMetaEntry[]\n shouldBreak: boolean\n status?: ModuleStatus\n stopRun?: true\n}\n\nfunction interruptedDryRunResult(parameters: {\n aggregatedMeta: ModuleMetaEntry[]\n aggregatedStatus: \"changed\" | \"ok\"\n currentEnvironment: Environment\n}): StepResult {\n return {\n env: parameters.currentEnvironment,\n meta: parameters.aggregatedMeta.length === 0 ? undefined : parameters.aggregatedMeta,\n shouldBreak: true,\n status: parameters.aggregatedStatus === \"changed\" ? \"changed\" : undefined,\n }\n}\n\nasync function executeDryRunBlockingModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n connection: null | SshConnection\n diff: boolean\n environment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, connection, diff, environment, verbose } = parameters\n if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true }\n startModuleSpinner(childModule.name)\n const result =\n childModule._applyDryRun == null\n ? await childModule.apply(connection, environment)\n : await childModule._applyDryRun(connection, environment, {\n shutdownSignal: parameters.shutdownSignal,\n })\n const nextEnvironment =\n result.meta == null ? environment : await mergeEnvironmentFromMeta(environment, result.meta)\n const diffOutput = diff ? result.diff : undefined\n printModuleResult(\n childModule.name,\n result.status,\n result._dryRunDetail ?? \"(dry-run)\",\n diffOutput\n )\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n return {\n env: nextEnvironment,\n meta: result.meta,\n shouldBreak: result.status === \"failed\" || result._stopRun === true,\n status: result.status,\n stopRun: result._stopRun,\n }\n}\n\nasync function executeDryRunChildModule(parameters: {\n childModule: RecipeModule[\"_modules\"][number]\n diff: boolean\n environment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n verbose: boolean\n}): Promise<StepResult> {\n const { childModule, diff, environment, ssh, verbose } = parameters\n if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true }\n const connection = childModule.local === true ? null : ssh\n startModuleSpinner(childModule.name)\n const checkResult = await childModule.check(connection, environment)\n if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true }\n if (checkResult !== \"ok\" && shouldExecuteApplyDuringDryRun(childModule, diff)) {\n return executeDryRunBlockingModule({\n childModule,\n connection,\n diff,\n environment,\n shutdownSignal: parameters.shutdownSignal,\n verbose,\n })\n }\n const status = checkResult === \"ok\" ? \"ok\" : \"changed\"\n const suffix = checkResult === \"ok\" ? undefined : \"(dry-run)\"\n printModuleResult(childModule.name, status, suffix)\n return { env: environment, shouldBreak: false, status }\n}\n\ntype DryRunRecipeAccumulator = {\n aggregatedMeta: ModuleMetaEntry[]\n aggregatedStatus: \"changed\" | \"ok\"\n currentEnvironment: Environment\n}\n\nfunction applyDryRunChildResult(\n accumulator: DryRunRecipeAccumulator,\n result: StepResult\n): DryRunRecipeAccumulator {\n return {\n aggregatedMeta:\n result.meta == null\n ? accumulator.aggregatedMeta\n : [...accumulator.aggregatedMeta, ...result.meta],\n aggregatedStatus: result.status === \"changed\" ? \"changed\" : accumulator.aggregatedStatus,\n currentEnvironment: result.env,\n }\n}\n\nasync function runDryRunChildLoop(parameters: {\n accumulator: DryRunRecipeAccumulator\n diff: boolean\n recipeModule: RecipeModule\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n verbose: boolean\n}): Promise<DryRunRecipeAccumulator | StepResult> {\n let accumulator = parameters.accumulator\n for (const childModule of parameters.recipeModule._modules) {\n if (parameters.shutdownSignal() != null) {\n return interruptedDryRunResult({\n aggregatedMeta: accumulator.aggregatedMeta,\n aggregatedStatus: accumulator.aggregatedStatus,\n currentEnvironment: accumulator.currentEnvironment,\n })\n }\n // eslint-disable-next-line no-await-in-loop\n const result = await executeDryRunChildModule({\n childModule,\n diff: parameters.diff,\n environment: accumulator.currentEnvironment,\n shutdownSignal: parameters.shutdownSignal,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n if (result.shouldBreak) return result\n accumulator = applyDryRunChildResult(accumulator, result)\n }\n return accumulator\n}\n\nexport async function dryRunRecipeModule(parameters: {\n environment: Environment\n options?: {\n diff?: boolean\n verbose?: boolean\n }\n recipeModule: RecipeModule\n shutdownSignal?: () => NodeJS.Signals | null\n ssh: null | SshConnection\n}): Promise<StepResult> {\n return withRecipeOutputScope(async () => {\n const { environment, recipeModule, ssh } = parameters\n printRecipeHeader(recipeModule.name)\n const shutdownSignal = parameters.shutdownSignal ?? (() => null)\n const loopResult = await runDryRunChildLoop({\n accumulator: {\n aggregatedMeta: [],\n aggregatedStatus: \"ok\",\n currentEnvironment: environment,\n },\n diff: parameters.options?.diff ?? false,\n recipeModule,\n shutdownSignal,\n ssh,\n verbose: parameters.options?.verbose ?? false,\n })\n if (\"shouldBreak\" in loopResult) return loopResult\n return {\n env: loopResult.currentEnvironment,\n meta: loopResult.aggregatedMeta.length === 0 ? undefined : loopResult.aggregatedMeta,\n shouldBreak: false,\n status: loopResult.aggregatedStatus,\n }\n })\n}\n","import type {\n Environment,\n Module,\n ModuleStatus,\n OrchestrationStep,\n SshConnection,\n} from \"./types.js\"\n\nimport { assertValidModuleMetaEntries, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport { printCommandFailure, printModuleResult, startModuleSpinner } from \"./output.js\"\n\nexport type SignalHooks = {\n onSignalFinished?: (status: ModuleStatus) => void\n onSignalStarted?: () => void\n}\n\nexport type SignalRunStatus = \"changed\" | \"failed\"\n\ntype SignalRunParameters = {\n environment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}\n\nfunction handleSignalResult(parameters: {\n hooks?: SignalHooks\n result: Awaited<ReturnType<Module[\"apply\"]>>\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { hooks, result, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, result.status)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n hooks?.onSignalFinished?.(result.status)\n return result.status === \"failed\" ? \"failed\" : \"changed\"\n}\n\nfunction handleSignalFailure(parameters: {\n error: unknown\n hooks?: SignalHooks\n signalName: string\n verbose: boolean\n}): SignalRunStatus {\n const { error, hooks, signalName, verbose } = parameters\n printModuleResult(`signal: ${signalName}`, \"failed\")\n printCommandFailure(error, verbose)\n hooks?.onSignalFinished?.(\"failed\")\n return \"failed\"\n}\n\nasync function applySignalMeta(parameters: {\n currentEnvironment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n result: Awaited<ReturnType<Module[\"apply\"]>>\n}): Promise<Environment> {\n assertValidModuleMetaEntries(parameters.result.meta)\n const nextEnvironment =\n parameters.result.status === \"failed\"\n ? parameters.currentEnvironment\n : await mergeEnvironmentFromMeta(parameters.currentEnvironment, parameters.result.meta)\n await parameters.onSignalStep?.({\n env: nextEnvironment,\n meta: parameters.result.meta,\n status: parameters.result.status,\n })\n return nextEnvironment\n}\n\nasync function runOneSignal(parameters: {\n currentEnvironment: Environment\n hooks?: SignalHooks\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signal: Module\n ssh: null | SshConnection\n verbose: boolean\n}): Promise<{ nextEnvironment: Environment; status: SignalRunStatus }> {\n const connection = parameters.signal.local === true ? null : parameters.ssh\n startModuleSpinner(`signal: ${parameters.signal.name}`)\n const result =\n parameters.shutdownSignal == null\n ? await parameters.signal.apply(connection, parameters.currentEnvironment)\n : await parameters.signal.apply(connection, parameters.currentEnvironment, {\n shutdownSignal: parameters.shutdownSignal,\n })\n const nextEnvironment = await applySignalMeta({\n currentEnvironment: parameters.currentEnvironment,\n onSignalStep: parameters.onSignalStep,\n result,\n })\n return {\n nextEnvironment,\n status: handleSignalResult({\n hooks: parameters.hooks,\n result,\n signalName: parameters.signal.name,\n verbose: parameters.verbose,\n }),\n }\n}\n\nexport async function runSignalModules(parameters: SignalRunParameters): Promise<SignalRunStatus> {\n const getShutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let currentEnvironment = parameters.environment\n let status: SignalRunStatus = \"changed\"\n\n for (const signal of parameters.signals) {\n if (getShutdownSignal() != null) break\n parameters.hooks?.onSignalStarted?.()\n try {\n // eslint-disable-next-line no-await-in-loop\n const signalStep = await runOneSignal({\n currentEnvironment,\n hooks: parameters.hooks,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signal,\n ssh: parameters.ssh,\n verbose,\n })\n currentEnvironment = signalStep.nextEnvironment\n if (signalStep.status === \"failed\") status = \"failed\"\n } catch (error) {\n status = handleSignalFailure({\n error,\n hooks: parameters.hooks,\n signalName: signal.name,\n verbose,\n })\n }\n }\n\n return status\n}\n","/* eslint-disable max-lines -- recipe orchestration intentionally stays co-located */\nimport { dryRunRecipeModule } from \"./dryRunRecipe.js\"\nimport { createNullPrototypeEnvironment } from \"./environment.js\"\nimport { isEnvironmentMetaEntry, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n printRecipeModuleResult,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\nimport { getRunnerAbortSignal } from \"./runnerAbortSignal.js\"\nimport { runSignalModules, type SignalHooks } from \"./signalOrchestration.js\"\nimport { CommandError } from \"./sshHelpers.js\"\nimport {\n type Environment,\n type Module,\n type ModuleMetaEntry,\n type ModuleResult,\n type ModuleStatus,\n NEEDS_APPLY,\n type OrchestrationStep,\n type ShutdownSignal,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Internal representation of a recipe module.\n * The `_isRecipe` flag lets the runner distinguish recipes from leaf modules.\n * @internal\n */\nexport type RecipeModule = {\n _isRecipe: true\n _modules: Module[]\n _signals?: Module[]\n apply: (\n ssh: null | SshConnection,\n environment: Environment,\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ) => Promise<ModuleResult>\n} & Module\n\ntype RecipeState = {\n env: Environment\n meta?: ModuleMetaEntry[]\n signalsPending: boolean\n status: Exclude<ModuleStatus, \"skipped\">\n stopRun?: true\n}\n\ntype ExecuteModulesParameters = {\n environment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n signals?: Module[]\n verbose?: boolean\n}\n\ntype RecipeLoopStepResult =\n | { kind: \"break\"; state: RecipeState }\n | { kind: \"continue\"; state: RecipeState }\n\nconst INTERRUPTED_BEFORE_APPLY = Symbol(\"recipe-interrupted-before-apply\")\n\nfunction isRecipeModuleLike(module: Module): module is RecipeModule {\n return (module as { _isRecipe?: boolean } & Module)._isRecipe === true\n}\n\nfunction printRecipeChildResult(module: Module, result: ModuleResult): void {\n if (isRecipeModuleLike(module)) {\n printRecipeModuleResult(module.name, result.status, result.detail)\n return\n }\n\n printModuleResult(module.name, result.status, result.detail)\n}\n\nfunction applyRecipeStepToState(\n state: RecipeState,\n step: OrchestrationStep,\n preserveControlPlaneMeta: boolean\n): RecipeState {\n let stepMeta: ModuleMetaEntry[] | undefined\n if (step.meta == null) {\n stepMeta = undefined\n } else if (preserveControlPlaneMeta) {\n stepMeta = step.meta\n } else {\n stepMeta = step.meta.filter(isEnvironmentMetaEntry)\n }\n const nextMeta = stepMeta == null ? (state.meta ?? []) : [...(state.meta ?? []), ...stepMeta]\n let nextStatus = state.status\n if (step.status === \"failed\") nextStatus = \"failed\"\n else if (step.status === \"changed\") nextStatus = \"changed\"\n\n return {\n env: step.env,\n meta: nextMeta,\n signalsPending: step.status === \"changed\" ? true : state.signalsPending,\n status: nextStatus,\n stopRun: step._stopRun === true ? true : state.stopRun,\n }\n}\n\n/**\n * Recipes run their own check→apply loop, separate from the runner's\n * `runModuleLoop` in runner.ts. This is intentional: recipes execute as a\n * single nested module inside the runner, so SSH-lifecycle concerns\n * (port-change reconnects, reboot handling), shutdown-signal guards,\n * dry-run mode and stats tracking are the runner's responsibility and\n * must not be duplicated here.\n *\n * @param parameters - Parameters for executing one child module.\n * @param parameters.targetModule - The module to check and conditionally apply.\n * @param parameters.ssh - Active SSH connection, or `null` for local modules.\n * @param parameters.currentEnvironment - Environment values available to the module.\n * @param parameters.shutdownSignal - Optional shutdown getter used to suppress new apply steps.\n * @param parameters.verbose - Whether verbose command diagnostics should be printed.\n * @param parameters.onChildStep - Forwarded to nested RecipeModule children so the\n * runner observes meta from grand-children. Ignored for leaf modules.\n * @param parameters.onSignalStep - Forwarded to nested RecipeModule children so\n * their signal meta also reaches the runner. Ignored for leaf modules.\n * @param parameters.signalHooks - Forwarded to nested RecipeModule children so\n * stats accounting for signal modules works through the recipe tree.\n * @returns The updated environment and status, or `null` if the module was already ok.\n */\nasync function executeOneModule(parameters: {\n currentEnvironment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n ssh: null | SshConnection\n targetModule: Module\n verbose?: boolean\n}): Promise<null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY> {\n const { currentEnvironment, ssh, targetModule } = parameters\n const verbose = parameters.verbose ?? false\n const connection = targetModule.local === true ? null : ssh\n const checkResult = await checkRecipeChild(targetModule, connection, currentEnvironment)\n\n if (checkResult === \"ok\") {\n printModuleResult(targetModule.name, \"ok\")\n return null\n }\n\n if ((parameters.shutdownSignal?.() ?? null) != null) {\n return INTERRUPTED_BEFORE_APPLY\n }\n\n // R-0000091: when the child is a nested RecipeModule, forward the\n // shutdownSignal (and the other recipe-aware options) so the inner\n // recipe loop observes SIGINT/SIGTERM and the runner still sees meta\n // and signal stats from grand-children. Leaf modules use the plain\n // 2-arg Module.apply contract.\n const result = await applyRecipeChild({\n connection,\n currentEnvironment,\n onChildStep: parameters.onChildStep,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signalHooks: parameters.signalHooks,\n targetModule,\n verbose,\n })\n printRecipeChildResult(targetModule, result)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n\n // R-0000579: mirror `applySignalMeta` (signalOrchestration.ts) and skip the\n // env merge when the child failed, so the failure does not leak partial meta\n // back into the recipe environment.\n const environment =\n result.status === \"failed\"\n ? currentEnvironment\n : await mergeEnvironmentFromMeta(currentEnvironment, result.meta)\n return {\n _flushSignals: result._flushSignals,\n _stopRun: result._stopRun,\n env: environment,\n meta: result.meta,\n status: result.status,\n }\n}\n\nasync function checkRecipeChild(\n targetModule: Module,\n connection: null | SshConnection,\n currentEnvironment: Environment\n): Promise<\"needs-apply\" | \"ok\"> {\n startModuleSpinner(targetModule.name)\n return targetModule.check(connection, currentEnvironment)\n}\n\nasync function applyRecipeChild(parameters: {\n connection: null | SshConnection\n currentEnvironment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n targetModule: Module\n verbose: boolean\n}): Promise<ModuleResult> {\n if (isRecipeModuleLike(parameters.targetModule)) {\n return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment, {\n onChildStep: parameters.onChildStep,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signalHooks: parameters.signalHooks,\n verbose: parameters.verbose,\n })\n }\n\n if (\n parameters.targetModule._supportsChildStepHook === true &&\n (parameters.onChildStep != null || parameters.shutdownSignal != null)\n ) {\n return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment, {\n onChildStep: parameters.onChildStep,\n shutdownSignal: parameters.shutdownSignal,\n })\n }\n\n return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment)\n}\n\nasync function applyExecutedRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n state: RecipeState\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n}): Promise<null | RecipeState> {\n if (parameters.step == null) return parameters.state\n if (parameters.step === INTERRUPTED_BEFORE_APPLY) return null\n\n if (parameters.onChildStep != null) {\n await parameters.onChildStep(parameters.step)\n }\n\n return applyRecipeStepToState(\n parameters.state,\n parameters.step,\n parameters.preserveControlPlaneMeta\n )\n}\n\nfunction failedRecipeState(environment: Environment): RecipeState {\n return {\n env: environment,\n meta: undefined,\n signalsPending: false,\n status: \"failed\",\n }\n}\n\nfunction annotateRecipeChildError(moduleName: string, error: unknown): Error {\n const prefix = `[${moduleName}] `\n if (error instanceof CommandError) {\n return new CommandError(`${prefix}${error.message}`, error.fullStdout, error.fullStderr)\n }\n if (error instanceof Error) {\n return new Error(`${prefix}${error.message}`)\n }\n return new Error(`${prefix}${String(error)}`)\n}\n\ntype RecipeChildExecution =\n | { kind: \"failed\"; state: RecipeState }\n | {\n kind: \"step\"\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n }\n\nasync function executeRecipeChildStep(parameters: {\n currentEnvironment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n ssh: null | SshConnection\n targetModule: Module\n verbose: boolean\n}): Promise<RecipeChildExecution> {\n try {\n return { kind: \"step\", step: await executeOneModule(parameters) }\n } catch (error) {\n if (parameters.shutdownSignal() != null) {\n return { kind: \"step\", step: INTERRUPTED_BEFORE_APPLY }\n }\n printModuleResult(parameters.targetModule.name, \"failed\")\n printCommandFailure(error, parameters.verbose)\n return { kind: \"failed\", state: failedRecipeState(parameters.currentEnvironment) }\n }\n}\n\nasync function processRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n shutdownSignal: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n signals?: Module[]\n ssh: null | SshConnection\n state: RecipeState\n step: RecipeChildExecution\n verbose: boolean\n}): Promise<RecipeLoopStepResult> {\n if (parameters.step.kind === \"failed\") {\n return { kind: \"break\", state: parameters.step.state }\n }\n\n const nextState = await applyExecutedRecipeStep({\n onChildStep: parameters.onChildStep,\n preserveControlPlaneMeta: parameters.preserveControlPlaneMeta,\n state: parameters.state,\n step: parameters.step.step,\n })\n if (nextState == null) {\n return { kind: \"break\", state: parameters.state }\n }\n\n let state = nextState\n if (shouldFlushRecipeSignals(parameters.step.step, state, parameters.signals)) {\n const signalStatus = await flushPendingRecipeSignals({\n environment: state.env,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n state = applyRecipeSignalStatus(state, signalStatus)\n }\n\n if (state.status === \"failed\" || state.stopRun === true) {\n return { kind: \"break\", state }\n }\n\n return { kind: \"continue\", state }\n}\n\nasync function executeModules(\n modules: Module[],\n ssh: null | SshConnection,\n parameters: ExecuteModulesParameters\n): Promise<RecipeState> {\n const onChildStep = parameters.onChildStep\n const preserveControlPlaneMeta = onChildStep == null\n const shutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let state: RecipeState = {\n // R-0000079: preserve the null-prototype guarantee that\n // R-0000069/R-0000070/R-0000074 establish for the runner-level\n // environment. Plain object spread (`{ ...environment }`) would create a\n // map with `Object.prototype`, exposing the first child module of every\n // recipe to a polluted-fähige environment. Mirror the meta.ts:214\n // approach.\n env: Object.assign(createNullPrototypeEnvironment(), parameters.environment),\n meta: undefined,\n signalsPending: false,\n status: \"ok\",\n }\n\n for (const currentModule of modules) {\n if (shutdownSignal() != null) break\n // eslint-disable-next-line no-await-in-loop\n const step = await executeRecipeChildStep({\n currentEnvironment: state.env,\n onChildStep,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.signalHooks,\n ssh,\n targetModule: currentModule,\n verbose,\n })\n // eslint-disable-next-line no-await-in-loop\n const processedStep = await processRecipeStep({\n onChildStep,\n onSignalStep: parameters.onSignalStep,\n preserveControlPlaneMeta,\n shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh,\n state,\n step,\n verbose,\n })\n state = processedStep.state\n if (processedStep.kind === \"break\") return state\n }\n\n return state\n}\n\nasync function triggerSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return runSignalModules({\n environment: parameters.environment,\n hooks: parameters.signalHooks,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n}\n\nfunction shouldFlushRecipeSignals(\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY,\n state: RecipeState,\n signals?: Module[]\n): signals is Module[] {\n return (\n step != null &&\n step !== INTERRUPTED_BEFORE_APPLY &&\n step._flushSignals === true &&\n state.signalsPending &&\n signals != null\n )\n}\n\nfunction applyRecipeSignalStatus(\n state: RecipeState,\n signalStatus: \"changed\" | \"failed\"\n): RecipeState {\n return {\n ...state,\n signalsPending: false,\n status: signalStatus === \"failed\" ? \"failed\" : state.status,\n }\n}\n\nasync function flushPendingRecipeSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return triggerSignals(parameters)\n}\n\nasync function applyRecipe(parameters: {\n environment: Environment\n modules: Module[]\n name: string\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n signals?: Module[]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n return withRecipeOutputScope(async () => {\n const shutdownSignal = parameters.options?.shutdownSignal\n const verbose = parameters.options?.verbose ?? false\n printRecipeHeader(parameters.name)\n const state = await executeModules(parameters.modules, parameters.ssh, {\n environment: parameters.environment,\n onChildStep: parameters.options?.onChildStep,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n verbose,\n })\n\n if (shouldRunRecipeSignalsAtEnd(state, parameters.signals)) {\n state.status = await triggerSignals({\n environment: state.env,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose,\n })\n }\n\n return {\n _stopRun: state.stopRun,\n meta: state.meta,\n status: state.status,\n }\n })\n}\n\nfunction shouldRunRecipeSignalsAtEnd(state: RecipeState, signals?: Module[]): signals is Module[] {\n return state.signalsPending && state.status === \"changed\" && signals != null\n}\n\nfunction shouldExecuteRecipeDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nfunction recipeNeedsDryRunApply(modules: Module[]): boolean {\n return modules.some((module) => shouldExecuteRecipeDryRun(module))\n}\n\nfunction createRecipeDryRunApply(\n name: string,\n modules: Module[],\n needsDryRunApply: boolean\n): Module[\"_applyDryRun\"] | undefined {\n if (!needsDryRunApply) return undefined\n return async (\n ssh: null | SshConnection,\n environment: Environment,\n parameters?: {\n shutdownSignal?: () => null | ShutdownSignal\n }\n ): Promise<ModuleResult> => {\n const result = await dryRunRecipeModule({\n environment,\n recipeModule: {\n _isRecipe: true,\n _modules: modules,\n async apply() {\n await Promise.resolve()\n return { status: \"ok\" }\n },\n async check() {\n await Promise.resolve()\n return \"ok\"\n },\n name,\n },\n shutdownSignal: parameters?.shutdownSignal,\n ssh,\n })\n\n return {\n _stopRun: result.stopRun,\n meta: result.meta,\n status: result.status ?? \"ok\",\n }\n }\n}\n\n/**\n * Group a list of modules into a named, self-contained recipe.\n *\n * The recipe runs each child module in order, short-circuits on the first\n * failure, and propagates `meta` env values from one module to all subsequent\n * ones. If any child reports `\"changed\"`, the optional `signals` are triggered\n * at the end of the run.\n *\n * @param name - Display name shown in the run output header.\n * @param modules - Ordered list of modules to execute.\n * @param options - Optional recipe configuration.\n * @param options.signals - Modules to fire when at least one child changed state.\n * @returns A RecipeModule that groups the child modules.\n *\n * @example\n * export const nginxRecipe = recipe(\"nginx\", [\n * apt.installed(\"nginx\"),\n * file.template(\"/etc/nginx/nginx.conf\", \"./files/nginx.conf.tmpl\"),\n * service.enabled(\"nginx\"),\n * ], {\n * signals: [service.reload(\"nginx\")],\n * });\n */\nexport function recipe(\n name: string,\n modules: Module[],\n options?: { signals?: Module[] }\n): RecipeModule {\n const needsDryRunApply = recipeNeedsDryRunApply(modules)\n const applyDryRun = createRecipeDryRunApply(name, modules, needsDryRunApply)\n\n return {\n ...(modules.some((module) => module._dryRunBlocker === true)\n ? { _dryRunBlocker: true as const }\n : {}),\n ...(modules.some((module) => module._dryRunMetaProducer === true)\n ? { _dryRunMetaProducer: true as const }\n : {}),\n ...(applyDryRun == null ? {} : { _applyDryRun: applyDryRun }),\n _isRecipe: true,\n _modules: modules,\n _signals: options?.signals,\n _supportsChildStepHook: true,\n async apply(\n ssh: null | SshConnection,\n environment: Environment,\n parameters?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => null | ShutdownSignal\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ): Promise<ModuleResult> {\n return applyRecipe({\n environment,\n modules,\n name,\n options: parameters,\n signals: options?.signals,\n ssh,\n })\n },\n\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n // Each child receives the original environment — no meta propagation,\n // because check() never calls apply() and therefore produces no meta.\n for (const childModule of modules) {\n // R-0000096 / R-0000797: cooperate with SIGINT/SIGTERM during long\n // check phases. The runner installs the abort signal via\n // setRunnerAbortSignal at the start of a run; honoring it here keeps\n // recipe.check responsive when individual children have slow check\n // implementations.\n //\n // R-0000797: return NEEDS_APPLY (not \"ok\") on abort. The previous\n // \"ok\" sentinel made an interrupted check indistinguishable from a\n // successful clean state — the next run could observe the cached\n // \"ok\" and skip the apply that the partial check never reached.\n // Surfacing NEEDS_APPLY tells the caller that the check was\n // incomplete and that an apply must still run after the shutdown\n // unwinds.\n if (getRunnerAbortSignal()?.aborted === true) return NEEDS_APPLY\n const connection = childModule.local === true ? null : ssh\n let result: \"needs-apply\" | \"ok\"\n try {\n // eslint-disable-next-line no-await-in-loop\n result = await childModule.check(connection, environment)\n } catch (error) {\n throw annotateRecipeChildError(childModule.name, error)\n }\n if (result === NEEDS_APPLY) return NEEDS_APPLY\n }\n return \"ok\"\n },\n\n name,\n }\n}\n","import type { ServerDefinition } from \"./types.js\"\n\nimport { describeHostValidationFailure, validateHostLabel } from \"./hostValidation.js\"\nimport { validateSshConfig } from \"./serverDefinitionValidation.js\"\n\nfunction isModuleLike(value: unknown): boolean {\n if (value === null || typeof value !== \"object\") return false\n return (\n \"apply\" in value &&\n \"check\" in value &&\n \"name\" in value &&\n typeof value.name === \"string\" &&\n value.name.length > 0 &&\n typeof value.check === \"function\" &&\n typeof value.apply === \"function\"\n )\n}\n\nfunction validateModuleList(\n modules: unknown,\n property: \"run\" | \"signals\",\n options?: { requireNonEmpty?: boolean }\n): void {\n if (!Array.isArray(modules)) {\n throw new TypeError(`ServerDefinition: ${property} must be an array of modules`)\n }\n if (options?.requireNonEmpty === true && modules.length === 0) {\n throw new Error(\"ServerDefinition: run must contain at least one module\")\n }\n for (const [index, module] of modules.entries()) {\n if (!isModuleLike(module)) {\n throw new Error(\n `ServerDefinition: ${property}[${index}] must be a module with name, check, and apply`\n )\n }\n }\n}\n\nexport function validateServerDefinition(\n config: ServerDefinition,\n options?: { allowEmptyRun?: boolean }\n): void {\n const hostValidationFailure = validateHostLabel(config.host)\n if (hostValidationFailure === \"empty\") {\n throw new Error(\"ServerDefinition: host is required\")\n }\n if (hostValidationFailure != null) {\n throw new Error(\n `ServerDefinition: host ${describeHostValidationFailure(hostValidationFailure)}`\n )\n }\n if (config.name.length === 0) {\n throw new Error(\"ServerDefinition: name is required\")\n }\n validateSshConfig(config.ssh)\n validateModuleList(config.run, \"run\", { requireNonEmpty: options?.allowEmptyRun !== true })\n if (config.signals !== undefined) validateModuleList(config.signals, \"signals\")\n}\n\n/**\n * Define a server and validate its configuration at construction time.\n *\n * This is a thin identity function whose only purpose is to provide\n * type-safe validation with descriptive error messages before the runner\n * ever attempts an SSH connection.\n *\n * @param config - The server definition to validate and return.\n * @returns The validated server definition.\n * @throws {Error} When any required field is missing or empty.\n *\n * @example\n * export default server({\n * name: \"web-01\",\n * host: \"10.0.0.1\",\n * ssh: { user: \"root\", ports: [22], privateKey: \"~/.ssh/id_ed25519\" }, // \"~\" is expanded\n * run: [apt.installed(\"nginx\")],\n * });\n */\nexport function server(config: ServerDefinition): ServerDefinition {\n validateServerDefinition(config)\n return config\n}\n"],"mappings":"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","names":["ssh","ssh","ssh","ssh","signals"]}
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -1,119 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
import { M as Module } from '../types-Cl2Muw1x.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Modules for managing swap files and common swap-related kernel tuning.
|
|
6
|
-
*/
|
|
7
|
-
declare const swap: {
|
|
8
|
-
/**
|
|
9
|
-
* Ensure a file-backed swap area exists, is activated, and is persisted in `/etc/fstab`.
|
|
10
|
-
*
|
|
11
|
-
* @param options - Configuration for the swap file.
|
|
12
|
-
* @param options.mode - File mode applied to the swap file. Defaults to `0600`.
|
|
13
|
-
* @param options.path - Absolute path to the swap file, e.g. `/swapfile`.
|
|
14
|
-
* @param options.priority - Optional swap priority written into the fstab entry.
|
|
15
|
-
* @param options.size - Desired file size as bytes or a shell-friendly size string such as `2G`.
|
|
16
|
-
* @param options.state - Whether the swap file should be `present` (default) or `absent`.
|
|
17
|
-
* @returns A Module that manages the swap file lifecycle.
|
|
18
|
-
*/
|
|
19
|
-
file(options: {
|
|
20
|
-
mode?: string;
|
|
21
|
-
path: string;
|
|
22
|
-
priority?: number;
|
|
23
|
-
size: number | string;
|
|
24
|
-
state?: "absent" | "present";
|
|
25
|
-
}): Module;
|
|
26
|
-
/**
|
|
27
|
-
* Persist `vm.swappiness`.
|
|
28
|
-
*
|
|
29
|
-
* @param value - Desired swappiness value.
|
|
30
|
-
* @returns A Module that manages `vm.swappiness`.
|
|
31
|
-
*/
|
|
32
|
-
swappiness(value: number): Module;
|
|
33
|
-
/**
|
|
34
|
-
* Persist `vm.vfs_cache_pressure`.
|
|
35
|
-
*
|
|
36
|
-
* @param value - Desired VFS cache pressure value.
|
|
37
|
-
* @returns A Module that manages `vm.vfs_cache_pressure`.
|
|
38
|
-
*/
|
|
39
|
-
vfsCachePressure(value: number): Module;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/** Options for `timer.scheduled`. */
|
|
43
|
-
type TimerScheduledOptions = {
|
|
44
|
-
/** Optional `AccuracySec=` for the `[Timer]` section. */
|
|
45
|
-
accuracySec?: number | string;
|
|
46
|
-
/** Description used in both unit `[Unit]` sections. Defaults to `Paratix scheduled task: <name>`. */
|
|
47
|
-
description?: string;
|
|
48
|
-
/** Extra environment variables rendered as `Environment=KEY=VAL` lines in `[Service]`. */
|
|
49
|
-
environment?: Record<string, string>;
|
|
50
|
-
/** The single command line written as `ExecStart=` in the generated `.service`. */
|
|
51
|
-
exec: string;
|
|
52
|
-
/** Optional `Group=` for the `[Service]` section. */
|
|
53
|
-
group?: string;
|
|
54
|
-
/** Calendar specification(s) written as one or more `OnCalendar=` lines. */
|
|
55
|
-
onCalendar: string | string[];
|
|
56
|
-
/** Whether `Persistent=true` is written into the `[Timer]` section. Defaults to `true`. */
|
|
57
|
-
persistent?: boolean;
|
|
58
|
-
/** Optional `RandomizedDelaySec=` for the `[Timer]` section. */
|
|
59
|
-
randomizedDelaySec?: number | string;
|
|
60
|
-
/** Whether the timer should be `"present"` or `"absent"`. Defaults to `"present"`. */
|
|
61
|
-
state?: "absent" | "present";
|
|
62
|
-
/** Optional `User=` for the `[Service]` section. */
|
|
63
|
-
user?: string;
|
|
64
|
-
/** Optional `WorkingDirectory=` for the `[Service]` section. */
|
|
65
|
-
workingDirectory?: string;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Modules for managing systemd timer-driven scheduled tasks.
|
|
70
|
-
*
|
|
71
|
-
* `scheduled(name, options)` is the timer-based equivalent of `cron.job`: it
|
|
72
|
-
* generates a `.service` and `.timer` unit pair under `/etc/systemd/system/`,
|
|
73
|
-
* reloads systemd, and enables and starts the timer in a single idempotent
|
|
74
|
-
* step. Compared to cron, this gives you `Persistent=` for catch-up runs,
|
|
75
|
-
* journald logging, and richer scheduling expressions.
|
|
76
|
-
*/
|
|
77
|
-
declare const timer: {
|
|
78
|
-
/**
|
|
79
|
-
* Ensure a systemd timer-driven scheduled task does not exist.
|
|
80
|
-
*
|
|
81
|
-
* Disables and stops `<name>.timer`, removes both `<name>.service` and
|
|
82
|
-
* `<name>.timer` from `/etc/systemd/system/`, and reloads systemd. The
|
|
83
|
-
* `disable --now` step also removes the timer's `wants/` symlink. Failure
|
|
84
|
-
* to disable a unit that does not exist is ignored, so this method is
|
|
85
|
-
* safe to apply repeatedly.
|
|
86
|
-
*
|
|
87
|
-
* Behaves like `timer.scheduled(name, { exec: "<unused>", onCalendar: "<unused>", state: "absent" })`,
|
|
88
|
-
* but does not require placeholder values for `exec` or `onCalendar` and
|
|
89
|
-
* reports failures with a `timer.absent` prefix instead of `timer.scheduled`.
|
|
90
|
-
*
|
|
91
|
-
* @param name - Base unit name without extension. Must match
|
|
92
|
-
* `^[A-Za-z0-9_\-]+$`.
|
|
93
|
-
* @returns A Module that ensures the timer-driven scheduled task is absent.
|
|
94
|
-
*/
|
|
95
|
-
absent(name: string): Module;
|
|
96
|
-
/**
|
|
97
|
-
* Ensure a systemd timer-driven scheduled task is present (or absent).
|
|
98
|
-
*
|
|
99
|
-
* Generates `<name>.service` (`Type=oneshot`, no `[Install]` section since
|
|
100
|
-
* it is triggered exclusively by the timer) and `<name>.timer` under
|
|
101
|
-
* `/etc/systemd/system/`, reloads systemd, and runs
|
|
102
|
-
* `systemctl enable --now <name>.timer`. When the timer unit content
|
|
103
|
-
* changed, the timer is restarted so a new `OnCalendar=` schedule is picked
|
|
104
|
-
* up immediately. With `state: "absent"`, the timer is disabled and
|
|
105
|
-
* stopped, both unit files are removed, and systemd is reloaded.
|
|
106
|
-
*
|
|
107
|
-
* Validation of `exec`, `description`, `user`, `group`, `workingDirectory`,
|
|
108
|
-
* `environment` and `onCalendar` only runs when `state` is `"present"`,
|
|
109
|
-
* so callers can pass placeholder values when removing a timer.
|
|
110
|
-
*
|
|
111
|
-
* @param name - Base unit name without extension. Used as `<name>.service`
|
|
112
|
-
* and `<name>.timer`. Must match `^[A-Za-z0-9_\-]+$`.
|
|
113
|
-
* @param options - Schedule, command, optional service hardening, and state.
|
|
114
|
-
* @returns A Module that manages the timer-driven scheduled task.
|
|
115
|
-
*/
|
|
116
|
-
scheduled(name: string, options: TimerScheduledOptions): Module;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export { swap, timer };
|
|
1
|
+
export { V as UpgradeOptions, o as apt, p as archive, q as command, r as compose, s as cron, t as download, u as file, v as git, w as group, x as hostname, y as mount, z as net, A as op, B as package, C as quadlet, D as releaseUpgrade, F as rsync, G as script, H as service, I as ssh, J as sshd, K as swap, L as sysctl, P as system, Q as systemd, R as timer, T as ufw, U as user } from '../index-udpAybq3.js';
|