unitup 0.0.11 → 0.1.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 +125 -6
- package/SECURITY.md +21 -0
- package/docs/index.html +118 -29
- package/index.d.ts +137 -0
- package/package.json +9 -1
- package/src/cli.js +318 -20
- package/src/index.js +53 -3
- package/src/schedule.js +462 -0
- package/src/systemd.js +126 -0
- package/src/unit.js +230 -4
- package/src/utils.js +253 -0
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ At its core, `unitup` generates systemd service unit files directly from a gener
|
|
|
85
85
|
- 🧠 **Systemd-Native Memory Limits** — Configure `MemoryHigh`, `MemoryMax`, and `MemorySwapMax` per service without extra monitoring daemons.
|
|
86
86
|
- 📜 **Journald Log Maintenance** — Advanced log streaming with filters (`--since`, `--until`, `--priority`, `--grep`, `--boot`, `--json`) and journal maintenance (`disk-usage`, `rotate`, `vacuum`).
|
|
87
87
|
- 🩺 **System readiness & runtime check** — `unitup doctor` verifies OS, systemd PID 1, systemctl user bus, cgroup v2 memory controller support, user lingering, and detected runtimes.
|
|
88
|
-
-
|
|
88
|
+
- ⏰ **Systemd Timer-Based Scheduling** — Create native `.timer` systemd units (`unitup schedule`) with interval (`--every 30m`), calendar (`--calendar daily`), boot delay (`--on-boot`), random delay jitter (`--random-delay`), and missed execution persistence (`--persistent`) without background cron daemons or continuously running worker processes.
|
|
89
89
|
- 📦 **Programmatic API** — imported directly into JavaScript and TypeScript applications.
|
|
90
90
|
|
|
91
91
|
---
|
|
@@ -112,6 +112,32 @@ unitup limits api --reset-memory
|
|
|
112
112
|
|
|
113
113
|
*Supported size units:* `128K`, `256M`, `1G`, `infinity`, `max`, or raw byte integers.
|
|
114
114
|
|
|
115
|
+
### Memory Usage Overview (`unitup memory` / `unitup top`)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# View memory usage overview for all running services & schedules
|
|
119
|
+
unitup top
|
|
120
|
+
|
|
121
|
+
# Alias: unitup memory or unitup mem
|
|
122
|
+
unitup memory
|
|
123
|
+
|
|
124
|
+
# Filter memory overview by group
|
|
125
|
+
unitup top --group backend
|
|
126
|
+
|
|
127
|
+
# View detailed memory breakdown for a specific app
|
|
128
|
+
unitup memory api
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Example Output:
|
|
132
|
+
```text
|
|
133
|
+
NAME GROUP TYPE STATUS PID MEMORY PEAK LIMIT (MAX)
|
|
134
|
+
api backend service running 14220 284 MB 361 MB 512 MB
|
|
135
|
+
worker backend service running 14302 120 MB 150 MB 1 GB
|
|
136
|
+
cleanup backend timer waiting - 0 MB unavailable 1 GB
|
|
137
|
+
|
|
138
|
+
Total Memory Usage across 2 active app(s): 404 MB
|
|
139
|
+
```
|
|
140
|
+
|
|
115
141
|
### Journald Log Maintenance
|
|
116
142
|
|
|
117
143
|
```bash
|
|
@@ -131,6 +157,52 @@ unitup journal vacuum --files 10 --yes
|
|
|
131
157
|
> [!NOTE]
|
|
132
158
|
> `unitup journal vacuum` prompts for user confirmation before executing system-wide log cleanup unless `--yes` / `-y` or `--dry-run` is provided. If permissions are insufficient, `unitup` handles it gracefully without executing `sudo`.
|
|
133
159
|
|
|
160
|
+
### Systemd Timer Schedules (`unitup schedule`)
|
|
161
|
+
|
|
162
|
+
`unitup` allows you to schedule tasks completely via native systemd `.timer` units without running background daemons, cron services, or continuous worker loops.
|
|
163
|
+
|
|
164
|
+
Each schedule creates a paired service (`~/.config/systemd/user/unitup-<name>.service`) with `Type=oneshot` and timer (`~/.config/systemd/user/unitup-<name>.timer`).
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Interval schedule (--every)
|
|
168
|
+
unitup schedule cleanup.js --every 30m --persistent --start
|
|
169
|
+
|
|
170
|
+
# Calendar schedule (--calendar)
|
|
171
|
+
unitup schedule backup.py --calendar daily --random-delay 10m --persistent --start
|
|
172
|
+
|
|
173
|
+
# Complex calendar expression
|
|
174
|
+
unitup schedule report.js --calendar "Mon..Fri 09:00" --start
|
|
175
|
+
|
|
176
|
+
# List all active schedule timers
|
|
177
|
+
unitup schedules # or unitup timers
|
|
178
|
+
|
|
179
|
+
# Detailed schedule status
|
|
180
|
+
unitup schedule-status cleanup # or unitup timer-status cleanup
|
|
181
|
+
|
|
182
|
+
# Manually run the schedule service once (leaves timer schedule unchanged)
|
|
183
|
+
unitup schedule-run cleanup
|
|
184
|
+
|
|
185
|
+
# Enable or disable schedule timers
|
|
186
|
+
unitup schedule-enable cleanup
|
|
187
|
+
unitup schedule-disable cleanup
|
|
188
|
+
|
|
189
|
+
# Remove schedule timer, service unit, and metadata
|
|
190
|
+
unitup schedule-remove cleanup [--force]
|
|
191
|
+
|
|
192
|
+
# Read scheduled task logs
|
|
193
|
+
unitup schedule-logs cleanup
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Schedule Options
|
|
197
|
+
|
|
198
|
+
- `--every <duration>`: Execution interval (e.g., `30s`, `10m`, `2h`, `1d`). Generates `OnActiveSec` and `OnUnitActiveSec`.
|
|
199
|
+
- `--calendar <expression>`: Systemd calendar expression (e.g., `daily`, `weekly`, `"Mon..Fri 09:00"`). Validated via `systemd-analyze calendar`.
|
|
200
|
+
- `--on-boot <duration>`: Delay execution relative to system boot (e.g., `5m`).
|
|
201
|
+
- `--on-active <duration>`: Delay execution relative to timer activation.
|
|
202
|
+
- `--random-delay <duration>`: Adds a randomized delay jitter to prevent simultaneous job stampedes (e.g., `10m`).
|
|
203
|
+
- `--persistent`: Sets `Persistent=true` so systemd executes missed schedules immediately after system boot or wake.
|
|
204
|
+
- `--start`: Automatically enables and starts the timer unit after creation (`systemctl --user enable --now`).
|
|
205
|
+
|
|
134
206
|
---
|
|
135
207
|
|
|
136
208
|
## Requirements
|
|
@@ -525,15 +597,62 @@ await createService({
|
|
|
525
597
|
```js
|
|
526
598
|
const {
|
|
527
599
|
createService,
|
|
528
|
-
|
|
529
|
-
|
|
600
|
+
createSchedule,
|
|
601
|
+
listSchedules
|
|
530
602
|
} = require("unitup");
|
|
531
603
|
|
|
532
|
-
await
|
|
533
|
-
name: "
|
|
534
|
-
script: "./
|
|
604
|
+
await createSchedule({
|
|
605
|
+
name: "cleanup",
|
|
606
|
+
script: "./cleanup.js",
|
|
607
|
+
every: "30m",
|
|
608
|
+
start: true
|
|
609
|
+
});
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
### Programmatic Schedule API Example
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
import {
|
|
616
|
+
createSchedule,
|
|
617
|
+
listSchedules,
|
|
618
|
+
getScheduleStatus,
|
|
619
|
+
runSchedule,
|
|
620
|
+
enableSchedule,
|
|
621
|
+
disableSchedule,
|
|
622
|
+
removeSchedule
|
|
623
|
+
} from "unitup";
|
|
624
|
+
|
|
625
|
+
// Create an interval schedule
|
|
626
|
+
await createSchedule({
|
|
627
|
+
name: "cleanup",
|
|
628
|
+
script: "./cleanup.js",
|
|
629
|
+
runtime: "node",
|
|
630
|
+
every: "30m",
|
|
631
|
+
persistent: true,
|
|
535
632
|
start: true
|
|
536
633
|
});
|
|
634
|
+
|
|
635
|
+
// Create a calendar schedule with random delay jitter
|
|
636
|
+
await createSchedule({
|
|
637
|
+
name: "backup",
|
|
638
|
+
script: "./backup.py",
|
|
639
|
+
runtime: "python",
|
|
640
|
+
calendar: "daily",
|
|
641
|
+
randomDelay: "10m",
|
|
642
|
+
persistent: true,
|
|
643
|
+
start: true
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// List all active schedules
|
|
647
|
+
const schedules = await listSchedules();
|
|
648
|
+
console.log(schedules);
|
|
649
|
+
|
|
650
|
+
// Manually trigger a schedule run
|
|
651
|
+
await runSchedule("cleanup");
|
|
652
|
+
|
|
653
|
+
// Disable and remove schedule
|
|
654
|
+
await disableSchedule("cleanup");
|
|
655
|
+
await removeSchedule("cleanup");
|
|
537
656
|
```
|
|
538
657
|
|
|
539
658
|
### Script & Runtime Example
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Use this section to tell people about which versions of your project are
|
|
6
|
+
currently being supported with security updates.
|
|
7
|
+
|
|
8
|
+
| Version | Supported |
|
|
9
|
+
| ------- | ------------------ |
|
|
10
|
+
| 5.1.x | :white_check_mark: |
|
|
11
|
+
| 5.0.x | :x: |
|
|
12
|
+
| 4.0.x | :white_check_mark: |
|
|
13
|
+
| < 4.0 | :x: |
|
|
14
|
+
|
|
15
|
+
## Reporting a Vulnerability
|
|
16
|
+
|
|
17
|
+
Use this section to tell people how to report a vulnerability.
|
|
18
|
+
|
|
19
|
+
Tell them where to go, how often they can expect to get an update on a
|
|
20
|
+
reported vulnerability, what to expect if the vulnerability is accepted or
|
|
21
|
+
declined, etc.
|
package/docs/index.html
CHANGED
|
@@ -25,12 +25,16 @@
|
|
|
25
25
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/yaml.min.js"></script>
|
|
26
26
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/ini.min.js"></script>
|
|
27
27
|
<style>
|
|
28
|
+
body { overflow-x: hidden; }
|
|
29
|
+
.grid > * { min-width: 0; }
|
|
28
30
|
html { scroll-behavior: smooth; }
|
|
29
|
-
pre { tab-size: 2; margin: 0; }
|
|
30
|
-
pre code { padding: 1rem; background: #0f172a; border-radius: 0.75rem; font-size: 0.
|
|
31
|
-
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
31
|
+
pre { tab-size: 2; margin: 0; overflow-x: auto; max-width: 100%; }
|
|
32
|
+
pre code { padding: 1rem; background: #0f172a; border-radius: 0.75rem; font-size: 0.85rem; display: block; overflow-x: auto; white-space: pre; }
|
|
33
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; word-break: break-word; }
|
|
32
34
|
.doc-section { scroll-margin-top: 2rem; }
|
|
33
|
-
table { border-
|
|
35
|
+
.table-container { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 0.75rem; border: 1px solid #e2e8f0; margin-bottom: 1rem; }
|
|
36
|
+
.dark .table-container { border-color: #334155; }
|
|
37
|
+
table { border-collapse: collapse; width: 100%; min-width: 580px; }
|
|
34
38
|
th, td { border: 1px solid #e2e8f0; padding: 0.75rem 1rem; text-align: left; }
|
|
35
39
|
th { background: #f8fafc; font-weight: 600; }
|
|
36
40
|
.dark th, .dark td { border-color: #334155; }
|
|
@@ -38,31 +42,39 @@
|
|
|
38
42
|
</style>
|
|
39
43
|
</head>
|
|
40
44
|
<body class="bg-slate-50 text-slate-900 antialiased dark:bg-slate-950 dark:text-slate-100 transition-colors">
|
|
41
|
-
<div class="max-w-6xl mx-auto px-6 py-10">
|
|
45
|
+
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-6 sm:py-10">
|
|
42
46
|
<!-- Top Navigation Bar -->
|
|
43
|
-
<div class="flex items-center justify-between mb-10 gap-4 flex-wrap pb-6 border-b border-slate-200 dark:border-slate-800">
|
|
44
|
-
<div class="flex items-center gap-3">
|
|
47
|
+
<div class="flex items-center justify-between mb-8 sm:mb-10 gap-4 flex-wrap pb-6 border-b border-slate-200 dark:border-slate-800">
|
|
48
|
+
<div class="flex items-center gap-2.5 sm:gap-3">
|
|
45
49
|
<span class="text-2xl font-black tracking-tight text-cyan-600 dark:text-cyan-400">unitup</span>
|
|
46
|
-
<span class="text-xs px-2.5 py-0.5 rounded-full bg-cyan-100 text-cyan-800 dark:bg-cyan-950 dark:text-cyan-300 font-semibold border border-cyan-300 dark:border-cyan-800">v0.0.
|
|
47
|
-
<a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-slate-500 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 transition-colors" aria-label="GitHub Repository">
|
|
50
|
+
<span class="text-xs px-2.5 py-0.5 rounded-full bg-cyan-100 text-cyan-800 dark:bg-cyan-950 dark:text-cyan-300 font-semibold border border-cyan-300 dark:border-cyan-800">v0.0.11</span>
|
|
51
|
+
<a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-slate-500 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 transition-colors p-1" aria-label="GitHub Repository">
|
|
48
52
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.53 1.032 1.53 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
|
|
49
53
|
</a>
|
|
50
54
|
</div>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
|
|
56
|
+
<div class="flex items-center gap-2">
|
|
57
|
+
<button id="mobile-menu-btn" type="button" class="md:hidden p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400" aria-label="Toggle navigation menu">
|
|
58
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
|
59
|
+
</button>
|
|
60
|
+
<button id="theme-toggle" type="button" class="p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400 shrink-0" aria-label="Toggle dark mode">
|
|
61
|
+
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
62
|
+
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<nav id="nav-menu" class="hidden md:flex w-full md:w-auto flex-col md:flex-row flex-wrap gap-x-4 gap-y-2 text-sm font-medium pt-3 md:pt-0 border-t md:border-t-0 border-slate-200 dark:border-slate-800" aria-label="Page sections">
|
|
67
|
+
<a href="#overview" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Overview</a>
|
|
68
|
+
<a href="#comparison" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">vs PM2</a>
|
|
69
|
+
<a href="#architecture" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Architecture</a>
|
|
70
|
+
<a href="#runtimes" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Runtimes</a>
|
|
71
|
+
<a href="#cli" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">CLI Reference</a>
|
|
72
|
+
<a href="#schedules" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Schedules</a>
|
|
73
|
+
<a href="#api" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">API</a>
|
|
74
|
+
<a href="#declarative" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Roadmap</a>
|
|
75
|
+
<a href="#troubleshooting" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Lingering</a>
|
|
76
|
+
<a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-cyan-600 dark:text-cyan-400 hover:underline flex items-center gap-1 font-semibold py-1">GitHub →</a>
|
|
61
77
|
</nav>
|
|
62
|
-
<button id="theme-toggle" type="button" class="p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400 shrink-0" aria-label="Toggle dark mode">
|
|
63
|
-
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
64
|
-
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
|
65
|
-
</button>
|
|
66
78
|
</div>
|
|
67
79
|
|
|
68
80
|
<!-- Header / Hero Section -->
|
|
@@ -146,7 +158,7 @@ unitup add ./server --runtime native --name api --start</code></pre>
|
|
|
146
158
|
<strong class="text-slate-800 dark:text-slate-200">unitup</strong> is not a PM2 replacement or an independent process manager. While PM2 runs its own master daemon, unitup is strictly a thin, transparent CLI layer on top of Linux OS <code class="text-cyan-600 dark:text-cyan-400">systemd</code>.
|
|
147
159
|
</p>
|
|
148
160
|
|
|
149
|
-
<div class="
|
|
161
|
+
<div class="table-container">
|
|
150
162
|
<table>
|
|
151
163
|
<thead>
|
|
152
164
|
<tr>
|
|
@@ -203,8 +215,8 @@ unitup add ./server --runtime native --name api --start</code></pre>
|
|
|
203
215
|
At its core, unitup generates systemd service unit files directly from a generic <code class="text-cyan-600 dark:text-cyan-400">command + args</code> model rather than being hardcoded to Node.js:
|
|
204
216
|
</p>
|
|
205
217
|
|
|
206
|
-
<div class="grid md:grid-cols-2 gap-6">
|
|
207
|
-
<div>
|
|
218
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
219
|
+
<div class="min-w-0">
|
|
208
220
|
<h3 class="text-sm font-semibold uppercase tracking-wider text-cyan-600 dark:text-cyan-400 mb-2">JSON Execution Model</h3>
|
|
209
221
|
<pre><code class="language-json">{
|
|
210
222
|
"command": "/usr/bin/python3",
|
|
@@ -215,8 +227,8 @@ unitup add ./server --runtime native --name api --start</code></pre>
|
|
|
215
227
|
}
|
|
216
228
|
}</code></pre>
|
|
217
229
|
</div>
|
|
218
|
-
<div>
|
|
219
|
-
<h3 class="text-sm font-semibold uppercase tracking-wider text-cyan-600 dark:text-cyan-400 mb-2">Generated Unit File
|
|
230
|
+
<div class="min-w-0">
|
|
231
|
+
<h3 class="text-sm font-semibold uppercase tracking-wider text-cyan-600 dark:text-cyan-400 mb-2 flex flex-wrap items-center gap-1">Generated Unit File <span class="lowercase font-mono text-xs normal-case opacity-90 break-all">(unitup-worker.service)</span></h3>
|
|
220
232
|
<pre><code class="language-ini">[Unit]
|
|
221
233
|
Description=unitup service: worker
|
|
222
234
|
After=network.target
|
|
@@ -241,7 +253,7 @@ WantedBy=default.target</code></pre>
|
|
|
241
253
|
<section id="runtimes" class="doc-section mb-14">
|
|
242
254
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-4">Supported Runtimes & Auto-Detection</h2>
|
|
243
255
|
|
|
244
|
-
<div class="
|
|
256
|
+
<div class="table-container">
|
|
245
257
|
<table>
|
|
246
258
|
<thead>
|
|
247
259
|
<tr>
|
|
@@ -372,6 +384,15 @@ unitup stop @backend</code></pre>
|
|
|
372
384
|
unitup limits api --reset-memory</code></pre>
|
|
373
385
|
</div>
|
|
374
386
|
|
|
387
|
+
<!-- memory / top -->
|
|
388
|
+
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
389
|
+
<h3 class="text-lg font-bold text-cyan-600 dark:text-cyan-400 mb-2"><code class="text-base">unitup memory</code> / <code class="text-base">unitup top</code></h3>
|
|
390
|
+
<p class="text-sm text-slate-600 dark:text-slate-400 mb-3">Displays memory usage overview across all running apps and schedules, with aggregated total memory consumption summary.</p>
|
|
391
|
+
<pre><code class="language-bash">unitup top
|
|
392
|
+
unitup memory api
|
|
393
|
+
unitup mem --group backend</code></pre>
|
|
394
|
+
</div>
|
|
395
|
+
|
|
375
396
|
<!-- status & logs -->
|
|
376
397
|
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
377
398
|
<h3 class="text-lg font-bold text-cyan-600 dark:text-cyan-400 mb-2"><code class="text-base">unitup status / logs <name></code></h3>
|
|
@@ -400,6 +421,58 @@ unitup remove @backend --force</code></pre>
|
|
|
400
421
|
</div>
|
|
401
422
|
</section>
|
|
402
423
|
|
|
424
|
+
<!-- Section: Schedules -->
|
|
425
|
+
<section id="schedules" class="doc-section mb-14">
|
|
426
|
+
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-4">Systemd Timer Schedules</h2>
|
|
427
|
+
<p class="text-slate-600 dark:text-slate-400 mb-6 leading-relaxed">
|
|
428
|
+
<strong class="text-slate-800 dark:text-slate-200">unitup</strong> supports native systemd timer scheduling without running continuous worker processes or cron daemons. Each schedule generates a paired oneshot service (<code class="text-cyan-700 dark:text-cyan-400">~/.config/systemd/user/unitup-<name>.service</code>) and timer unit (<code class="text-cyan-700 dark:text-cyan-400">~/.config/systemd/user/unitup-<name>.timer</code>).
|
|
429
|
+
</p>
|
|
430
|
+
|
|
431
|
+
<div class="space-y-6">
|
|
432
|
+
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
433
|
+
<h3 class="text-lg font-bold text-cyan-600 dark:text-cyan-400 mb-2">CLI Schedule Commands</h3>
|
|
434
|
+
<pre><code class="language-bash"># Create interval schedule (--every 30m)
|
|
435
|
+
unitup schedule cleanup.js --every 30m --persistent --start
|
|
436
|
+
|
|
437
|
+
# Create calendar schedule (--calendar daily with random delay jitter)
|
|
438
|
+
unitup schedule backup.py --calendar daily --random-delay 10m --persistent --start
|
|
439
|
+
|
|
440
|
+
# Specific calendar expression
|
|
441
|
+
unitup schedule report.js --calendar "Mon..Fri 09:00" --start
|
|
442
|
+
|
|
443
|
+
# List all schedule timers
|
|
444
|
+
unitup schedules # or unitup timers
|
|
445
|
+
|
|
446
|
+
# Detailed schedule status
|
|
447
|
+
unitup schedule-status cleanup
|
|
448
|
+
|
|
449
|
+
# Manually trigger schedule service unit once
|
|
450
|
+
unitup schedule-run cleanup
|
|
451
|
+
|
|
452
|
+
# Enable / Disable / Remove schedule
|
|
453
|
+
unitup schedule-enable cleanup
|
|
454
|
+
unitup schedule-disable cleanup
|
|
455
|
+
unitup schedule-remove cleanup [--force]
|
|
456
|
+
|
|
457
|
+
# Read schedule logs
|
|
458
|
+
unitup schedule-logs cleanup</code></pre>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
462
|
+
<h3 class="text-lg font-bold text-cyan-600 dark:text-cyan-400 mb-2">Schedule Options</h3>
|
|
463
|
+
<ul class="list-disc list-inside space-y-1.5 text-sm text-slate-600 dark:text-slate-400">
|
|
464
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--every <duration></code>: Execution interval (e.g., <code class="text-xs">30s</code>, <code class="text-xs">10m</code>, <code class="text-xs">2h</code>, <code class="text-xs">1d</code>). Sets <code class="text-xs">OnActiveSec</code> and <code class="text-xs">OnUnitActiveSec</code>.</li>
|
|
465
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--calendar <expression></code>: Systemd calendar expression (e.g., <code class="text-xs">daily</code>, <code class="text-xs">"Mon..Fri 09:00"</code>). Validated via <code class="text-xs">systemd-analyze calendar</code>.</li>
|
|
466
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--on-boot <duration></code>: Delay execution relative to system boot (e.g., <code class="text-xs">5m</code>).</li>
|
|
467
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--on-active <duration></code>: Delay execution relative to timer activation.</li>
|
|
468
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--random-delay <duration></code>: Adds randomized delay jitter (<code class="text-xs">RandomizedDelaySec</code>).</li>
|
|
469
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--persistent</code>: Sets <code class="text-xs">Persistent=true</code> to execute missed schedules immediately after system wake/boot.</li>
|
|
470
|
+
<li><code class="text-xs font-bold text-slate-800 dark:text-slate-200">--start</code>: Enables and starts the timer unit immediately after creation.</li>
|
|
471
|
+
</ul>
|
|
472
|
+
</div>
|
|
473
|
+
</div>
|
|
474
|
+
</section>
|
|
475
|
+
|
|
403
476
|
<!-- Section: API -->
|
|
404
477
|
<section id="api" class="doc-section mb-14">
|
|
405
478
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-4">Programmatic API</h2>
|
|
@@ -535,6 +608,22 @@ unitup restart @default</code></pre>
|
|
|
535
608
|
document.documentElement.classList.toggle('dark');
|
|
536
609
|
});
|
|
537
610
|
|
|
611
|
+
// Mobile navigation toggle
|
|
612
|
+
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
|
|
613
|
+
const navMenu = document.getElementById('nav-menu');
|
|
614
|
+
if (mobileMenuBtn && navMenu) {
|
|
615
|
+
mobileMenuBtn.addEventListener('click', () => {
|
|
616
|
+
navMenu.classList.toggle('hidden');
|
|
617
|
+
});
|
|
618
|
+
navMenu.querySelectorAll('a').forEach(link => {
|
|
619
|
+
link.addEventListener('click', () => {
|
|
620
|
+
if (window.innerWidth < 768) {
|
|
621
|
+
navMenu.classList.add('hidden');
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
|
|
538
627
|
// Highlight code blocks
|
|
539
628
|
document.addEventListener('DOMContentLoaded', () => {
|
|
540
629
|
hljs.highlightAll();
|
package/index.d.ts
CHANGED
|
@@ -97,6 +97,11 @@ export interface CreateServiceOptions {
|
|
|
97
97
|
* Swap memory limit directive (e.g. '256M').
|
|
98
98
|
*/
|
|
99
99
|
memorySwapMax?: string;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Default memory limit fallback or request marker (defaults to '1G').
|
|
103
|
+
*/
|
|
104
|
+
defaultMemory?: string | boolean;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
/**
|
|
@@ -420,6 +425,36 @@ export function inspectService(name: string): Promise<InspectInfo>;
|
|
|
420
425
|
export function getServiceFailures(): Promise<FailureItem[]>;
|
|
421
426
|
export function getServicesByGroup(groupName: string): Promise<string[]>;
|
|
422
427
|
export function setServiceLimits(name: string, options?: ServiceLimitsOptions): Promise<InspectInfo>;
|
|
428
|
+
export function getServiceMemoryUsage(name: string): Promise<{
|
|
429
|
+
name: string;
|
|
430
|
+
group: string;
|
|
431
|
+
type: string;
|
|
432
|
+
status: string;
|
|
433
|
+
pid: string;
|
|
434
|
+
memoryBytes: number;
|
|
435
|
+
memory: string;
|
|
436
|
+
memoryPeak: string;
|
|
437
|
+
memoryHigh: string;
|
|
438
|
+
memoryMax: string;
|
|
439
|
+
memorySwapMax: string;
|
|
440
|
+
}>;
|
|
441
|
+
export function getAllServicesMemoryUsage(options?: { group?: string }): Promise<{
|
|
442
|
+
items: Array<{
|
|
443
|
+
name: string;
|
|
444
|
+
group: string;
|
|
445
|
+
type: string;
|
|
446
|
+
status: string;
|
|
447
|
+
pid: string;
|
|
448
|
+
memory: string;
|
|
449
|
+
memoryPeak: string;
|
|
450
|
+
memoryMax: string;
|
|
451
|
+
memoryHigh: string;
|
|
452
|
+
memoryBytes: number;
|
|
453
|
+
}>;
|
|
454
|
+
totalBytes: number;
|
|
455
|
+
totalMemory: string;
|
|
456
|
+
runningCount: number;
|
|
457
|
+
}>;
|
|
423
458
|
export function executeJournalctlMaintenance(action: string, options?: Record<string, unknown>): Promise<string>;
|
|
424
459
|
export function getServiceLogs(name: string, options?: LogOptions): Promise<string | ChildProcess>;
|
|
425
460
|
|
|
@@ -463,3 +498,105 @@ export function getUnitupDir(): string;
|
|
|
463
498
|
// Testing Mock Helpers
|
|
464
499
|
export function setCommandRunner(runner: CommandRunnerFn): void;
|
|
465
500
|
export function resetCommandRunner(): void;
|
|
501
|
+
|
|
502
|
+
// ---------------------------------------------------------------------------
|
|
503
|
+
// Schedule Management API
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
|
|
506
|
+
export interface CreateScheduleOptions {
|
|
507
|
+
name?: string;
|
|
508
|
+
script?: string;
|
|
509
|
+
command?: string;
|
|
510
|
+
runtime?: string;
|
|
511
|
+
args?: string[];
|
|
512
|
+
runtimeArgs?: string[];
|
|
513
|
+
cwd?: string;
|
|
514
|
+
env?: Record<string, string> | string[];
|
|
515
|
+
envFile?: string;
|
|
516
|
+
group?: string;
|
|
517
|
+
memoryHigh?: string;
|
|
518
|
+
memoryMax?: string;
|
|
519
|
+
memorySwapMax?: string;
|
|
520
|
+
defaultMemory?: string | boolean;
|
|
521
|
+
every?: string;
|
|
522
|
+
calendar?: string;
|
|
523
|
+
onBoot?: string;
|
|
524
|
+
onActive?: string;
|
|
525
|
+
randomDelay?: string;
|
|
526
|
+
persistent?: boolean;
|
|
527
|
+
start?: boolean;
|
|
528
|
+
enable?: boolean;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export interface ScheduleMetadata {
|
|
532
|
+
name: string;
|
|
533
|
+
group: string;
|
|
534
|
+
type: string;
|
|
535
|
+
runtime: string;
|
|
536
|
+
command: string;
|
|
537
|
+
args: string[];
|
|
538
|
+
cwd: string;
|
|
539
|
+
schedule: {
|
|
540
|
+
every: string | null;
|
|
541
|
+
calendar: string | null;
|
|
542
|
+
onBoot: string | null;
|
|
543
|
+
onActive: string | null;
|
|
544
|
+
persistent: boolean;
|
|
545
|
+
randomDelay: string | null;
|
|
546
|
+
};
|
|
547
|
+
serviceUnit: string;
|
|
548
|
+
timerUnit: string;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export interface ScheduleStatus {
|
|
552
|
+
name: string;
|
|
553
|
+
group: string;
|
|
554
|
+
schedule: string;
|
|
555
|
+
nextRun: string;
|
|
556
|
+
lastRun: string;
|
|
557
|
+
status: string;
|
|
558
|
+
activeState: string;
|
|
559
|
+
subState: string;
|
|
560
|
+
unitFileState: string;
|
|
561
|
+
serviceActiveState: string;
|
|
562
|
+
metadata?: ScheduleMetadata | null;
|
|
563
|
+
serviceUnit: string;
|
|
564
|
+
timerUnit: string;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export interface CreateScheduleResult {
|
|
568
|
+
name: string;
|
|
569
|
+
group: string;
|
|
570
|
+
type: string;
|
|
571
|
+
runtime: string;
|
|
572
|
+
command: string;
|
|
573
|
+
args: string[];
|
|
574
|
+
cwd: string;
|
|
575
|
+
schedule: Record<string, unknown>;
|
|
576
|
+
serviceUnit: string;
|
|
577
|
+
timerUnit: string;
|
|
578
|
+
servicePath: string;
|
|
579
|
+
timerPath: string;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function createSchedule(options: CreateScheduleOptions): Promise<CreateScheduleResult>;
|
|
583
|
+
export function listSchedules(group?: string): Promise<ScheduleStatus[]>;
|
|
584
|
+
export function getScheduleStatus(name: string): Promise<ScheduleStatus>;
|
|
585
|
+
export function runSchedule(name: string): Promise<boolean>;
|
|
586
|
+
export function enableSchedule(name: string): Promise<boolean>;
|
|
587
|
+
export function disableSchedule(name: string): Promise<boolean>;
|
|
588
|
+
export function removeSchedule(name: string, options?: { force?: boolean }): Promise<boolean>;
|
|
589
|
+
export function validateCalendar(expression: string): Promise<{ valid: boolean; error?: string; warning?: string }>;
|
|
590
|
+
export function validateDuration(val: string | number, paramName?: string): string;
|
|
591
|
+
export function getTimerFilename(name: string): string;
|
|
592
|
+
export function getTimerPath(name: string): string;
|
|
593
|
+
export function timerFileExists(name: string): boolean;
|
|
594
|
+
export function readScheduleMetadata(name: string): ScheduleMetadata | null;
|
|
595
|
+
export function getScheduleMetadataPath(name: string): string;
|
|
596
|
+
export function getSchedulesDir(): string;
|
|
597
|
+
export function readGlobalConfig(): Record<string, unknown>;
|
|
598
|
+
export function saveGlobalConfig(config: Record<string, unknown>): Record<string, unknown>;
|
|
599
|
+
export function getConfigFilepath(): string;
|
|
600
|
+
export function resolveEffectiveMemoryLimits(opts?: Record<string, unknown>): { memoryHigh?: string; memoryMax?: string; memorySwapMax?: string };
|
|
601
|
+
|
|
602
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unitup",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Minimal, zero-dependency systemd-native service manager for any runtime, script, or executable",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
],
|
|
37
37
|
"author": "",
|
|
38
38
|
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/litepacks/unitup.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/litepacks/unitup/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/litepacks/unitup#readme",
|
|
39
47
|
"engines": {
|
|
40
48
|
"node": ">=20.0.0"
|
|
41
49
|
}
|