paratix 0.12.6 → 0.13.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 +5 -1
- package/dist/{chunk-CWGQ2MYN.js → chunk-XIRORNO3.js} +252 -25
- package/dist/cli.js +2259 -1505
- package/dist/{index-udpAybq3.d.ts → index-DwKdTyHo.d.ts} +25 -6
- package/dist/index.d.ts +18 -6
- package/dist/index.js +56 -29
- package/dist/modules/index.d.ts +1 -1
- package/dist/modules/index.js +1 -2
- package/llm-guide.md +17 -17
- package/package.json +5 -3
- package/dist/chunk-CWGQ2MYN.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/modules/index.js.map +0 -1
|
@@ -157,6 +157,17 @@ type Module = {
|
|
|
157
157
|
* @returns `"ok"` if the desired state is already present, `"needs-apply"` otherwise.
|
|
158
158
|
*/
|
|
159
159
|
check: (ssh: null | SshConnection, environment: Environment) => Promise<"needs-apply" | "ok">;
|
|
160
|
+
/**
|
|
161
|
+
* Internal discriminator that distinguishes leaf modules from recipes.
|
|
162
|
+
*
|
|
163
|
+
* Leaf modules leave this `undefined` (treated as `"module"`); recipes set
|
|
164
|
+
* `"recipe"` so the runner can narrow to the recipe contract via the
|
|
165
|
+
* `isRecipe` type guard instead of an unsafe structural cast. Intentionally
|
|
166
|
+
* optional so existing custom modules and playbooks stay source-compatible —
|
|
167
|
+
* they never need to set it.
|
|
168
|
+
* @internal
|
|
169
|
+
*/
|
|
170
|
+
kind?: "module" | "recipe";
|
|
160
171
|
/**
|
|
161
172
|
* When true the module runs locally instead of over SSH.
|
|
162
173
|
* The `ssh` parameter will be `null` in check/apply.
|
|
@@ -240,8 +251,16 @@ type SshConnection = {
|
|
|
240
251
|
probeSudo: () => Promise<void>;
|
|
241
252
|
/** Read the full contents of a remote file as a string. */
|
|
242
253
|
readFile: (remotePath: string) => Promise<string>;
|
|
243
|
-
/**
|
|
244
|
-
|
|
254
|
+
/**
|
|
255
|
+
* Reconnect the SSH session using the current host and registered port
|
|
256
|
+
* candidates. An explicit `reconnectTimeout` in the SSH config always takes
|
|
257
|
+
* precedence; when it is unset, `options.defaultTimeout` (in milliseconds)
|
|
258
|
+
* overrides the generic reconnect default for this call — used by the reboot
|
|
259
|
+
* path to grant a longer window.
|
|
260
|
+
*/
|
|
261
|
+
reconnect: (options?: {
|
|
262
|
+
defaultTimeout?: number;
|
|
263
|
+
}) => Promise<void>;
|
|
245
264
|
/** Remove a previously registered port from the reconnect candidate list. */
|
|
246
265
|
removePort: (port: number) => void;
|
|
247
266
|
/** Return the SHA-256 hex digest of a remote file, or `null` if not found. */
|
|
@@ -250,13 +269,13 @@ type SshConnection = {
|
|
|
250
269
|
test: (command: string) => Promise<boolean>;
|
|
251
270
|
/** Update the target host address (e.g. after a reboot with new IP). */
|
|
252
271
|
updateHost: (host: string) => void;
|
|
253
|
-
/** Upload a local file to the remote host via SFTP. */
|
|
272
|
+
/** Upload a local file to the remote host via SFTP. `options.mode` is optional and defaults to `"0600"`. */
|
|
254
273
|
uploadFile: (localPath: string, remotePath: string, options?: {
|
|
255
274
|
mode?: string;
|
|
256
275
|
}) => Promise<void>;
|
|
257
|
-
/** Write a string to a remote file, creating or overwriting it. */
|
|
258
|
-
writeFile: (remotePath: string, content: string, options
|
|
259
|
-
mode
|
|
276
|
+
/** Write a string to a remote file, creating or overwriting it. `options.mode` is optional and defaults to `"0600"`. */
|
|
277
|
+
writeFile: (remotePath: string, content: string, options?: {
|
|
278
|
+
mode?: string;
|
|
260
279
|
}) => Promise<void>;
|
|
261
280
|
};
|
|
262
281
|
/** SSH connection parameters for a server. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { E as Environment, M as Module, a as ModuleMetaEntry, b as MetaEnvironmentValue, c as EnvironmentMetaEntry, S as SshdPortMetaEntry, d as SystemHostMetaEntry, e as SystemRebootMetaEntry, f as ModuleResult, g as ExecResult, h as ModuleStatus, i as SshConnection, O as OrchestrationStep, j as ShutdownSignal, k as ServerDefinition } from './index-
|
|
2
|
-
export { l as EnvironmentValue, m as ExecOptions, N as NEEDS_APPLY, n as SshConfig, 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-
|
|
1
|
+
import { E as Environment, M as Module, a as ModuleMetaEntry, b as MetaEnvironmentValue, c as EnvironmentMetaEntry, S as SshdPortMetaEntry, d as SystemHostMetaEntry, e as SystemRebootMetaEntry, f as ModuleResult, g as ExecResult, h as ModuleStatus, i as SshConnection, O as OrchestrationStep, j as ShutdownSignal, k as ServerDefinition } from './index-DwKdTyHo.js';
|
|
2
|
+
export { l as EnvironmentValue, m as ExecOptions, N as NEEDS_APPLY, n as SshConfig, 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-DwKdTyHo.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Fail the run if a condition on the current env is not satisfied.
|
|
@@ -185,11 +185,16 @@ type SignalHooks = {
|
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
187
|
* Internal representation of a recipe module.
|
|
188
|
-
*
|
|
188
|
+
*
|
|
189
|
+
* The `kind: "recipe"` discriminator lets the runner distinguish recipes from
|
|
190
|
+
* leaf modules through the {@link isRecipe} type guard, forming a discriminated
|
|
191
|
+
* union with plain {@link Module} values (which leave `kind` unset). The child
|
|
192
|
+
* `_modules` and optional `_signals` are recipe-specific data that TypeScript
|
|
193
|
+
* narrows to once `isRecipe` confirmed the discriminator — no structural cast
|
|
194
|
+
* is required anywhere.
|
|
189
195
|
* @internal
|
|
190
196
|
*/
|
|
191
197
|
type RecipeModule = {
|
|
192
|
-
_isRecipe: true;
|
|
193
198
|
_modules: Module[];
|
|
194
199
|
_signals?: Module[];
|
|
195
200
|
apply: (ssh: null | SshConnection, environment: Environment, options?: {
|
|
@@ -199,6 +204,7 @@ type RecipeModule = {
|
|
|
199
204
|
signalHooks?: SignalHooks;
|
|
200
205
|
verbose?: boolean;
|
|
201
206
|
}) => Promise<ModuleResult>;
|
|
207
|
+
kind: "recipe";
|
|
202
208
|
} & Module;
|
|
203
209
|
/**
|
|
204
210
|
* Group a list of modules into a named, self-contained recipe.
|
|
@@ -215,8 +221,11 @@ type RecipeModule = {
|
|
|
215
221
|
* @returns A RecipeModule that groups the child modules.
|
|
216
222
|
*
|
|
217
223
|
* @example
|
|
224
|
+
* import { recipe } from "paratix"
|
|
225
|
+
* import { package as pkg, file, service } from "paratix/modules"
|
|
226
|
+
*
|
|
218
227
|
* export const nginxRecipe = recipe("nginx", [
|
|
219
|
-
*
|
|
228
|
+
* pkg.installed("nginx"),
|
|
220
229
|
* file.template("/etc/nginx/nginx.conf", "./files/nginx.conf.tmpl"),
|
|
221
230
|
* service.enabled("nginx"),
|
|
222
231
|
* ], {
|
|
@@ -239,11 +248,14 @@ declare function recipe(name: string, modules: Module[], options?: {
|
|
|
239
248
|
* @throws {Error} When any required field is missing or empty.
|
|
240
249
|
*
|
|
241
250
|
* @example
|
|
251
|
+
* import { server } from "paratix"
|
|
252
|
+
* import { package as pkg } from "paratix/modules"
|
|
253
|
+
*
|
|
242
254
|
* export default server({
|
|
243
255
|
* name: "web-01",
|
|
244
256
|
* host: "10.0.0.1",
|
|
245
257
|
* ssh: { user: "root", ports: [22], privateKey: "~/.ssh/id_ed25519" }, // "~" is expanded
|
|
246
|
-
* run: [
|
|
258
|
+
* run: [pkg.installed("nginx")],
|
|
247
259
|
* });
|
|
248
260
|
*/
|
|
249
261
|
declare function server(config: ServerDefinition): ServerDefinition;
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
user,
|
|
62
62
|
validateHostLabel,
|
|
63
63
|
validateSshConfig
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-XIRORNO3.js";
|
|
65
65
|
|
|
66
66
|
// src/conditionalModules.ts
|
|
67
67
|
function createConditionalApplyState(environment) {
|
|
@@ -607,6 +607,9 @@ var MODULE_NAME_WIDTH = 56;
|
|
|
607
607
|
var MIN_MODULE_NAME_WIDTH = 12;
|
|
608
608
|
var OUTPUT_INDENT_UNIT = " ";
|
|
609
609
|
var SPINNER_FRAME_INTERVAL_MS = 80;
|
|
610
|
+
var ASCII_ESC = 27;
|
|
611
|
+
var ANSI_HIDE_CURSOR = `${String.fromCharCode(ASCII_ESC)}[?25l`;
|
|
612
|
+
var ANSI_SHOW_CURSOR = `${String.fromCharCode(ASCII_ESC)}[?25h`;
|
|
610
613
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
611
614
|
var STATUS_ICONS = {
|
|
612
615
|
changed: pc.yellow("\u21BA"),
|
|
@@ -615,10 +618,22 @@ var STATUS_ICONS = {
|
|
|
615
618
|
skipped: pc.dim("\u2298"),
|
|
616
619
|
waiting: pc.cyan("\u23F8")
|
|
617
620
|
};
|
|
618
|
-
var
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
621
|
+
var LIVE_OUTPUT_STATE_KEY = /* @__PURE__ */ Symbol.for("paratix.output.liveState");
|
|
622
|
+
function getSharedLiveOutputState() {
|
|
623
|
+
const registry = globalThis;
|
|
624
|
+
const existing = registry[LIVE_OUTPUT_STATE_KEY];
|
|
625
|
+
if (existing != null) return existing;
|
|
626
|
+
const created = {
|
|
627
|
+
activeRecipeGuideDepths: [],
|
|
628
|
+
activeSpinner: null,
|
|
629
|
+
cursorHidden: false,
|
|
630
|
+
pendingRecipeClosureGuideDepths: [],
|
|
631
|
+
recipeOutputDepth: -1
|
|
632
|
+
};
|
|
633
|
+
registry[LIVE_OUTPUT_STATE_KEY] = created;
|
|
634
|
+
return created;
|
|
635
|
+
}
|
|
636
|
+
var liveOutputState = getSharedLiveOutputState();
|
|
622
637
|
function supportsAnimatedModuleOutput() {
|
|
623
638
|
return process.stdout.isTTY && typeof process.stdout.clearLine === "function" && typeof process.stdout.cursorTo === "function";
|
|
624
639
|
}
|
|
@@ -645,7 +660,7 @@ function getModuleStatusText(status) {
|
|
|
645
660
|
}
|
|
646
661
|
}
|
|
647
662
|
function getCurrentOutputDepth() {
|
|
648
|
-
return Math.max(recipeOutputDepth, 0);
|
|
663
|
+
return Math.max(liveOutputState.recipeOutputDepth, 0);
|
|
649
664
|
}
|
|
650
665
|
function getGuideDot(depth) {
|
|
651
666
|
return depth % 2 === 0 ? pc.gray("\xB7") : pc.cyan("\xB7");
|
|
@@ -653,7 +668,7 @@ function getGuideDot(depth) {
|
|
|
653
668
|
function buildGuideIndent(baseIndent, options) {
|
|
654
669
|
const indentCharacters = Array.from(baseIndent);
|
|
655
670
|
const guideDepths = [
|
|
656
|
-
...options?.activeGuideDepths ?? activeRecipeGuideDepths,
|
|
671
|
+
...options?.activeGuideDepths ?? liveOutputState.activeRecipeGuideDepths,
|
|
657
672
|
...options?.extraGuideDepths ?? []
|
|
658
673
|
];
|
|
659
674
|
for (const guideDepth of guideDepths) {
|
|
@@ -664,16 +679,16 @@ function buildGuideIndent(baseIndent, options) {
|
|
|
664
679
|
return indentCharacters.join("");
|
|
665
680
|
}
|
|
666
681
|
function clearPendingRecipeClosureGuides() {
|
|
667
|
-
pendingRecipeClosureGuideDepths = [];
|
|
682
|
+
liveOutputState.pendingRecipeClosureGuideDepths = [];
|
|
668
683
|
}
|
|
669
684
|
function getModuleIndent() {
|
|
670
|
-
if (recipeOutputDepth < 0) {
|
|
685
|
+
if (liveOutputState.recipeOutputDepth < 0) {
|
|
671
686
|
return OUTPUT_INDENT_UNIT;
|
|
672
687
|
}
|
|
673
688
|
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2);
|
|
674
689
|
}
|
|
675
690
|
function getRecipeHeaderIndent() {
|
|
676
|
-
if (recipeOutputDepth < 0) return "";
|
|
691
|
+
if (liveOutputState.recipeOutputDepth < 0) return "";
|
|
677
692
|
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1);
|
|
678
693
|
}
|
|
679
694
|
function getErrorIndent() {
|
|
@@ -683,13 +698,13 @@ function getContinuationIndent() {
|
|
|
683
698
|
return `${getModuleIndent()} `;
|
|
684
699
|
}
|
|
685
700
|
async function withRecipeOutputScope(scopedOperation) {
|
|
686
|
-
recipeOutputDepth += 1;
|
|
701
|
+
liveOutputState.recipeOutputDepth += 1;
|
|
687
702
|
try {
|
|
688
703
|
return await scopedOperation();
|
|
689
704
|
} finally {
|
|
690
|
-
activeRecipeGuideDepths = activeRecipeGuideDepths.filter((depth) => depth !== recipeOutputDepth);
|
|
691
|
-
pendingRecipeClosureGuideDepths = [recipeOutputDepth];
|
|
692
|
-
recipeOutputDepth -= 1;
|
|
705
|
+
liveOutputState.activeRecipeGuideDepths = liveOutputState.activeRecipeGuideDepths.filter((depth) => depth !== liveOutputState.recipeOutputDepth);
|
|
706
|
+
liveOutputState.pendingRecipeClosureGuideDepths = [liveOutputState.recipeOutputDepth];
|
|
707
|
+
liveOutputState.recipeOutputDepth -= 1;
|
|
693
708
|
}
|
|
694
709
|
}
|
|
695
710
|
function renderModuleLine(parameters) {
|
|
@@ -702,15 +717,28 @@ function renderModuleLine(parameters) {
|
|
|
702
717
|
const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - baseIndent.length, MIN_MODULE_NAME_WIDTH);
|
|
703
718
|
return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`;
|
|
704
719
|
}
|
|
720
|
+
function hideCursor() {
|
|
721
|
+
if (liveOutputState.cursorHidden) return;
|
|
722
|
+
if (!supportsAnimatedModuleOutput()) return;
|
|
723
|
+
process.stdout.write(ANSI_HIDE_CURSOR);
|
|
724
|
+
liveOutputState.cursorHidden = true;
|
|
725
|
+
}
|
|
726
|
+
function showCursor() {
|
|
727
|
+
if (!liveOutputState.cursorHidden) return;
|
|
728
|
+
process.stdout.write(ANSI_SHOW_CURSOR);
|
|
729
|
+
liveOutputState.cursorHidden = false;
|
|
730
|
+
}
|
|
705
731
|
function writeAnimatedModuleLine(line) {
|
|
732
|
+
hideCursor();
|
|
706
733
|
process.stdout.clearLine(0);
|
|
707
734
|
process.stdout.cursorTo(0);
|
|
708
735
|
process.stdout.write(fitAnimatedModuleLine(line, process.stdout.columns));
|
|
709
736
|
}
|
|
710
737
|
function stopAnimatedModuleLine(clearCurrentLine = false) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
activeSpinner
|
|
738
|
+
showCursor();
|
|
739
|
+
if (liveOutputState.activeSpinner == null) return;
|
|
740
|
+
clearInterval(liveOutputState.activeSpinner.interval);
|
|
741
|
+
liveOutputState.activeSpinner = null;
|
|
714
742
|
if (clearCurrentLine && supportsAnimatedModuleOutput()) {
|
|
715
743
|
process.stdout.clearLine(0);
|
|
716
744
|
process.stdout.cursorTo(0);
|
|
@@ -745,7 +773,7 @@ function startModuleSpinner(name, detail) {
|
|
|
745
773
|
}, SPINNER_FRAME_INTERVAL_MS)
|
|
746
774
|
};
|
|
747
775
|
if (typeof spinner.interval.unref === "function") spinner.interval.unref();
|
|
748
|
-
activeSpinner = spinner;
|
|
776
|
+
liveOutputState.activeSpinner = spinner;
|
|
749
777
|
writeAnimatedModuleLine(
|
|
750
778
|
renderModuleLine({
|
|
751
779
|
detail: displayModule.detail,
|
|
@@ -760,8 +788,8 @@ function printRecipeHeader(name) {
|
|
|
760
788
|
clearPendingRecipeClosureGuides();
|
|
761
789
|
const header = pc.bold(pc.blue(`[${name}]`));
|
|
762
790
|
console.log(`${buildGuideIndent(getRecipeHeaderIndent())}${header}`);
|
|
763
|
-
if (recipeOutputDepth >= 0) {
|
|
764
|
-
activeRecipeGuideDepths = [...activeRecipeGuideDepths, recipeOutputDepth];
|
|
791
|
+
if (liveOutputState.recipeOutputDepth >= 0) {
|
|
792
|
+
liveOutputState.activeRecipeGuideDepths = [...liveOutputState.activeRecipeGuideDepths, liveOutputState.recipeOutputDepth];
|
|
765
793
|
}
|
|
766
794
|
}
|
|
767
795
|
function colorizeDiffLine(line) {
|
|
@@ -815,7 +843,7 @@ function printRenderedModuleResult(parameters) {
|
|
|
815
843
|
status: parameters.status
|
|
816
844
|
});
|
|
817
845
|
const diffLines = parameters.diff == null ? [] : renderDiffLines(parameters.diff);
|
|
818
|
-
const usesSpinner = supportsAnimatedModuleOutput() && activeSpinner != null;
|
|
846
|
+
const usesSpinner = supportsAnimatedModuleOutput() && liveOutputState.activeSpinner != null;
|
|
819
847
|
if (usesSpinner) {
|
|
820
848
|
stopAnimatedModuleLine();
|
|
821
849
|
writeAnimatedModuleLine(line);
|
|
@@ -838,7 +866,7 @@ function printRecipeModuleResult(name, status, detail, diff) {
|
|
|
838
866
|
printRenderedModuleResult({
|
|
839
867
|
detail,
|
|
840
868
|
diff,
|
|
841
|
-
extraGuideDepths: pendingRecipeClosureGuideDepths,
|
|
869
|
+
extraGuideDepths: liveOutputState.pendingRecipeClosureGuideDepths,
|
|
842
870
|
name,
|
|
843
871
|
status
|
|
844
872
|
});
|
|
@@ -1163,11 +1191,11 @@ async function runSignalModules(parameters) {
|
|
|
1163
1191
|
|
|
1164
1192
|
// src/recipe.ts
|
|
1165
1193
|
var INTERRUPTED_BEFORE_APPLY = /* @__PURE__ */ Symbol("recipe-interrupted-before-apply");
|
|
1166
|
-
function
|
|
1167
|
-
return module.
|
|
1194
|
+
function isRecipe(module) {
|
|
1195
|
+
return module.kind === "recipe";
|
|
1168
1196
|
}
|
|
1169
1197
|
function printRecipeChildResult(module, result) {
|
|
1170
|
-
if (
|
|
1198
|
+
if (isRecipe(module)) {
|
|
1171
1199
|
printRecipeModuleResult(module.name, result.status, result.detail);
|
|
1172
1200
|
return;
|
|
1173
1201
|
}
|
|
@@ -1234,7 +1262,7 @@ async function checkRecipeChild(targetModule, connection, currentEnvironment) {
|
|
|
1234
1262
|
return targetModule.check(connection, currentEnvironment);
|
|
1235
1263
|
}
|
|
1236
1264
|
async function applyRecipeChild(parameters) {
|
|
1237
|
-
if (
|
|
1265
|
+
if (isRecipe(parameters.targetModule)) {
|
|
1238
1266
|
return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment, {
|
|
1239
1267
|
onChildStep: parameters.onChildStep,
|
|
1240
1268
|
onSignalStep: parameters.onSignalStep,
|
|
@@ -1441,7 +1469,6 @@ function createRecipeDryRunApply(name, modules, needsDryRunApply) {
|
|
|
1441
1469
|
const result = await dryRunRecipeModule({
|
|
1442
1470
|
environment,
|
|
1443
1471
|
recipeModule: {
|
|
1444
|
-
_isRecipe: true,
|
|
1445
1472
|
_modules: modules,
|
|
1446
1473
|
async apply() {
|
|
1447
1474
|
await Promise.resolve();
|
|
@@ -1451,6 +1478,7 @@ function createRecipeDryRunApply(name, modules, needsDryRunApply) {
|
|
|
1451
1478
|
await Promise.resolve();
|
|
1452
1479
|
return "ok";
|
|
1453
1480
|
},
|
|
1481
|
+
kind: "recipe",
|
|
1454
1482
|
name
|
|
1455
1483
|
},
|
|
1456
1484
|
shutdownSignal: parameters?.shutdownSignal,
|
|
@@ -1470,7 +1498,6 @@ function recipe(name, modules, options) {
|
|
|
1470
1498
|
...modules.some((module) => module._dryRunBlocker === true) ? { _dryRunBlocker: true } : {},
|
|
1471
1499
|
...modules.some((module) => module._dryRunMetaProducer === true) ? { _dryRunMetaProducer: true } : {},
|
|
1472
1500
|
...applyDryRun == null ? {} : { _applyDryRun: applyDryRun },
|
|
1473
|
-
_isRecipe: true,
|
|
1474
1501
|
_modules: modules,
|
|
1475
1502
|
_signals: options?.signals,
|
|
1476
1503
|
_supportsChildStepHook: true,
|
|
@@ -1498,6 +1525,7 @@ function recipe(name, modules, options) {
|
|
|
1498
1525
|
}
|
|
1499
1526
|
return "ok";
|
|
1500
1527
|
},
|
|
1528
|
+
kind: "recipe",
|
|
1501
1529
|
name
|
|
1502
1530
|
};
|
|
1503
1531
|
}
|
|
@@ -1606,4 +1634,3 @@ export {
|
|
|
1606
1634
|
user,
|
|
1607
1635
|
when
|
|
1608
1636
|
};
|
|
1609
|
-
//# sourceMappingURL=index.js.map
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
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-
|
|
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-DwKdTyHo.js';
|
package/dist/modules/index.js
CHANGED
package/llm-guide.md
CHANGED
|
@@ -481,23 +481,23 @@ function myCustomModule(configPath: string, content: string): Module {
|
|
|
481
481
|
|
|
482
482
|
Methods available on the `ssh` parameter:
|
|
483
483
|
|
|
484
|
-
| Method
|
|
485
|
-
|
|
|
486
|
-
| `ssh.exec(cmd, options?)`
|
|
487
|
-
| `ssh.test(cmd)`
|
|
488
|
-
| `ssh.output(cmd)`
|
|
489
|
-
| `ssh.lines(cmd)`
|
|
490
|
-
| `ssh.exists(path)`
|
|
491
|
-
| `ssh.readFile(path)`
|
|
492
|
-
| `ssh.writeFile(path, content, { mode: "0644" })`
|
|
493
|
-
| `ssh.uploadFile(local, remote)`
|
|
494
|
-
| `ssh.downloadFile(remote, local)`
|
|
495
|
-
| `ssh.sha256(path)`
|
|
496
|
-
| `ssh.addPort(port)`
|
|
497
|
-
| `ssh.disconnect()`
|
|
498
|
-
| `ssh.getConnectionInfo()`
|
|
499
|
-
| `ssh.probeSudo()`
|
|
500
|
-
| `ssh.updateHost(host)`
|
|
484
|
+
| Method | Return type | Description |
|
|
485
|
+
| ------------------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
486
|
+
| `ssh.exec(cmd, options?)` | `Promise<ExecResult>` | Run command, get `{ code, stdout, stderr }`. Throws on non-zero unless `ignoreExitCode: true`. |
|
|
487
|
+
| `ssh.test(cmd)` | `Promise<boolean>` | Run command, return `true` if exit code is 0. |
|
|
488
|
+
| `ssh.output(cmd)` | `Promise<string>` | Run command, return trimmed stdout. |
|
|
489
|
+
| `ssh.lines(cmd)` | `Promise<string[]>` | Run command, return stdout split into lines. |
|
|
490
|
+
| `ssh.exists(path)` | `Promise<boolean>` | Check if remote path exists. |
|
|
491
|
+
| `ssh.readFile(path)` | `Promise<string>` | Read remote file content. |
|
|
492
|
+
| `ssh.writeFile(path, content, { mode: "0644" })` | `Promise<void>` | Write content to remote file. `mode` is optional and defaults to `"0600"`. |
|
|
493
|
+
| `ssh.uploadFile(local, remote, { mode: "0644" })` | `Promise<void>` | Upload local file via SFTP. `mode` is optional and defaults to `"0600"`. |
|
|
494
|
+
| `ssh.downloadFile(remote, local)` | `Promise<void>` | Download remote file. |
|
|
495
|
+
| `ssh.sha256(path)` | `Promise<string \| null>` | Get SHA-256 hex digest, or null if not found. |
|
|
496
|
+
| `ssh.addPort(port)` | `void` | Register an additional port opened on the remote host (advanced). |
|
|
497
|
+
| `ssh.disconnect()` | `void` | Close the SSH connection. |
|
|
498
|
+
| `ssh.getConnectionInfo()` | `{ host, port, privateKeyPath?, user }` | Return current connection parameters. |
|
|
499
|
+
| `ssh.probeSudo()` | `Promise<void>` | Probe/cache sudo access; prompts interactively if needed. |
|
|
500
|
+
| `ssh.updateHost(host)` | `void` | Update the target host address (e.g., after IP change). |
|
|
501
501
|
|
|
502
502
|
### ExecOptions
|
|
503
503
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paratix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Idempotent VPS setup tool in TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
"tsx": "^4.22.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@types/node": "24.
|
|
24
|
+
"@types/node": "24.13.2",
|
|
25
25
|
"@types/ssh2": "^1.15.4",
|
|
26
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
26
27
|
"tsup": "^8.4.0",
|
|
27
28
|
"typescript": "^6.0.3",
|
|
28
29
|
"vitest": "^4.1.7"
|
|
@@ -56,9 +57,10 @@
|
|
|
56
57
|
"scripts": {
|
|
57
58
|
"build": "tsup",
|
|
58
59
|
"test": "pnpm test:unit && pnpm test:dist",
|
|
60
|
+
"test:coverage": "vitest run --coverage",
|
|
59
61
|
"test:dist": "pnpm build && vitest run --config vitest.distribution.config.ts",
|
|
60
62
|
"test:integration": "pnpm build && vitest run --config vitest.integration.config.ts",
|
|
61
|
-
"test:unit": "vitest run",
|
|
63
|
+
"test:unit": "vitest run --coverage",
|
|
62
64
|
"test:watch": "vitest"
|
|
63
65
|
}
|
|
64
66
|
}
|