paratix 0.9.0 → 0.10.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/dist/{chunk-7Y2RBVG4.js → chunk-M7GETOJ5.js} +350 -4
- package/dist/chunk-M7GETOJ5.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/modules/index.d.ts +79 -2
- package/dist/modules/index.js +3 -1
- package/dist/{user-B9lkXr0X.d.ts → user-CJDqZC8n.d.ts} +16 -0
- package/llm-guide.md +43 -3
- package/package.json +1 -1
- package/dist/chunk-7Y2RBVG4.js.map +0 -1
|
@@ -1123,7 +1123,48 @@ function hasMarkedJob(lines, marker, cronJob) {
|
|
|
1123
1123
|
const index = lines.indexOf(marker);
|
|
1124
1124
|
return index !== -1 && index + 1 < lines.length && lines[index + 1] === cronJob;
|
|
1125
1125
|
}
|
|
1126
|
+
function assertCronName(name) {
|
|
1127
|
+
if (new RegExp("[\\n\\r]", "v").test(name)) {
|
|
1128
|
+
throw new Error(`cron: name must not contain newlines: ${JSON.stringify(name)}`);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1126
1131
|
var cron = {
|
|
1132
|
+
/**
|
|
1133
|
+
* Ensure a cron job is absent from a user's crontab.
|
|
1134
|
+
*
|
|
1135
|
+
* Removes the `# paratix: <name>` marker comment and the crontab line
|
|
1136
|
+
* directly below it. If the marker is not found, the module reports `ok`
|
|
1137
|
+
* without writing the crontab. When the crontab becomes empty after the
|
|
1138
|
+
* removal, it is deleted entirely via `crontab -r`.
|
|
1139
|
+
*
|
|
1140
|
+
* Equivalent to `cron.job(user, name, { job: "<unused>", state: "absent" })`,
|
|
1141
|
+
* but does not require a placeholder `job` argument.
|
|
1142
|
+
*
|
|
1143
|
+
* @param user - The target user whose crontab is managed.
|
|
1144
|
+
* @param name - Unique identifier of the cron job marker to remove.
|
|
1145
|
+
* @returns A Module that ensures the cron job entry is absent.
|
|
1146
|
+
*/
|
|
1147
|
+
absent(user2, name) {
|
|
1148
|
+
assertCronName(name);
|
|
1149
|
+
const marker = `# paratix: ${name}`;
|
|
1150
|
+
return {
|
|
1151
|
+
async apply(ssh2) {
|
|
1152
|
+
if (!ssh2) return failed(`[cron.absent: ${name} (${user2})] SSH connection is required`);
|
|
1153
|
+
const lines = await readCrontab(ssh2, user2);
|
|
1154
|
+
const markerIndex = lines.indexOf(marker);
|
|
1155
|
+
if (markerIndex === -1) return { status: "ok" };
|
|
1156
|
+
lines.splice(markerIndex, 2);
|
|
1157
|
+
await writeCrontab(ssh2, user2, lines);
|
|
1158
|
+
return { status: "changed" };
|
|
1159
|
+
},
|
|
1160
|
+
async check(ssh2) {
|
|
1161
|
+
if (!ssh2) return NEEDS_APPLY;
|
|
1162
|
+
const lines = await readCrontab(ssh2, user2);
|
|
1163
|
+
return lines.includes(marker) ? NEEDS_APPLY : "ok";
|
|
1164
|
+
},
|
|
1165
|
+
name: `cron.absent: ${name} (${user2})`
|
|
1166
|
+
};
|
|
1167
|
+
},
|
|
1127
1168
|
/**
|
|
1128
1169
|
* Ensure a cron job is present in (or absent from) a user's crontab.
|
|
1129
1170
|
*
|
|
@@ -1140,9 +1181,7 @@ var cron = {
|
|
|
1140
1181
|
* @returns A Module that manages the cron job entry.
|
|
1141
1182
|
*/
|
|
1142
1183
|
job(user2, name, options) {
|
|
1143
|
-
|
|
1144
|
-
throw new Error(`cron.job: name must not contain newlines: ${JSON.stringify(name)}`);
|
|
1145
|
-
}
|
|
1184
|
+
assertCronName(name);
|
|
1146
1185
|
if (new RegExp("[\\n\\r]", "v").test(options.job)) {
|
|
1147
1186
|
throw new Error(`cron.job: job must not contain newlines: ${JSON.stringify(options.job)}`);
|
|
1148
1187
|
}
|
|
@@ -5621,6 +5660,312 @@ var systemd = {
|
|
|
5621
5660
|
}
|
|
5622
5661
|
};
|
|
5623
5662
|
|
|
5663
|
+
// src/modules/timerHelpers.ts
|
|
5664
|
+
var SYSTEMD_DIRECTORY = "/etc/systemd/system";
|
|
5665
|
+
var TIMER_NAME_PATTERN = new RegExp("^[A-Za-z0-9_\\-]+$", "v");
|
|
5666
|
+
var ENVIRONMENT_KEY_PATTERN = new RegExp("^[A-Za-z_]\\w*$", "v");
|
|
5667
|
+
var ENVIRONMENT_VALUE_NEEDS_QUOTING = new RegExp('[\\s"\\\\]', "v");
|
|
5668
|
+
function assertNoNewline(field, value) {
|
|
5669
|
+
if (new RegExp("[\\n\\r]", "v").test(value)) {
|
|
5670
|
+
throw new Error(`timer.scheduled: ${field} must not contain newlines: ${JSON.stringify(value)}`);
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
function assertNotBlank(field, value) {
|
|
5674
|
+
if (value.trim().length === 0) {
|
|
5675
|
+
throw new Error(`timer.scheduled: ${field} must not be empty: ${JSON.stringify(value)}`);
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
function normalizeOnCalendar(onCalendar) {
|
|
5679
|
+
const entries = Array.isArray(onCalendar) ? onCalendar : [onCalendar];
|
|
5680
|
+
if (entries.length === 0) {
|
|
5681
|
+
throw new Error("timer.scheduled: onCalendar must contain at least one entry");
|
|
5682
|
+
}
|
|
5683
|
+
for (const entry of entries) {
|
|
5684
|
+
assertNoNewline("onCalendar entry", entry);
|
|
5685
|
+
if (entry.trim().length === 0) {
|
|
5686
|
+
throw new Error("timer.scheduled: onCalendar entries must not be empty");
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
return entries;
|
|
5690
|
+
}
|
|
5691
|
+
function quoteEnvironmentValue(value) {
|
|
5692
|
+
if (!ENVIRONMENT_VALUE_NEEDS_QUOTING.test(value)) return value;
|
|
5693
|
+
const escaped = value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
5694
|
+
return `"${escaped}"`;
|
|
5695
|
+
}
|
|
5696
|
+
function renderServiceUnit(name, options) {
|
|
5697
|
+
const description = options.description ?? `Paratix scheduled task: ${name}`;
|
|
5698
|
+
const lines = ["[Unit]", `Description=${description}`, "", "[Service]", "Type=oneshot"];
|
|
5699
|
+
if (options.user != null) lines.push(`User=${options.user}`);
|
|
5700
|
+
if (options.group != null) lines.push(`Group=${options.group}`);
|
|
5701
|
+
if (options.workingDirectory != null) {
|
|
5702
|
+
lines.push(`WorkingDirectory=${options.workingDirectory}`);
|
|
5703
|
+
}
|
|
5704
|
+
if (options.environment) {
|
|
5705
|
+
for (const [key, value] of Object.entries(options.environment)) {
|
|
5706
|
+
lines.push(`Environment=${key}=${quoteEnvironmentValue(value)}`);
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
lines.push(`ExecStart=${options.exec}`);
|
|
5710
|
+
return `${lines.join("\n")}
|
|
5711
|
+
`;
|
|
5712
|
+
}
|
|
5713
|
+
function renderTimerUnit(name, options) {
|
|
5714
|
+
const description = options.description ?? `Paratix scheduled task: ${name}`;
|
|
5715
|
+
const persistent = options.persistent ?? true;
|
|
5716
|
+
const lines = ["[Unit]", `Description=${description} (timer)`, "", "[Timer]"];
|
|
5717
|
+
for (const entry of normalizeOnCalendar(options.onCalendar)) {
|
|
5718
|
+
lines.push(`OnCalendar=${entry}`);
|
|
5719
|
+
}
|
|
5720
|
+
if (persistent) lines.push("Persistent=true");
|
|
5721
|
+
if (options.randomizedDelaySec != null) {
|
|
5722
|
+
lines.push(`RandomizedDelaySec=${String(options.randomizedDelaySec)}`);
|
|
5723
|
+
}
|
|
5724
|
+
if (options.accuracySec != null) lines.push(`AccuracySec=${String(options.accuracySec)}`);
|
|
5725
|
+
lines.push(`Unit=${name}.service`, "", "[Install]", "WantedBy=timers.target");
|
|
5726
|
+
return `${lines.join("\n")}
|
|
5727
|
+
`;
|
|
5728
|
+
}
|
|
5729
|
+
function validateEnvironment(environment) {
|
|
5730
|
+
for (const [key, value] of Object.entries(environment)) {
|
|
5731
|
+
if (!ENVIRONMENT_KEY_PATTERN.test(key)) {
|
|
5732
|
+
throw new Error(
|
|
5733
|
+
`timer.scheduled: environment key must match ${String(ENVIRONMENT_KEY_PATTERN)}, got: ${JSON.stringify(key)}`
|
|
5734
|
+
);
|
|
5735
|
+
}
|
|
5736
|
+
assertNoNewline(`environment[${key}]`, value);
|
|
5737
|
+
}
|
|
5738
|
+
}
|
|
5739
|
+
function validatePresentOptions(options) {
|
|
5740
|
+
assertNoNewline("exec", options.exec);
|
|
5741
|
+
assertNotBlank("exec", options.exec);
|
|
5742
|
+
const optionalStringFields = [
|
|
5743
|
+
["description", options.description],
|
|
5744
|
+
["user", options.user],
|
|
5745
|
+
["group", options.group],
|
|
5746
|
+
["workingDirectory", options.workingDirectory]
|
|
5747
|
+
];
|
|
5748
|
+
for (const [field, value] of optionalStringFields) {
|
|
5749
|
+
if (value != null) {
|
|
5750
|
+
assertNoNewline(field, value);
|
|
5751
|
+
assertNotBlank(field, value);
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
if (options.randomizedDelaySec != null) {
|
|
5755
|
+
assertNoNewline("randomizedDelaySec", String(options.randomizedDelaySec));
|
|
5756
|
+
}
|
|
5757
|
+
if (options.accuracySec != null) {
|
|
5758
|
+
assertNoNewline("accuracySec", String(options.accuracySec));
|
|
5759
|
+
}
|
|
5760
|
+
if (options.environment) validateEnvironment(options.environment);
|
|
5761
|
+
normalizeOnCalendar(options.onCalendar);
|
|
5762
|
+
}
|
|
5763
|
+
function assertTimerName(name) {
|
|
5764
|
+
if (!TIMER_NAME_PATTERN.test(name)) {
|
|
5765
|
+
throw new Error(
|
|
5766
|
+
`timer: name must match ${String(TIMER_NAME_PATTERN)}, got: ${JSON.stringify(name)}`
|
|
5767
|
+
);
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
5770
|
+
function buildTimerLocations(name) {
|
|
5771
|
+
return {
|
|
5772
|
+
servicePath: `${SYSTEMD_DIRECTORY}/${name}.service`,
|
|
5773
|
+
timerPath: `${SYSTEMD_DIRECTORY}/${name}.timer`,
|
|
5774
|
+
timerUnit: `${name}.timer`
|
|
5775
|
+
};
|
|
5776
|
+
}
|
|
5777
|
+
function buildTimerPaths(name, options) {
|
|
5778
|
+
return {
|
|
5779
|
+
...buildTimerLocations(name),
|
|
5780
|
+
serviceContent: renderServiceUnit(name, options),
|
|
5781
|
+
timerContent: renderTimerUnit(name, options)
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5785
|
+
// src/modules/timer.ts
|
|
5786
|
+
var SYSTEMCTL5 = "systemctl";
|
|
5787
|
+
var UNIT_FILE_MODE = "0644";
|
|
5788
|
+
async function fileMatches(ssh2, path, expected) {
|
|
5789
|
+
if (!await ssh2.exists(path)) return false;
|
|
5790
|
+
const remote = await ssh2.readFile(path);
|
|
5791
|
+
return remote.trim() === expected.trim();
|
|
5792
|
+
}
|
|
5793
|
+
async function checkPresent2(ssh2, paths) {
|
|
5794
|
+
if (!await fileMatches(ssh2, paths.servicePath, paths.serviceContent)) return NEEDS_APPLY;
|
|
5795
|
+
if (!await fileMatches(ssh2, paths.timerPath, paths.timerContent)) return NEEDS_APPLY;
|
|
5796
|
+
const enabled = await ssh2.test(`${SYSTEMCTL5} is-enabled --quiet ${shellQuote(paths.timerUnit)}`);
|
|
5797
|
+
if (!enabled) return NEEDS_APPLY;
|
|
5798
|
+
const active = await ssh2.test(`${SYSTEMCTL5} is-active --quiet ${shellQuote(paths.timerUnit)}`);
|
|
5799
|
+
return active ? "ok" : NEEDS_APPLY;
|
|
5800
|
+
}
|
|
5801
|
+
async function checkAbsent2(ssh2, locations) {
|
|
5802
|
+
if (await ssh2.exists(locations.servicePath)) return NEEDS_APPLY;
|
|
5803
|
+
if (await ssh2.exists(locations.timerPath)) return NEEDS_APPLY;
|
|
5804
|
+
return "ok";
|
|
5805
|
+
}
|
|
5806
|
+
async function syncUnitFiles(ssh2, name, paths) {
|
|
5807
|
+
const serviceMatched = await fileMatches(ssh2, paths.servicePath, paths.serviceContent);
|
|
5808
|
+
const timerMatched = await fileMatches(ssh2, paths.timerPath, paths.timerContent);
|
|
5809
|
+
if (!serviceMatched) {
|
|
5810
|
+
await ssh2.writeFile(paths.servicePath, paths.serviceContent, { mode: UNIT_FILE_MODE });
|
|
5811
|
+
}
|
|
5812
|
+
if (!timerMatched) {
|
|
5813
|
+
await ssh2.writeFile(paths.timerPath, paths.timerContent, { mode: UNIT_FILE_MODE });
|
|
5814
|
+
}
|
|
5815
|
+
if (!serviceMatched || !timerMatched) {
|
|
5816
|
+
const reload = await ssh2.exec(`${SYSTEMCTL5} daemon-reload`, {
|
|
5817
|
+
ignoreExitCode: true,
|
|
5818
|
+
silent: true
|
|
5819
|
+
});
|
|
5820
|
+
if (reload.code !== 0) {
|
|
5821
|
+
return {
|
|
5822
|
+
failure: failedCommand(`[timer.scheduled: ${name}] systemctl daemon-reload failed`, reload),
|
|
5823
|
+
ok: false
|
|
5824
|
+
};
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
return { ok: true, serviceMatched, timerMatched };
|
|
5828
|
+
}
|
|
5829
|
+
async function isTimerFullyActive(ssh2, timerUnit) {
|
|
5830
|
+
const enabled = await ssh2.test(`${SYSTEMCTL5} is-enabled --quiet ${shellQuote(timerUnit)}`);
|
|
5831
|
+
if (!enabled) return false;
|
|
5832
|
+
return ssh2.test(`${SYSTEMCTL5} is-active --quiet ${shellQuote(timerUnit)}`);
|
|
5833
|
+
}
|
|
5834
|
+
async function applyPresent(ssh2, name, paths) {
|
|
5835
|
+
const sync = await syncUnitFiles(ssh2, name, paths);
|
|
5836
|
+
if (!sync.ok) return sync.failure;
|
|
5837
|
+
if (sync.serviceMatched && sync.timerMatched && await isTimerFullyActive(ssh2, paths.timerUnit)) {
|
|
5838
|
+
return { status: "ok" };
|
|
5839
|
+
}
|
|
5840
|
+
const enable = await ssh2.exec(`${SYSTEMCTL5} enable --now ${shellQuote(paths.timerUnit)}`, {
|
|
5841
|
+
ignoreExitCode: true,
|
|
5842
|
+
silent: true
|
|
5843
|
+
});
|
|
5844
|
+
if (enable.code !== 0) {
|
|
5845
|
+
return failedCommand(`[timer.scheduled: ${name}] systemctl enable --now failed`, enable);
|
|
5846
|
+
}
|
|
5847
|
+
if (!sync.timerMatched) {
|
|
5848
|
+
const restart = await ssh2.exec(`${SYSTEMCTL5} restart ${shellQuote(paths.timerUnit)}`, {
|
|
5849
|
+
ignoreExitCode: true,
|
|
5850
|
+
silent: true
|
|
5851
|
+
});
|
|
5852
|
+
if (restart.code !== 0) {
|
|
5853
|
+
return failedCommand(`[timer.scheduled: ${name}] systemctl restart failed`, restart);
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
return { status: "changed" };
|
|
5857
|
+
}
|
|
5858
|
+
async function applyAbsent(ssh2, context) {
|
|
5859
|
+
const { locations, module, name } = context;
|
|
5860
|
+
const serviceExists = await ssh2.exists(locations.servicePath);
|
|
5861
|
+
const timerExists = await ssh2.exists(locations.timerPath);
|
|
5862
|
+
if (!serviceExists && !timerExists) return { status: "ok" };
|
|
5863
|
+
await ssh2.exec(`${SYSTEMCTL5} disable --now ${shellQuote(locations.timerUnit)}`, {
|
|
5864
|
+
ignoreExitCode: true,
|
|
5865
|
+
silent: true
|
|
5866
|
+
});
|
|
5867
|
+
const remove = await ssh2.exec(
|
|
5868
|
+
`rm -f ${shellQuote(locations.timerPath)} ${shellQuote(locations.servicePath)}`,
|
|
5869
|
+
{ ignoreExitCode: true, silent: true }
|
|
5870
|
+
);
|
|
5871
|
+
if (remove.code !== 0) {
|
|
5872
|
+
return failedCommand(`[${module}: ${name}] failed to remove unit files`, remove);
|
|
5873
|
+
}
|
|
5874
|
+
const reload = await ssh2.exec(`${SYSTEMCTL5} daemon-reload`, {
|
|
5875
|
+
ignoreExitCode: true,
|
|
5876
|
+
silent: true
|
|
5877
|
+
});
|
|
5878
|
+
if (reload.code !== 0) {
|
|
5879
|
+
return failedCommand(`[${module}: ${name}] systemctl daemon-reload failed`, reload);
|
|
5880
|
+
}
|
|
5881
|
+
return { status: "changed" };
|
|
5882
|
+
}
|
|
5883
|
+
var timer = {
|
|
5884
|
+
/**
|
|
5885
|
+
* Ensure a systemd timer-driven scheduled task does not exist.
|
|
5886
|
+
*
|
|
5887
|
+
* Disables and stops `<name>.timer`, removes both `<name>.service` and
|
|
5888
|
+
* `<name>.timer` from `/etc/systemd/system/`, and reloads systemd. The
|
|
5889
|
+
* `disable --now` step also removes the timer's `wants/` symlink. Failure
|
|
5890
|
+
* to disable a unit that does not exist is ignored, so this method is
|
|
5891
|
+
* safe to apply repeatedly.
|
|
5892
|
+
*
|
|
5893
|
+
* Behaves like `timer.scheduled(name, { exec: "<unused>", onCalendar: "<unused>", state: "absent" })`,
|
|
5894
|
+
* but does not require placeholder values for `exec` or `onCalendar` and
|
|
5895
|
+
* reports failures with a `timer.absent` prefix instead of `timer.scheduled`.
|
|
5896
|
+
*
|
|
5897
|
+
* @param name - Base unit name without extension. Must match
|
|
5898
|
+
* `^[A-Za-z0-9_\-]+$`.
|
|
5899
|
+
* @returns A Module that ensures the timer-driven scheduled task is absent.
|
|
5900
|
+
*/
|
|
5901
|
+
absent(name) {
|
|
5902
|
+
assertTimerName(name);
|
|
5903
|
+
const locations = buildTimerLocations(name);
|
|
5904
|
+
return {
|
|
5905
|
+
async apply(ssh2) {
|
|
5906
|
+
if (!ssh2) return failed(`[timer.absent: ${name}] SSH connection is required`);
|
|
5907
|
+
return applyAbsent(ssh2, { locations, module: "timer.absent", name });
|
|
5908
|
+
},
|
|
5909
|
+
async check(ssh2) {
|
|
5910
|
+
if (!ssh2) return NEEDS_APPLY;
|
|
5911
|
+
return checkAbsent2(ssh2, locations);
|
|
5912
|
+
},
|
|
5913
|
+
name: `timer.absent: ${name}`
|
|
5914
|
+
};
|
|
5915
|
+
},
|
|
5916
|
+
/**
|
|
5917
|
+
* Ensure a systemd timer-driven scheduled task is present (or absent).
|
|
5918
|
+
*
|
|
5919
|
+
* Generates `<name>.service` (`Type=oneshot`, no `[Install]` section since
|
|
5920
|
+
* it is triggered exclusively by the timer) and `<name>.timer` under
|
|
5921
|
+
* `/etc/systemd/system/`, reloads systemd, and runs
|
|
5922
|
+
* `systemctl enable --now <name>.timer`. When the timer unit content
|
|
5923
|
+
* changed, the timer is restarted so a new `OnCalendar=` schedule is picked
|
|
5924
|
+
* up immediately. With `state: "absent"`, the timer is disabled and
|
|
5925
|
+
* stopped, both unit files are removed, and systemd is reloaded.
|
|
5926
|
+
*
|
|
5927
|
+
* Validation of `exec`, `description`, `user`, `group`, `workingDirectory`,
|
|
5928
|
+
* `environment` and `onCalendar` only runs when `state` is `"present"`,
|
|
5929
|
+
* so callers can pass placeholder values when removing a timer.
|
|
5930
|
+
*
|
|
5931
|
+
* @param name - Base unit name without extension. Used as `<name>.service`
|
|
5932
|
+
* and `<name>.timer`. Must match `^[A-Za-z0-9_\-]+$`.
|
|
5933
|
+
* @param options - Schedule, command, optional service hardening, and state.
|
|
5934
|
+
* @returns A Module that manages the timer-driven scheduled task.
|
|
5935
|
+
*/
|
|
5936
|
+
scheduled(name, options) {
|
|
5937
|
+
assertTimerName(name);
|
|
5938
|
+
const state = options.state ?? "present";
|
|
5939
|
+
if (state === "absent") {
|
|
5940
|
+
const locations = buildTimerLocations(name);
|
|
5941
|
+
return {
|
|
5942
|
+
async apply(ssh2) {
|
|
5943
|
+
if (!ssh2) return failed(`[timer.scheduled: ${name}] SSH connection is required`);
|
|
5944
|
+
return applyAbsent(ssh2, { locations, module: "timer.scheduled", name });
|
|
5945
|
+
},
|
|
5946
|
+
async check(ssh2) {
|
|
5947
|
+
if (!ssh2) return NEEDS_APPLY;
|
|
5948
|
+
return checkAbsent2(ssh2, locations);
|
|
5949
|
+
},
|
|
5950
|
+
name: `timer.scheduled: ${name}`
|
|
5951
|
+
};
|
|
5952
|
+
}
|
|
5953
|
+
validatePresentOptions(options);
|
|
5954
|
+
const paths = buildTimerPaths(name, options);
|
|
5955
|
+
return {
|
|
5956
|
+
async apply(ssh2) {
|
|
5957
|
+
if (!ssh2) return failed(`[timer.scheduled: ${name}] SSH connection is required`);
|
|
5958
|
+
return applyPresent(ssh2, name, paths);
|
|
5959
|
+
},
|
|
5960
|
+
async check(ssh2) {
|
|
5961
|
+
if (!ssh2) return NEEDS_APPLY;
|
|
5962
|
+
return checkPresent2(ssh2, paths);
|
|
5963
|
+
},
|
|
5964
|
+
name: `timer.scheduled: ${name}`
|
|
5965
|
+
};
|
|
5966
|
+
}
|
|
5967
|
+
};
|
|
5968
|
+
|
|
5624
5969
|
// src/modules/ufw.ts
|
|
5625
5970
|
var UFW = "ufw";
|
|
5626
5971
|
var ufw = {
|
|
@@ -5885,7 +6230,8 @@ export {
|
|
|
5885
6230
|
swap,
|
|
5886
6231
|
system,
|
|
5887
6232
|
systemd,
|
|
6233
|
+
timer,
|
|
5888
6234
|
ufw,
|
|
5889
6235
|
user
|
|
5890
6236
|
};
|
|
5891
|
-
//# sourceMappingURL=chunk-
|
|
6237
|
+
//# sourceMappingURL=chunk-M7GETOJ5.js.map
|