paratix 0.8.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.
@@ -1,5 +1,108 @@
1
1
  import { M as Module } from './types-Cl2Muw1x.js';
2
2
 
3
+ /** Per-call overrides for package operations that can take a long time. */
4
+ type UpgradeOptions = {
5
+ /** Override the SSH layer's command timeout (milliseconds). */
6
+ timeout?: number;
7
+ };
8
+ /**
9
+ * Distro-agnostic package management module.
10
+ *
11
+ * Automatically detects the system package manager (apt, dnf, yum, apk)
12
+ * and delegates to the appropriate commands. All methods are idempotent.
13
+ *
14
+ * For Debian/Ubuntu-specific configuration (debconf pre-seeding, GPG keys,
15
+ * apt repositories) use the `apt` module instead.
16
+ *
17
+ * @example
18
+ * // Install packages
19
+ * pkg.installed("git", "curl")
20
+ *
21
+ * @example
22
+ * // Refresh package lists and upgrade all packages on a specific date
23
+ * pkg.update("2024-01-15")
24
+ * pkg.upgrade("2024-01-15")
25
+ */
26
+ declare const pkg: {
27
+ /**
28
+ * Ensure the given packages are not installed.
29
+ *
30
+ * The check phase queries the package database for each package individually;
31
+ * the remove command is only executed when at least one package is present.
32
+ *
33
+ * Pass an `UpgradeOptions` object as the last argument to override the SSH
34
+ * timeout for slow remove operations.
35
+ *
36
+ * @param packagesAndOptions - One or more package names, optionally followed
37
+ * by an `UpgradeOptions` object as the last argument.
38
+ * @returns A Module that removes the packages if any are present.
39
+ *
40
+ * @example
41
+ * pkg.absent("vim", "nano")
42
+ * pkg.absent("vim", "nano", { timeout: 600_000 })
43
+ */
44
+ absent(...packagesAndOptions: Array<string | UpgradeOptions>): Module;
45
+ /**
46
+ * Ensure the given packages are installed.
47
+ *
48
+ * The check phase queries the package database for each package individually;
49
+ * the install command is only executed when at least one package is missing.
50
+ *
51
+ * Pass an `UpgradeOptions` object as the last argument to override the SSH
52
+ * timeout for slow install operations.
53
+ *
54
+ * @param packagesAndOptions - One or more package names, optionally followed
55
+ * by an `UpgradeOptions` object as the last argument.
56
+ * @returns A Module that installs missing packages.
57
+ *
58
+ * @example
59
+ * pkg.installed("git", "curl", "unzip")
60
+ * pkg.installed("texlive-full", { timeout: 900_000 })
61
+ */
62
+ installed(...packagesAndOptions: Array<string | UpgradeOptions>): Module;
63
+ /**
64
+ * Refresh the package manager's package lists once per dated flag.
65
+ *
66
+ * A flag file at `${FLAGS_DIRECTORY}/package-update-<date>` is created after
67
+ * a successful run. On the next run the flag is detected and the module
68
+ * reports `"ok"` without running the update again. Changing `date` to a new
69
+ * value invalidates all previous flags for this operation.
70
+ *
71
+ * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
72
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
73
+ * @returns A Module that refreshes package lists.
74
+ *
75
+ * @example
76
+ * pkg.update("2024-01-15")
77
+ * pkg.update("2024-01-15", { timeout: 600_000 })
78
+ */
79
+ update(date: string, options?: UpgradeOptions): Module;
80
+ /**
81
+ * Upgrade all installed packages once per dated flag.
82
+ *
83
+ * A flag file at `${FLAGS_DIRECTORY}/package-upgrade-<date>` is created after
84
+ * a successful run. On the next run the flag is detected and the module
85
+ * reports `"ok"` without running the upgrade again. Changing `date` to a new
86
+ * value invalidates all previous flags for this operation.
87
+ *
88
+ * On apt systems this runs `dpkg --configure -a`, `apt-get update`, and
89
+ * `apt-get upgrade -y` as three separate commands (each subject to its own
90
+ * SSH `timeout`). For full dependency resolution use `apt.distUpgrade`.
91
+ *
92
+ * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
93
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
94
+ * The same `timeout` is applied to every step of the upgrade pipeline.
95
+ * @returns A Module that upgrades all packages.
96
+ *
97
+ * @example
98
+ * pkg.upgrade("2024-01-15")
99
+ * pkg.upgrade("2024-01-15", { timeout: 900_000 })
100
+ *
101
+ * @see apt.distUpgrade
102
+ */
103
+ upgrade(date: string, options?: UpgradeOptions): Module;
104
+ };
105
+
3
106
  /**
4
107
  * Modules for Debian/Ubuntu-specific apt configuration.
5
108
  *
@@ -35,10 +138,19 @@ declare const apt: {
35
138
  * Run `apt-get update && apt-get dist-upgrade` once per dated flag.
36
139
  * Performs a full distribution upgrade with dependency resolution.
37
140
  *
141
+ * The pipeline is split into three separate SSH commands so that each step
142
+ * gets its own timeout window and produces a precise failure label.
143
+ *
38
144
  * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
145
+ * @param options - Optional per-call overrides (e.g. SSH command `timeout`).
146
+ * The same `timeout` is applied to every step of the dist-upgrade pipeline.
39
147
  * @returns A Module that performs the dist-upgrade.
148
+ *
149
+ * @example
150
+ * apt.distUpgrade("2024-01-15")
151
+ * apt.distUpgrade("2024-01-15", { timeout: 900_000 })
40
152
  */
