paratix 0.3.0 → 0.5.0
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 +20 -1
- package/dist/{chunk-DUIGEB2J.js → chunk-3WK4QNJK.js} +73 -17
- package/dist/chunk-3WK4QNJK.js.map +1 -0
- package/dist/{chunk-ULJMW23T.js → chunk-LI47NIKN.js} +683 -64
- package/dist/chunk-LI47NIKN.js.map +1 -0
- package/dist/{chunk-G3BMCQKU.js → chunk-MHPFGCEY.js} +158 -34
- package/dist/chunk-MHPFGCEY.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +17 -2
- package/dist/index.js +197 -100
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +23 -1313
- package/dist/modules/index.js +6 -2
- package/dist/user-CraCJci7.d.ts +1392 -0
- package/llm-guide.md +86 -4
- package/package.json +1 -1
- package/dist/chunk-DUIGEB2J.js.map +0 -1
- package/dist/chunk-G3BMCQKU.js.map +0 -1
- package/dist/chunk-ULJMW23T.js.map +0 -1
package/README.md
CHANGED
|
@@ -18,8 +18,9 @@ The result is a practical server automation tool with a compact mental model: mo
|
|
|
18
18
|
- **TypeScript authoring**: use regular `.ts` files with imports, conditions, and editor tooling.
|
|
19
19
|
- **Resilient SSH flow**: reconnects after reboots and SSH port changes when modules require it.
|
|
20
20
|
- **Structured orchestration**: recipes and signals keep service reloads and grouped changes explicit.
|
|
21
|
+
- **Declarative host guards**: gate modules on package, command, file, directory, symlink, or socket state without embedding shell checks in strings.
|
|
21
22
|
- **Strong bootstrap story**: supports explicit first-run flows and strict host-key handling.
|
|
22
|
-
- **Practical built-in modules**: packages, files, services, users, SSH, firewall, systemd, sysctl, mount, rsync, and more.
|
|
23
|
+
- **Practical built-in modules**: packages, files, services, users, SSH, firewall, systemd, sysctl, swap, mount, rsync, and more.
|
|
23
24
|
|
|
24
25
|
## Getting Started
|
|
25
26
|
|
|
@@ -85,6 +86,8 @@ A playbook is a TypeScript file that default-exports `server(...)`. It defines t
|
|
|
85
86
|
|
|
86
87
|
Modules are the smallest units of work. Each module checks whether its target state already exists and only applies changes when needed.
|
|
87
88
|
|
|
89
|
+
For filesystem metadata, you can now also use dedicated modules such as `file.chmod(...)` and `file.chown(...)` when you want to manage permissions or ownership without coupling that change to a file upload or template render.
|
|
90
|
+
|
|
88
91
|
### Recipes
|
|
89
92
|
|
|
90
93
|
Recipes group related modules into a named unit. They help structure larger playbooks and keep the CLI output readable.
|
|
@@ -93,6 +96,22 @@ Recipes group related modules into a named unit. They help structure larger play
|
|
|
93
96
|
|
|
94
97
|
Signals are deferred side effects such as `service.reload(...)` or `service.restart(...)`. They run when the surrounding scope actually changed, and can also be flushed explicitly with `signals.flush()` when you need a checkpoint inside a larger flow.
|
|
95
98
|
|
|
99
|
+
### Compose + systemd
|
|
100
|
+
|
|
101
|
+
`compose.systemd(...)` can generate a native systemd unit for Docker Compose or Podman Compose projects. By default it now starts the stack with `compose up --remove-orphans`; set `detached: true` if you explicitly want the old `-d` behaviour in the generated `ExecStart`.
|
|
102
|
+
|
|
103
|
+
### Podman Quadlets
|
|
104
|
+
|
|
105
|
+
For Podman-native services, Paratix now also includes `quadlet.container(...)`. It writes a `.container` file under `/etc/containers/systemd`, reloads systemd when the content changes, and works cleanly with `service.enabled(...)` and `service.running(...)` for the generated service.
|
|
106
|
+
|
|
107
|
+
### Guards
|
|
108
|
+
|
|
109
|
+
Paratix also supports declarative host-state guards. Use `when.packageInstalled(...)`, `when.commandExists(...)`, `when.fileExists(...)`, `when.pathExists(...)`, `when.symlinkExists(...)`, or `when.socketExists(...)` and their inverted forms to gate modules or recipes on remote host state without shell-heavy playbooks.
|
|
110
|
+
|
|
111
|
+
### Swap
|
|
112
|
+
|
|
113
|
+
Paratix can also manage file-backed swap directly. Use `swap.file(...)` to provision and persist a swap file, then tune common memory behaviour with `swap.swappiness(...)` or `swap.vfsCachePressure(...)`.
|
|
114
|
+
|
|
96
115
|
## CLI
|
|
97
116
|
|
|
98
117
|
```text
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CommandError,
|
|
3
3
|
assertValidModuleMetaEntries,
|
|
4
4
|
mergeEnvironmentFromMeta
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MHPFGCEY.js";
|
|
6
6
|
|
|
7
7
|
// src/output.ts
|
|
8
8
|
import pc from "picocolors";
|
|
@@ -102,6 +102,8 @@ var CLI_HEADER_LINES = [
|
|
|
102
102
|
" |_| "
|
|
103
103
|
];
|
|
104
104
|
var activeSpinner = null;
|
|
105
|
+
var activeRecipeGuideDepths = [];
|
|
106
|
+
var pendingRecipeClosureGuideDepths = [];
|
|
105
107
|
var recipeOutputDepth = -1;
|
|
106
108
|
function renderCliHeader(version) {
|
|
107
109
|
const versionText = pc.dim(`v${version}`);
|
|
@@ -139,11 +141,34 @@ function getModuleStatusText(status) {
|
|
|
139
141
|
function getCurrentOutputDepth() {
|
|
140
142
|
return Math.max(recipeOutputDepth, 0);
|
|
141
143
|
}
|
|
144
|
+
function getGuideDot(depth) {
|
|
145
|
+
return depth % 2 === 0 ? pc.gray("\xB7") : pc.cyan("\xB7");
|
|
146
|
+
}
|
|
147
|
+
function buildGuideIndent(baseIndent, options) {
|
|
148
|
+
const indentCharacters = Array.from(baseIndent);
|
|
149
|
+
const guideDepths = [
|
|
150
|
+
...options?.activeGuideDepths ?? activeRecipeGuideDepths,
|
|
151
|
+
...options?.extraGuideDepths ?? []
|
|
152
|
+
];
|
|
153
|
+
for (const guideDepth of guideDepths) {
|
|
154
|
+
const guideCharacterIndex = OUTPUT_INDENT_UNIT.length * (guideDepth + 1);
|
|
155
|
+
if (guideCharacterIndex >= indentCharacters.length) continue;
|
|
156
|
+
indentCharacters[guideCharacterIndex] = options?.colorize === false ? "\xB7" : getGuideDot(guideDepth);
|
|
157
|
+
}
|
|
158
|
+
return indentCharacters.join("");
|
|
159
|
+
}
|
|
160
|
+
function clearPendingRecipeClosureGuides() {
|
|
161
|
+
pendingRecipeClosureGuideDepths = [];
|
|
162
|
+
}
|
|
142
163
|
function getModuleIndent() {
|
|
143
|
-
|
|
164
|
+
if (recipeOutputDepth < 0) {
|
|
165
|
+
return OUTPUT_INDENT_UNIT;
|
|
166
|
+
}
|
|
167
|
+
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2);
|
|
144
168
|
}
|
|
145
169
|
function getRecipeHeaderIndent() {
|
|
146
|
-
|
|
170
|
+
if (recipeOutputDepth < 0) return "";
|
|
171
|
+
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1);
|
|
147
172
|
}
|
|
148
173
|
function getErrorIndent() {
|
|
149
174
|
return `${getModuleIndent()}\u2502 `;
|
|
@@ -156,16 +181,19 @@ async function withRecipeOutputScope(scopedOperation) {
|
|
|
156
181
|
try {
|
|
157
182
|
return await scopedOperation();
|
|
158
183
|
} finally {
|
|
184
|
+
activeRecipeGuideDepths = activeRecipeGuideDepths.filter((depth) => depth !== recipeOutputDepth);
|
|
185
|
+
pendingRecipeClosureGuideDepths = [recipeOutputDepth];
|
|
159
186
|
recipeOutputDepth -= 1;
|
|
160
187
|
}
|
|
161
188
|
}
|
|
162
189
|
function renderModuleLine(parameters) {
|
|
163
|
-
const { detail, name, status, waitingFrame } = parameters;
|
|
164
|
-
const
|
|
190
|
+
const { detail, extraGuideDepths = [], name, status, waitingFrame } = parameters;
|
|
191
|
+
const baseIndent = getModuleIndent();
|
|
192
|
+
const indent = buildGuideIndent(baseIndent, { extraGuideDepths });
|
|
165
193
|
const icon = getModuleIcon(status, waitingFrame);
|
|
166
194
|
const statusText = getModuleStatusText(status);
|
|
167
195
|
const detailSuffix = detail == null ? "" : ` ${pc.dim(detail)}`;
|
|
168
|
-
const alignedNameWidth = Math.max(MODULE_NAME_WIDTH -
|
|
196
|
+
const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - baseIndent.length, MIN_MODULE_NAME_WIDTH);
|
|
169
197
|
return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`;
|
|
170
198
|
}
|
|
171
199
|
function writeAnimatedModuleLine(line) {
|
|
@@ -184,6 +212,7 @@ function stopAnimatedModuleLine(clearCurrentLine = false) {
|
|
|
184
212
|
}
|
|
185
213
|
function startModuleSpinner(name, detail) {
|
|
186
214
|
if (!supportsAnimatedModuleOutput()) return;
|
|
215
|
+
clearPendingRecipeClosureGuides();
|
|
187
216
|
stopAnimatedModuleLine();
|
|
188
217
|
const displayModule = formatDisplayModule({
|
|
189
218
|
continuationIndentWidth: getContinuationIndent().length,
|
|
@@ -219,8 +248,12 @@ function startModuleSpinner(name, detail) {
|
|
|
219
248
|
}
|
|
220
249
|
function printRecipeHeader(name) {
|
|
221
250
|
stopAnimatedModuleLine(true);
|
|
251
|
+
clearPendingRecipeClosureGuides();
|
|
222
252
|
const header = pc.bold(pc.blue(`[${name}]`));
|
|
223
|
-
console.log(`${getRecipeHeaderIndent()}${header}`);
|
|
253
|
+
console.log(`${buildGuideIndent(getRecipeHeaderIndent())}${header}`);
|
|
254
|
+
if (recipeOutputDepth >= 0) {
|
|
255
|
+
activeRecipeGuideDepths = [...activeRecipeGuideDepths, recipeOutputDepth];
|
|
256
|
+
}
|
|
224
257
|
}
|
|
225
258
|
function printRunContext(parameters) {
|
|
226
259
|
const mode = parameters.dryRun ? pc.yellow("dry-run") : pc.green("apply");
|
|
@@ -229,34 +262,56 @@ function printRunContext(parameters) {
|
|
|
229
262
|
pc.dim(`Run ${parameters.name} \xB7 host ${parameters.host} \xB7 ports ${ports} \xB7 mode ${mode}`)
|
|
230
263
|
);
|
|
231
264
|
}
|
|
232
|
-
function
|
|
265
|
+
function printRenderedModuleResult(parameters) {
|
|
266
|
+
const extraGuideDepths = parameters.extraGuideDepths ?? [];
|
|
233
267
|
const displayModule = formatDisplayModule({
|
|
234
|
-
continuationIndentWidth:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
268
|
+
continuationIndentWidth: `${buildGuideIndent(
|
|
269
|
+
OUTPUT_INDENT_UNIT.repeat(Math.max(getCurrentOutputDepth() + 2, 1)),
|
|
270
|
+
{ extraGuideDepths }
|
|
271
|
+
)} `.length,
|
|
272
|
+
detail: parameters.detail,
|
|
273
|
+
name: parameters.name,
|
|
274
|
+
status: parameters.status,
|
|
238
275
|
terminalColumns: process.stdout.columns
|
|
239
276
|
});
|
|
240
277
|
const line = renderModuleLine({
|
|
241
278
|
detail: displayModule.detail,
|
|
279
|
+
extraGuideDepths,
|
|
242
280
|
name: displayModule.name,
|
|
243
|
-
status
|
|
281
|
+
status: parameters.status
|
|
244
282
|
});
|
|
245
283
|
if (supportsAnimatedModuleOutput() && activeSpinner != null) {
|
|
246
284
|
stopAnimatedModuleLine();
|
|
247
285
|
writeAnimatedModuleLine(line);
|
|
248
286
|
process.stdout.write("\n");
|
|
249
287
|
for (const detailLine of displayModule.detailLines) {
|
|
250
|
-
process.stdout.write(
|
|
251
|
-
|
|
288
|
+
process.stdout.write(
|
|
289
|
+
`${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${pc.dim(detailLine)}
|
|
290
|
+
`
|
|
291
|
+
);
|
|
252
292
|
}
|
|
253
293
|
return;
|
|
254
294
|
}
|
|
255
295
|
console.log(line);
|
|
256
296
|
for (const detailLine of displayModule.detailLines) {
|
|
257
|
-
console.log(
|
|
297
|
+
console.log(
|
|
298
|
+
`${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${pc.dim(detailLine)}`
|
|
299
|
+
);
|
|
258
300
|
}
|
|
259
301
|
}
|
|
302
|
+
function printModuleResult(name, status, detail) {
|
|
303
|
+
clearPendingRecipeClosureGuides();
|
|
304
|
+
printRenderedModuleResult({ detail, name, status });
|
|
305
|
+
}
|
|
306
|
+
function printRecipeModuleResult(name, status, detail) {
|
|
307
|
+
printRenderedModuleResult({
|
|
308
|
+
detail,
|
|
309
|
+
extraGuideDepths: pendingRecipeClosureGuideDepths,
|
|
310
|
+
name,
|
|
311
|
+
status
|
|
312
|
+
});
|
|
313
|
+
clearPendingRecipeClosureGuides();
|
|
314
|
+
}
|
|
260
315
|
function printCommandError(stdout, stderr) {
|
|
261
316
|
const lines = [];
|
|
262
317
|
if (stderr.trim()) {
|
|
@@ -432,8 +487,9 @@ export {
|
|
|
432
487
|
printRecipeHeader,
|
|
433
488
|
printRunContext,
|
|
434
489
|
printModuleResult,
|
|
490
|
+
printRecipeModuleResult,
|
|
435
491
|
printCommandFailure,
|
|
436
492
|
printSummary,
|
|
437
493
|
runSignalModules
|
|
438
494
|
};
|
|
439
|
-
//# sourceMappingURL=chunk-
|
|
495
|
+
//# sourceMappingURL=chunk-3WK4QNJK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/output.ts","../src/outputFormatting.ts","../src/signalOrchestration.ts"],"sourcesContent":["/* eslint-disable max-lines -- CLI output rendering is intentionally kept together */\nimport pc from \"picocolors\"\n\nimport type { ModuleStatus } from \"./types.js\"\n\nimport { fitAnimatedModuleLine, formatDisplayModule } from \"./outputFormatting.js\"\nimport { CommandError } from \"./sshHelpers.js\"\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 startModuleSpinner(name: string, detail?: string): void {\n if (!supportsAnimatedModuleOutput()) return\n\n clearPendingRecipeClosureGuides()\n stopAnimatedModuleLine()\n const displayModule = formatDisplayModule({\n continuationIndentWidth: getContinuationIndent().length,\n detail,\n name,\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\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 printRenderedModuleResult(parameters: {\n detail?: string\n extraGuideDepths?: number[]\n name: string\n status: DisplayStatus\n}): void {\n const extraGuideDepths = parameters.extraGuideDepths ?? []\n const displayModule = formatDisplayModule({\n continuationIndentWidth: `${buildGuideIndent(\n OUTPUT_INDENT_UNIT.repeat(Math.max(getCurrentOutputDepth() + 2, 1)),\n { extraGuideDepths }\n )} `.length,\n detail: parameters.detail,\n name: parameters.name,\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\n if (supportsAnimatedModuleOutput() && activeSpinner != null) {\n stopAnimatedModuleLine()\n writeAnimatedModuleLine(line)\n process.stdout.write(\"\\n\")\n for (const detailLine of displayModule.detailLines) {\n process.stdout.write(\n `${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${pc.dim(detailLine)}\\n`\n )\n }\n return\n }\n\n console.log(line)\n for (const detailLine of displayModule.detailLines) {\n console.log(\n `${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${pc.dim(detailLine)}`\n )\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 */\nexport function printModuleResult(name: string, status: DisplayStatus, detail?: string): void {\n clearPendingRecipeClosureGuides()\n printRenderedModuleResult({ detail, name, status })\n}\n\nexport function printRecipeModuleResult(name: string, status: DisplayStatus, detail?: string): void {\n printRenderedModuleResult({\n detail,\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 const lines: string[] = []\n if (stderr.trim()) {\n lines.push(...stderr.trim().split(\"\\n\"))\n }\n if (stdout.trim()) {\n lines.push(...stdout.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 if (stderr.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stderr:`))\n for (const line of stderr.trim().split(\"\\n\")) {\n console.error(pc.red(`${getErrorIndent()}${line}`))\n }\n }\n if (stdout.trim()) {\n console.error(pc.red(`${getErrorIndent()}Full stdout:`))\n for (const line of stdout.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(cause: unknown, depth: number): void {\n const label = `Cause ${depth}:`\n if (cause instanceof Error) {\n const stack = cause.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(cause)\n printVerboseErrorBlock(label, stackOrMessage)\n const nestedCause = getErrorCause(cause)\n if (nestedCause !== undefined) {\n printVerboseErrorCause(nestedCause, depth + 1)\n }\n return\n }\n\n printVerboseErrorBlock(label, String(cause))\n}\n\nfunction printVerboseGenericError(error: Error): void {\n const stack = error.stack?.trim() ?? \"\"\n const stackOrMessage = stack.length > 0 ? stack : String(error)\n printVerboseErrorBlock(\"Full stack:\", stackOrMessage)\n const cause = getErrorCause(error)\n if (cause !== undefined) {\n printVerboseErrorCause(cause, 1)\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 * @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(\"\", summaryLine)\n printVerboseCommandError(error.fullStdout, error.fullStderr)\n return\n }\n\n printCommandError(\"\", String(error))\n if (verbose && error instanceof Error) {\n printVerboseGenericError(error)\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 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 {\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 = await mergeEnvironmentFromMeta(\n parameters.currentEnvironment,\n parameters.result.meta\n )\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 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 = await parameters.signal.apply(connection, parameters.currentEnvironment)\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 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"],"mappings":"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","names":[]}
|