41
- distUpgrade(date: string): Module;
153
+ distUpgrade(date: string, options?: UpgradeOptions): Module;
42
154
  /**
43
155
  * Import a GPG key into `/etc/apt/keyrings/` for use with signed repositories.
44
156
  * @param name - Key file name (without `.gpg` extension).
@@ -270,6 +382,22 @@ type CronJobOptions = {
270
382
  * idempotent and safely repeatable.
271
383
  */
272
384
  declare const cron: {
385
+ /**
386
+ * Ensure a cron job is absent from a user's crontab.
387
+ *
388
+ * Removes the `# paratix: <name>` marker comment and the crontab line
389
+ * directly below it. If the marker is not found, the module reports `ok`
390
+ * without writing the crontab. When the crontab becomes empty after the
391
+ * removal, it is deleted entirely via `crontab -r`.
392
+ *
393
+ * Equivalent to `cron.job(user, name, { job: "<unused>", state: "absent" })`,
394
+ * but does not require a placeholder `job` argument.
395
+ *
396
+ * @param user - The target user whose crontab is managed.
397
+ * @param name - Unique identifier of the cron job marker to remove.
398
+ * @returns A Module that ensures the cron job entry is absent.
399
+ */
400
+ absent(user: string, name: string): Module;
273
401
  /**
274
402
  * Ensure a cron job is present in (or absent from) a user's crontab.
275
403
  *
@@ -811,88 +939,6 @@ declare const op: {
811
939
  resolve(references: Record<string, string>): Module;
812
940
  };
813
941
 
814
- /**
815
- * Distro-agnostic package management module.
816
- *
817
- * Automatically detects the system package manager (apt, dnf, yum, apk)
818
- * and delegates to the appropriate commands. All methods are idempotent.
819
- *
820
- * For Debian/Ubuntu-specific configuration (debconf pre-seeding, GPG keys,
821
- * apt repositories) use the `apt` module instead.
822
- *
823
- * @example
824
- * // Install packages
825
- * pkg.installed("git", "curl")
826
- *
827
- * @example
828
- * // Refresh package lists and upgrade all packages on a specific date
829
- * pkg.update("2024-01-15")
830
- * pkg.upgrade("2024-01-15")
831
- */
832
- declare const pkg: {
833
- /**
834
- * Ensure the given packages are not installed.
835
- *
836
- * The check phase queries the package database for each package individually;
837
- * the remove command is only executed when at least one package is present.
838
- *
839
- * @param packages - One or more package names to remove.
840
- * @returns A Module that removes the packages if any are present.
841
- *
842
- * @example
843
- * pkg.absent("vim", "nano")
844
- */
845
- absent(...packages: string[]): Module;
846
- /**
847
- * Ensure the given packages are installed.
848
- *
849
- * The check phase queries the package database for each package individually;
850
- * the install command is only executed when at least one package is missing.
851
- *
852
- * @param packages - One or more package names to install.
853
- * @returns A Module that installs missing packages.
854
- *
855
- * @example
856
- * pkg.installed("git", "curl", "unzip")
857
- */
858
- installed(...packages: string[]): Module;
859
- /**
860
- * Refresh the package manager's package lists once per dated flag.
861
- *
862
- * A flag file at `${FLAGS_DIRECTORY}/package-update-<date>` is created after
863
- * a successful run. On the next run the flag is detected and the module
864
- * reports `"ok"` without running the update again. Changing `date` to a new
865
- * value invalidates all previous flags for this operation.
866
- *
867
- * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
868
- * @returns A Module that refreshes package lists.
869
- *
870
- * @example
871
- * pkg.update("2024-01-15")
872
- */
873
- update(date: string): Module;
874
- /**
875
- * Upgrade all installed packages once per dated flag.
876
- *
877
- * A flag file at `${FLAGS_DIRECTORY}/package-upgrade-<date>` is created after
878
- * a successful run. On the next run the flag is detected and the module
879
- * reports `"ok"` without running the upgrade again. Changing `date` to a new
880
- * value invalidates all previous flags for this operation.
881
- *
882
- * On apt systems this runs `apt-get update && apt-get upgrade -y` (not
883
- * `dist-upgrade`); use `apt.distUpgrade` for full dependency resolution.
884
- *
885
- * @param date - A date string used as the idempotency key (e.g. `"2024-01-15"`).
886
- * @returns A Module that upgrades all packages.
887
- *
888
- * @example
889
- * pkg.upgrade("2024-01-15")
890
- *
891
- * @see apt.distUpgrade
892
- */
893
- upgrade(date: string): Module;
894
- };
895
-
896
942
  type QuadletAutoUpdate = "local" | "registry";
897
943
  type QuadletHealthOnFailure = "kill" | "none" | "restart" | "stop";
898
944
  type QuadletPullPolicy = "always" | "missing" | "never" | "newer";
@@ -1440,4 +1486,4 @@ declare const user: {
1440
1486
  present(name: string, options?: UserOptions): Module;
1441
1487
  };
1442
1488
 
1443
- export { apt as a, archive as b, command as c, compose as d, cron as e, download as f, file as g, git as h, group as i, hostname as j, rsync as k, service as l, mount as m, net as n, op as o, pkg as p, quadlet as q, releaseUpgrade as r, script as s, ssh as t, sshd as u, sysctl as v, system as w, systemd as x, ufw as y, user as z };
1489
+ export { type UpgradeOptions as U, apt as a, archive as b, command as c, compose as d, cron as e, download as f, file as g, git as h, group as i, hostname as j, rsync as k, service as l, mount as m, net as n, op as o, pkg as p, quadlet as q, releaseUpgrade as r, script as s, ssh as t, sshd as u, sysctl as v, system as w, systemd as x, ufw as y, user as z };
package/llm-guide.md CHANGED
@@ -67,6 +67,7 @@ import {
67
67
  sysctl,
68
68
  system,
69
69
  systemd,
70
+ timer,
70
71
  ufw,
71
72
  user,
72
73
  } from "paratix/modules"
@@ -133,7 +134,7 @@ export default server({
133
134
  | Method | Signature | Idempotent |
134
135
  | ----------------- | ---------------------------------------------------------------------------------------- | -------------------- |
135
136
  | `apt.debconf` | `(packageName: string, selections: Record<string, string>): Module` | Yes |
136
- | `apt.distUpgrade` | `(date: string): Module` | Yes (versioned flag) |
137
+ | `apt.distUpgrade` | `(date: string, options?: UpgradeOptions): Module` | Yes (versioned flag) |
137
138
  | `apt.key` | `(name: string, url: string, options: { fingerprint: string }): Module` | Yes |
138
139
  | `apt.repository` | `(nameOrPpa: string, source?: string, options?: { signedBy?: false \| string }): Module` | Yes |
139
140
 
@@ -151,9 +152,14 @@ export default server({
151
152
 
152
153
  ### `cron`
153
154
 
154
- | Method | Signature | Idempotent |
155
- | ---------- | ----------------------------------------------------------------------------------------------- | ---------- |
156
- | `cron.job` | `(user: string, name: string, options: { job: string; state?: "absent" \| "present" }): Module` | Yes |
155
+ | Method | Signature | Idempotent |
156
+ | ------------- | ----------------------------------------------------------------------------------------------- | ---------- |
157
+ | `cron.job` | `(user: string, name: string, options: { job: string; state?: "absent" \| "present" }): Module` | Yes |
158
+ | `cron.absent` | `(user: string, name: string): Module` | Yes |
159
+
160
+ `cron.absent(user, name)` is the dedicated uninstall variant: it removes a
161
+ managed cron entry without requiring a placeholder `job` argument. Use it as
162
+ the idiomatic way to ensure a previously installed cron job is gone.
157
163
 
158
164
  ### `compose`
159
165
 
@@ -227,12 +233,32 @@ export default server({
227
233
 
228
234
  Import with renaming: `import { package as pkg } from "paratix/modules"`. The word `package` is reserved in JavaScript, so you must alias it.
229
235
 
230
- | Method | Signature | Idempotent |
231
- | ------------------- | --------------------------------- | -------------------- |
232
- | `package.installed` | `(...packages: string[]): Module` | Yes |
233
- | `package.absent` | `(...packages: string[]): Module` | Yes |
234
- | `package.update` | `(date: string): Module` | Yes (versioned flag) |
235
- | `package.upgrade` | `(date: string): Module` | Yes (versioned flag) |
236
+ | Method | Signature | Idempotent |
237
+ | ------------------- | ------------------------------------------------------------------ | -------------------- |
238
+ | `package.installed` | `(...packagesAndOptions: Array<string \| UpgradeOptions>): Module` | Yes |
239
+ | `package.absent` | `(...packagesAndOptions: Array<string \| UpgradeOptions>): Module` | Yes |
240
+ | `package.update` | `(date: string, options?: UpgradeOptions): Module` | Yes (versioned flag) |
241
+ | `package.upgrade` | `(date: string, options?: UpgradeOptions): Module` | Yes (versioned flag) |
242
+
243
+ #### `UpgradeOptions`
244
+
245
+ ```typescript
246
+ type UpgradeOptions = {
247
+ timeout?: number // Override SSH command timeout (ms). Same value applies to every step
248
+ // of multi-step pipelines (apt upgrade, apk upgrade, apt dist-upgrade).
249
+ }
250
+ ```
251
+
252
+ For `package.installed` / `package.absent`, pass the options object as the **last** argument
253
+ after the package names; existing variadic call sites such as `pkg.installed("git", "curl")`
254
+ keep working unchanged. Use a longer `timeout` for slow operations on production servers
255
+ with large update backlogs:
256
+
257
+ ```typescript
258
+ pkg.upgrade("2026-05-01", { timeout: 900_000 })
259
+ pkg.installed("texlive-full", { timeout: 900_000 })
260
+ apt.distUpgrade("2026-05-01", { timeout: 1_200_000 })
261
+ ```
236
262
 
237
263
  ### `quadlet`
238
264
 
@@ -314,6 +340,40 @@ rsync SSH process and does not depend on a local `known_hosts` entry.
314
340
  | `systemd.masked` | `(name: string): Module` | Yes |
315
341
  | `systemd.unmasked` | `(name: string): Module` | Yes |
316
342
 
343
+ ### `timer`
344
+
345
+ | Method | Signature | Idempotent |
346
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
347
+ | `timer.scheduled` | `(name: string, options: { accuracySec?: number \| string; description?: string; environment?: Record<string, string>; exec: string; group?: string; onCalendar: string \| string[]; persistent?: boolean; randomizedDelaySec?: number \| string; state?: "absent" \| "present"; user?: string; workingDirectory?: string }): Module` | Yes |
348
+ | `timer.absent` | `(name: string): Module` | Yes |
349
+
350
+ `timer.absent(name)` is the dedicated uninstall variant: it disables and stops
351
+ the timer, removes both unit files, and reloads systemd, without requiring
352
+ placeholder `exec`/`onCalendar` values.
353
+
354
+ `timer.scheduled` is the systemd-timer equivalent of `cron.job`. It writes
355
+ `<name>.service` (`Type=oneshot`) and `<name>.timer` to `/etc/systemd/system/`,
356
+ reloads systemd, and runs `systemctl enable --now <name>.timer`. Use it as the
357
+ default for new scheduled tasks: `Persistent=true` is on by default so missed
358
+ runs are caught up after downtime, and logs land in journald (`journalctl -u
359
+ <name>.service`). Cron remains a fit for ad-hoc per-user crontab entries.
360
+
361
+ ```typescript
362
+ timer.scheduled("backup", {
363
+ exec: "/usr/local/bin/backup",
364
+ onCalendar: "*-*-* 03:00:00",
365
+ })
366
+
367
+ timer.scheduled("cleanup", {
368
+ exec: "/usr/local/bin/cleanup",
369
+ onCalendar: ["Mon..Fri 02:00", "Sat 04:00"],
370
+ user: "deploy",
371
+ randomizedDelaySec: 300,
372
+ })
373
+
374
+ timer.absent("legacy-task")
375
+ ```
376
+
317
377
  ### `ufw`
318
378
 
319
379
  | Method | Signature | Idempotent |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paratix",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Idempotent VPS setup tool in TypeScript",
5
5
  "type": "module",
6
6
  "files": [