systemd-ts 0.2.0 → 0.3.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 +285 -24
- package/dist/index.d.mts +241 -6
- package/dist/index.mjs +582 -97
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,63 +1,312 @@
|
|
|
1
1
|
# systemd-ts
|
|
2
2
|
|
|
3
|
-
`systemd-ts`
|
|
4
|
-
services and timers from TypeScript.
|
|
3
|
+
`systemd-ts` lets you work with `systemd` from TypeScript.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
It is designed around a few practical jobs:
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
- keep application-owned units defined in `.timer` and `.service`up to date from TypeScript
|
|
8
|
+
- use `systemctl` from TypeScript
|
|
9
|
+
- define a service from TypeScript
|
|
10
|
+
- define a service that runs on a timer from TypeScript
|
|
11
|
+
- keep the code that runs the job close to the code that defines the service and timer
|
|
12
|
+
|
|
13
|
+
The library has two layers:
|
|
14
|
+
|
|
15
|
+
- `systemd.ts.*` is the code-owned upkeep layer for attaching, reattaching, and
|
|
16
|
+
detaching application-managed units.
|
|
17
|
+
- `systemd.*` stays close to raw `systemd` concepts like materializing units and
|
|
18
|
+
starting them directly.
|
|
19
|
+
|
|
20
|
+
That lets the README start from the application code story without giving up a
|
|
21
|
+
lower-level `systemd` surface when you want it.
|
|
22
|
+
|
|
23
|
+
It is also a good place to learn `systemd` itself. The unit directives are
|
|
24
|
+
deeply typed, documented in the TypeScript surface, and maintained against
|
|
25
|
+
versioned upstream manpage material, so the library doubles as a guided way to
|
|
26
|
+
explore what `systemd` can do without starting from loose strings and scattered
|
|
27
|
+
shell examples.
|
|
28
|
+
|
|
29
|
+
## What It Can Do
|
|
30
|
+
|
|
31
|
+
### Keep application-owned units up to date
|
|
32
|
+
|
|
33
|
+
If your application owns a service or timer and wants to keep that unit set up
|
|
34
|
+
to date across reinstalls, updates, or reconfiguration, use `systemd.ts`.
|
|
11
35
|
|
|
12
36
|
```ts
|
|
13
37
|
import { homedir } from "node:os";
|
|
14
38
|
import { join } from "node:path";
|
|
15
39
|
|
|
40
|
+
import backupJob from "./backup-job.ts";
|
|
16
41
|
import { Systemd, SystemdService, SystemdTimer } from "systemd-ts";
|
|
17
42
|
|
|
18
|
-
const
|
|
43
|
+
const systemd = new Systemd({
|
|
44
|
+
scope: `user`,
|
|
45
|
+
unitDir: join(homedir(), `.config/systemd/user`),
|
|
46
|
+
linkUnits: true,
|
|
47
|
+
});
|
|
19
48
|
|
|
20
49
|
const service = new SystemdService({
|
|
21
|
-
name:
|
|
50
|
+
name: `backup-db`,
|
|
51
|
+
service: {
|
|
52
|
+
Type: `oneshot`,
|
|
53
|
+
ExecStart: backupJob,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const timer = new SystemdTimer({
|
|
58
|
+
name: `backup-db`,
|
|
59
|
+
timer: {
|
|
60
|
+
OnCalendar: `daily`,
|
|
61
|
+
Persistent: true,
|
|
62
|
+
Unit: service.filename,
|
|
63
|
+
},
|
|
64
|
+
install: {
|
|
65
|
+
WantedBy: `timers.target`,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const attached = await systemd.ts.reattach([service, timer], {
|
|
70
|
+
owner: `com.example.backup-db`,
|
|
71
|
+
enable: true,
|
|
72
|
+
start: true,
|
|
73
|
+
prune: true,
|
|
74
|
+
});
|
|
75
|
+
if (!attached.ok) {
|
|
76
|
+
throw attached.error;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This is the highest-level upkeep workflow in the library. It is designed for
|
|
81
|
+
desktop apps, self-updating agents, OTA-managed software, and other code-owned
|
|
82
|
+
service setups where the application itself is responsible for the unit set it
|
|
83
|
+
maintains.
|
|
84
|
+
|
|
85
|
+
### Use `systemctl` from TypeScript
|
|
86
|
+
|
|
87
|
+
If you want a typed, argv-first `systemctl` client, use `Systemctl`.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Systemctl } from "systemd-ts";
|
|
91
|
+
|
|
92
|
+
const systemctl = new Systemctl({ scope: `user` });
|
|
93
|
+
|
|
94
|
+
const timers = await systemctl.listTimers({ all: true });
|
|
95
|
+
if (!timers.ok) {
|
|
96
|
+
throw timers.error;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const status = await systemctl.showServiceStatus(`backup-db.service`);
|
|
100
|
+
if (!status.ok) {
|
|
101
|
+
throw status.error;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(timers.value);
|
|
105
|
+
console.log(status.value);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This is a good fit when you already know the units you want to inspect or
|
|
109
|
+
control and you want to stay inside TypeScript instead of shelling out from app
|
|
110
|
+
code by hand.
|
|
111
|
+
|
|
112
|
+
### Define a service from TypeScript
|
|
113
|
+
|
|
114
|
+
If you want to describe a service unit in code, use `SystemdService`.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { SystemdService } from "systemd-ts";
|
|
118
|
+
|
|
119
|
+
const service = new SystemdService({
|
|
120
|
+
name: `backup-db`,
|
|
22
121
|
unit: {
|
|
23
|
-
Description:
|
|
122
|
+
Description: `Write a database backup`,
|
|
24
123
|
},
|
|
25
124
|
service: {
|
|
26
|
-
Type:
|
|
27
|
-
ExecStart:
|
|
125
|
+
Type: `oneshot`,
|
|
126
|
+
ExecStart: `/srv/app/bin/backup-db`,
|
|
127
|
+
StandardOutput: `append:/srv/app/log/backup-db.log`,
|
|
128
|
+
StandardError: `append:/srv/app/log/backup-db.log`,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
That service can be rendered into a real unit file, materialized into a unit
|
|
134
|
+
directory, and then managed through `Systemd` or `Systemctl`.
|
|
135
|
+
|
|
136
|
+
### Define a service that runs on a timer
|
|
137
|
+
|
|
138
|
+
If you want a scheduled job, pair `SystemdService` with `SystemdTimer`.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { SystemdService, SystemdTimer } from "systemd-ts";
|
|
142
|
+
|
|
143
|
+
const service = new SystemdService({
|
|
144
|
+
name: `backup-db`,
|
|
145
|
+
service: {
|
|
146
|
+
Type: `oneshot`,
|
|
147
|
+
ExecStart: `/srv/app/bin/backup-db`,
|
|
28
148
|
},
|
|
29
149
|
});
|
|
30
150
|
|
|
31
151
|
const timer = new SystemdTimer({
|
|
32
|
-
name:
|
|
152
|
+
name: `backup-db`,
|
|
33
153
|
unit: {
|
|
34
|
-
Description:
|
|
154
|
+
Description: `Run the backup every night`,
|
|
155
|
+
},
|
|
156
|
+
timer: {
|
|
157
|
+
OnCalendar: `03:15`,
|
|
158
|
+
Persistent: true,
|
|
159
|
+
Unit: service.filename,
|
|
160
|
+
},
|
|
161
|
+
install: {
|
|
162
|
+
WantedBy: `timers.target`,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This gives you a real `.service` plus a real `.timer`, both defined from the
|
|
168
|
+
same TypeScript module.
|
|
169
|
+
|
|
170
|
+
### Keep the executable close to the unit definitions
|
|
171
|
+
|
|
172
|
+
For many projects, the nicest workflow is to colocate the job entrypoint with
|
|
173
|
+
the service and timer that run it.
|
|
174
|
+
|
|
175
|
+
`defineExecutable()` is designed for that pattern:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import { defineExecutable } from "systemd-ts";
|
|
179
|
+
|
|
180
|
+
export default defineExecutable(async () => {
|
|
181
|
+
console.log(`Writing backup...`);
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Then you can use that executable directly in a service definition:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import backupJob from "./backup-job.ts";
|
|
189
|
+
import { SystemdService, SystemdTimer } from "systemd-ts";
|
|
190
|
+
|
|
191
|
+
const service = new SystemdService({
|
|
192
|
+
name: `backup-db`,
|
|
193
|
+
service: {
|
|
194
|
+
Type: `oneshot`,
|
|
195
|
+
ExecStart: backupJob,
|
|
35
196
|
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const timer = new SystemdTimer({
|
|
200
|
+
name: `backup-db`,
|
|
36
201
|
timer: {
|
|
37
|
-
OnCalendar:
|
|
202
|
+
OnCalendar: `daily`,
|
|
203
|
+
Unit: service.filename,
|
|
204
|
+
},
|
|
205
|
+
install: {
|
|
206
|
+
WantedBy: `timers.target`,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
That keeps the scheduled code, the service definition, and the timer definition
|
|
212
|
+
near each other instead of scattering them across shell scripts and unit files.
|
|
213
|
+
|
|
214
|
+
## A Typical Flow
|
|
215
|
+
|
|
216
|
+
Most applications use the library in a flow like this:
|
|
217
|
+
|
|
218
|
+
1. Define a `SystemdService`
|
|
219
|
+
2. Optionally define a matching `SystemdTimer`
|
|
220
|
+
3. Reattach the desired unit set through `systemd.ts`
|
|
221
|
+
4. Inspect or control units directly through `Systemctl` or `Systemd` when needed
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { homedir } from "node:os";
|
|
225
|
+
import { join } from "node:path";
|
|
226
|
+
|
|
227
|
+
import backupJob from "./backup-job.ts";
|
|
228
|
+
import { Systemd, SystemdService, SystemdTimer } from "systemd-ts";
|
|
229
|
+
|
|
230
|
+
const systemd = new Systemd({
|
|
231
|
+
scope: `user`,
|
|
232
|
+
unitDir: join(homedir(), `.config/systemd/user`),
|
|
233
|
+
linkUnits: true,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const service = new SystemdService({
|
|
237
|
+
name: `backup-db`,
|
|
238
|
+
service: {
|
|
239
|
+
Type: `oneshot`,
|
|
240
|
+
ExecStart: backupJob,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const timer = new SystemdTimer({
|
|
245
|
+
name: `backup-db`,
|
|
246
|
+
timer: {
|
|
247
|
+
OnCalendar: `daily`,
|
|
38
248
|
Persistent: true,
|
|
39
249
|
Unit: service.filename,
|
|
40
250
|
},
|
|
41
251
|
install: {
|
|
42
|
-
WantedBy:
|
|
252
|
+
WantedBy: `timers.target`,
|
|
43
253
|
},
|
|
44
254
|
});
|
|
45
255
|
|
|
256
|
+
const reattached = await systemd.ts.reattach([service, timer], {
|
|
257
|
+
owner: `com.example.backup-db`,
|
|
258
|
+
enable: true,
|
|
259
|
+
start: true,
|
|
260
|
+
prune: true,
|
|
261
|
+
});
|
|
262
|
+
if (!reattached.ok) {
|
|
263
|
+
throw reattached.error;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Using The `Systemd` API Directly
|
|
268
|
+
|
|
269
|
+
If you want to stay closer to raw `systemd` operations, use the lower-level
|
|
270
|
+
`Systemd` methods directly:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { homedir } from "node:os";
|
|
274
|
+
import { join } from "node:path";
|
|
275
|
+
|
|
276
|
+
import backupJob from "./backup-job.ts";
|
|
277
|
+
import { Systemd, SystemdService, SystemdTimer } from "systemd-ts";
|
|
278
|
+
|
|
46
279
|
const systemd = new Systemd({
|
|
47
|
-
scope:
|
|
48
|
-
unitDir:
|
|
280
|
+
scope: `user`,
|
|
281
|
+
unitDir: join(homedir(), `.config/systemd/user`),
|
|
49
282
|
linkUnits: true,
|
|
50
283
|
});
|
|
51
284
|
|
|
285
|
+
const service = new SystemdService({
|
|
286
|
+
name: `backup-db`,
|
|
287
|
+
service: {
|
|
288
|
+
Type: `oneshot`,
|
|
289
|
+
ExecStart: backupJob,
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const timer = new SystemdTimer({
|
|
294
|
+
name: `backup-db`,
|
|
295
|
+
timer: {
|
|
296
|
+
OnCalendar: `daily`,
|
|
297
|
+
Persistent: true,
|
|
298
|
+
Unit: service.filename,
|
|
299
|
+
},
|
|
300
|
+
install: {
|
|
301
|
+
WantedBy: `timers.target`,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
52
305
|
const materialized = await systemd.materialize(service, timer);
|
|
53
306
|
if (!materialized.ok) {
|
|
54
307
|
throw materialized.error;
|
|
55
308
|
}
|
|
56
309
|
|
|
57
|
-
console.log(systemd.pathFor(service));
|
|
58
|
-
console.log(systemd.pathFor(timer));
|
|
59
|
-
console.log(materialized.value.materialized);
|
|
60
|
-
|
|
61
310
|
const enabled = await systemd.enable(timer);
|
|
62
311
|
if (!enabled.ok) {
|
|
63
312
|
throw enabled.error;
|
|
@@ -69,6 +318,18 @@ if (!started.ok) {
|
|
|
69
318
|
}
|
|
70
319
|
```
|
|
71
320
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
321
|
+
This lower-level flow is useful when you want exact control over
|
|
322
|
+
materialization, enablement, or startup behavior without going through the
|
|
323
|
+
code-owned upkeep layer.
|
|
324
|
+
|
|
325
|
+
## Explore Further
|
|
326
|
+
|
|
327
|
+
The README is meant to answer what the library is for.
|
|
328
|
+
|
|
329
|
+
If you want the exact method surface, result types, or lower-level details, the
|
|
330
|
+
next places to look are:
|
|
331
|
+
|
|
332
|
+
- `Systemctl` for direct `systemctl` usage from TypeScript
|
|
333
|
+
- `Systemd` for materialization plus unit lifecycle workflows
|
|
334
|
+
- `SystemdService` and `SystemdTimer` for unit definitions
|
|
335
|
+
- `Executable` and `defineExecutable()` for colocated runnable entrypoints
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare namespace internal_d_exports {
|
|
2
|
-
export { Result, cloneUnitSection, defaultCommandExecutor, defaultUnitDirForScope, fileExists, freezeUnitOptions, normalizeUnitName, parseStartStatus, renderUnitFile, resolveTimerTargetUnit, sendNotify,
|
|
2
|
+
export { Result, cloneUnitSection, defaultCommandExecutor, defaultUnitDirForScope, extractCommandOutput, fileExists, freezeUnitOptions, normalizeUnitName, parseStartStatus, renderUnitFile, resolveTimerTargetUnit, sendNotify, validateServiceSection };
|
|
3
3
|
}
|
|
4
4
|
type Result<TValue, TError> = {
|
|
5
5
|
readonly ok: true;
|
|
@@ -17,10 +17,10 @@ declare function cloneUnitSection<TSection extends SectionLike | undefined>(sect
|
|
|
17
17
|
declare function validateServiceSection(service: SystemdServiceSection): Result<void, InvalidExecDirectiveError>;
|
|
18
18
|
declare function parseStartStatus(unit: string, output: string): StartStatus;
|
|
19
19
|
declare function renderUnitFile(sections: ReadonlyArray<readonly [string, SectionLike | undefined]>): Result<string, ExecutableInferenceError>;
|
|
20
|
-
declare function
|
|
21
|
-
declare function defaultCommandExecutor(command: string, args: readonly string[]): Promise<CommandOutput>;
|
|
20
|
+
declare function defaultCommandExecutor(command: string, args: readonly string[], options?: CommandExecutionOptions): Promise<CommandOutput>;
|
|
22
21
|
declare function fileExists(path: string): Promise<boolean>;
|
|
23
22
|
declare function sendNotify(state: `READY=1` | `WATCHDOG=1`, options: NotifyOptions): Promise<void>;
|
|
23
|
+
declare function extractCommandOutput(cause: unknown): CommandOutput | undefined;
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/main/executable.d.ts
|
|
26
26
|
/**
|
|
@@ -1086,14 +1086,61 @@ interface SystemdOptions {
|
|
|
1086
1086
|
readonly scope?: `system` | `user`;
|
|
1087
1087
|
readonly unitDir?: string;
|
|
1088
1088
|
}
|
|
1089
|
+
interface SystemctlOptions {
|
|
1090
|
+
readonly executor?: CommandExecutor;
|
|
1091
|
+
readonly scope?: `system` | `user`;
|
|
1092
|
+
}
|
|
1093
|
+
type SystemctlEnablementState = `enabled` | `enabled-runtime` | `linked` | `linked-runtime` | `alias` | `masked` | `masked-runtime` | `static` | `indirect` | `disabled` | `generated` | `transient` | `bad` | `not-found` | (string & {});
|
|
1094
|
+
type SystemctlActiveState = `active` | `reloading` | `inactive` | `failed` | `activating` | `deactivating` | `maintenance` | `refreshing`;
|
|
1089
1095
|
interface CommandOutput {
|
|
1090
1096
|
readonly stderr: string;
|
|
1091
1097
|
readonly stdout: string;
|
|
1092
1098
|
}
|
|
1093
|
-
|
|
1099
|
+
interface CommandExecutionOptions {
|
|
1100
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
1101
|
+
}
|
|
1102
|
+
type CommandExecutor = (command: string, args: readonly string[], options?: CommandExecutionOptions) => Promise<CommandOutput>;
|
|
1094
1103
|
interface LogsOptions {
|
|
1095
1104
|
readonly lines?: number;
|
|
1096
1105
|
}
|
|
1106
|
+
interface SystemctlShowOptions {
|
|
1107
|
+
readonly properties?: readonly string[];
|
|
1108
|
+
}
|
|
1109
|
+
interface SystemctlStatusOptions {
|
|
1110
|
+
readonly lines?: number;
|
|
1111
|
+
readonly noPager?: boolean;
|
|
1112
|
+
}
|
|
1113
|
+
interface SystemctlListTimersOptions {
|
|
1114
|
+
readonly all?: boolean;
|
|
1115
|
+
readonly noPager?: boolean;
|
|
1116
|
+
readonly patterns?: readonly string[];
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* The official `systemctl list-timers --output=json` entry shape exposed by
|
|
1120
|
+
* this library.
|
|
1121
|
+
*
|
|
1122
|
+
* Timestamp fields are returned as raw integer values exactly as reported by
|
|
1123
|
+
* `systemctl`, so callers can compose their own normalization strategy.
|
|
1124
|
+
*/
|
|
1125
|
+
interface SystemctlTimerListEntry {
|
|
1126
|
+
readonly next: number;
|
|
1127
|
+
readonly left: number;
|
|
1128
|
+
readonly last: number;
|
|
1129
|
+
readonly passed: number;
|
|
1130
|
+
readonly unit: `${string}.timer`;
|
|
1131
|
+
readonly activates: string;
|
|
1132
|
+
}
|
|
1133
|
+
interface SystemctlIsEnabledOptions {
|
|
1134
|
+
readonly full?: boolean;
|
|
1135
|
+
readonly quiet?: boolean;
|
|
1136
|
+
}
|
|
1137
|
+
interface SystemctlStateQueryOptions {
|
|
1138
|
+
readonly quiet?: boolean;
|
|
1139
|
+
}
|
|
1140
|
+
interface SystemctlIsSystemRunningOptions {
|
|
1141
|
+
readonly wait?: boolean;
|
|
1142
|
+
}
|
|
1143
|
+
type SystemctlSystemRunningState = `initializing` | `starting` | `running` | `degraded` | `maintenance` | `stopping` | `offline` | `unknown`;
|
|
1097
1144
|
interface NotifyOptions {
|
|
1098
1145
|
readonly executor?: CommandExecutor;
|
|
1099
1146
|
readonly pid?: number;
|
|
@@ -1107,6 +1154,13 @@ interface StartStatus {
|
|
|
1107
1154
|
readonly subState: string;
|
|
1108
1155
|
readonly unit: string;
|
|
1109
1156
|
}
|
|
1157
|
+
/**
|
|
1158
|
+
* The typed `systemctl show` status projection exposed for service units.
|
|
1159
|
+
*
|
|
1160
|
+
* This currently mirrors the shared start/status snapshot shape used by the
|
|
1161
|
+
* library's service workflows.
|
|
1162
|
+
*/
|
|
1163
|
+
interface SystemctlServiceStatus extends StartStatus {}
|
|
1110
1164
|
interface ExecutableOptions {
|
|
1111
1165
|
readonly args?: readonly string[];
|
|
1112
1166
|
readonly modulePath?: string;
|
|
@@ -1137,7 +1191,7 @@ type HasMismatchedServiceTimerPairs<TUnits extends readonly SystemdUnit[]> = Has
|
|
|
1137
1191
|
type ValidInstallUnits<TUnits extends readonly SystemdUnit[]> = HasMismatchedServiceTimerPairs<TUnits> extends true ? never : TUnits;
|
|
1138
1192
|
//#endregion
|
|
1139
1193
|
//#region src/main/errors.d.ts
|
|
1140
|
-
type SystemdTsErrorCode = `SYSTEMD_TS_INVALID_EXEC_DIRECTIVE` | `SYSTEMD_TS_NO_UNITS_PROVIDED` | `SYSTEMD_TS_EXECUTABLE_INFERENCE` | `SYSTEMD_TS_UNIT_MATERIALIZATION` | `SYSTEMD_TS_UNIT_ENABLE` | `SYSTEMD_TS_UNIT_START` | `SYSTEMD_TS_UNIT_LOGS` | `SYSTEMD_TS_NOTIFY_SEND`;
|
|
1194
|
+
type SystemdTsErrorCode = `SYSTEMD_TS_INVALID_EXEC_DIRECTIVE` | `SYSTEMD_TS_NO_UNITS_PROVIDED` | `SYSTEMD_TS_EXECUTABLE_INFERENCE` | `SYSTEMD_TS_UNIT_MATERIALIZATION` | `SYSTEMD_TS_UNIT_ENABLE` | `SYSTEMD_TS_UNIT_START` | `SYSTEMD_TS_UNIT_LOGS` | `SYSTEMD_TS_SYSTEMCTL` | `SYSTEMD_TS_NOTIFY_SEND`;
|
|
1141
1195
|
interface SystemdTsErrorOptions {
|
|
1142
1196
|
readonly cause?: unknown;
|
|
1143
1197
|
}
|
|
@@ -1173,6 +1227,11 @@ interface NotifySendErrorOptions extends SystemdCommandErrorOptions {
|
|
|
1173
1227
|
readonly reason?: NotifySendErrorReason;
|
|
1174
1228
|
}
|
|
1175
1229
|
type NotifySendErrorReason = `executor-failed` | `systemd-notify-failed`;
|
|
1230
|
+
interface SystemctlCommandErrorOptions extends SystemdCommandErrorOptions {
|
|
1231
|
+
readonly operation?: string;
|
|
1232
|
+
readonly reason?: SystemctlCommandErrorReason;
|
|
1233
|
+
}
|
|
1234
|
+
type SystemctlCommandErrorReason = `executor-failed` | `invalid-json`;
|
|
1176
1235
|
type SystemdCommandEnvironmentReason = `manager-unavailable` | `missing-command` | `permission-denied`;
|
|
1177
1236
|
declare class SystemdTsError extends Error {
|
|
1178
1237
|
readonly code: SystemdTsErrorCode;
|
|
@@ -1245,6 +1304,18 @@ declare class NotifySendError extends SystemdTsError {
|
|
|
1245
1304
|
readonly stdout: string | undefined;
|
|
1246
1305
|
constructor(message: string, options?: NotifySendErrorOptions);
|
|
1247
1306
|
}
|
|
1307
|
+
declare class SystemctlCommandError extends SystemdTsError {
|
|
1308
|
+
readonly args: readonly string[] | undefined;
|
|
1309
|
+
readonly command: string | undefined;
|
|
1310
|
+
readonly environmentReason: SystemdCommandEnvironmentReason | undefined;
|
|
1311
|
+
readonly exitCode: number | undefined;
|
|
1312
|
+
readonly operation: string | undefined;
|
|
1313
|
+
readonly reason: SystemctlCommandErrorReason | undefined;
|
|
1314
|
+
readonly stage: string | undefined;
|
|
1315
|
+
readonly stderr: string | undefined;
|
|
1316
|
+
readonly stdout: string | undefined;
|
|
1317
|
+
constructor(message: string, options?: SystemctlCommandErrorOptions);
|
|
1318
|
+
}
|
|
1248
1319
|
//#endregion
|
|
1249
1320
|
//#region src/main/notify.d.ts
|
|
1250
1321
|
/**
|
|
@@ -1278,7 +1349,138 @@ declare const notify: {
|
|
|
1278
1349
|
watchdog(options?: NotifyOptions): Promise<Result<void, NotifySendError>>;
|
|
1279
1350
|
};
|
|
1280
1351
|
//#endregion
|
|
1352
|
+
//#region src/main/systemctl.d.ts
|
|
1353
|
+
/**
|
|
1354
|
+
* A TypeScript-first client for invoking `systemctl` with structured argument
|
|
1355
|
+
* vectors instead of shell-composed command strings.
|
|
1356
|
+
*
|
|
1357
|
+
* This lower-level primitive is useful both on its own and as the subprocess
|
|
1358
|
+
* transport underneath higher-level abstractions such as {@link Systemd}.
|
|
1359
|
+
*/
|
|
1360
|
+
declare class Systemctl {
|
|
1361
|
+
/** Command executor used for `systemctl` subprocesses. */
|
|
1362
|
+
readonly executor: CommandExecutor;
|
|
1363
|
+
/** The target manager scope, either `system` or `user`. */
|
|
1364
|
+
readonly scope: `system` | `user`;
|
|
1365
|
+
constructor(options?: SystemctlOptions);
|
|
1366
|
+
/** Reloads the targeted systemd manager. */
|
|
1367
|
+
daemonReload(): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1368
|
+
/** Enables a unit by filename. */
|
|
1369
|
+
enable(unit: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1370
|
+
/** Disables a unit by filename. */
|
|
1371
|
+
disable(unit: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1372
|
+
/** Links a unit file path into the targeted manager. */
|
|
1373
|
+
link(path: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1374
|
+
/**
|
|
1375
|
+
* Checks the install-time enablement state of a unit file.
|
|
1376
|
+
*
|
|
1377
|
+
* Source:
|
|
1378
|
+
* - systemd v260.1, `systemctl(1)`
|
|
1379
|
+
*/
|
|
1380
|
+
isEnabled(unit: string, options?: SystemctlIsEnabledOptions): Promise<Result<SystemctlEnablementState, SystemctlCommandError>>;
|
|
1381
|
+
/**
|
|
1382
|
+
* Checks the runtime active state of a unit.
|
|
1383
|
+
*
|
|
1384
|
+
* Unless `quiet` is set, this returns the state string printed by
|
|
1385
|
+
* `systemctl is-active`.
|
|
1386
|
+
*/
|
|
1387
|
+
isActive(unit: string, options?: SystemctlStateQueryOptions): Promise<Result<SystemctlActiveState, SystemctlCommandError>>;
|
|
1388
|
+
/**
|
|
1389
|
+
* Checks whether a unit is in the failed state.
|
|
1390
|
+
*
|
|
1391
|
+
* Unless `quiet` is set, this returns the state string printed by
|
|
1392
|
+
* `systemctl is-failed`.
|
|
1393
|
+
*/
|
|
1394
|
+
isFailed(unit: string, options?: SystemctlStateQueryOptions): Promise<Result<SystemctlActiveState, SystemctlCommandError>>;
|
|
1395
|
+
/**
|
|
1396
|
+
* Queries the overall manager state reported by `systemctl is-system-running`.
|
|
1397
|
+
*
|
|
1398
|
+
* Source:
|
|
1399
|
+
* - systemd v260.1, `systemctl(1)`
|
|
1400
|
+
*/
|
|
1401
|
+
isSystemRunning(options?: SystemctlIsSystemRunningOptions): Promise<Result<SystemctlSystemRunningState, SystemctlCommandError>>;
|
|
1402
|
+
/**
|
|
1403
|
+
* Queries `systemctl show` for a unit.
|
|
1404
|
+
*
|
|
1405
|
+
* When `properties` are provided, only those properties are requested.
|
|
1406
|
+
*/
|
|
1407
|
+
show(unit: string, options?: SystemctlShowOptions): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Queries `systemctl show` and parses the result into a key-value object.
|
|
1410
|
+
*
|
|
1411
|
+
* Properties are returned exactly as printed by `systemctl show`.
|
|
1412
|
+
*/
|
|
1413
|
+
showProperties(unit: string, options?: SystemctlShowOptions): Promise<Result<Readonly<Record<string, string>>, SystemctlCommandError>>;
|
|
1414
|
+
/**
|
|
1415
|
+
* Queries the standard status-related `show` properties for a unit and
|
|
1416
|
+
* returns a parsed snapshot.
|
|
1417
|
+
*/
|
|
1418
|
+
showStatus(unit: string): Promise<Result<StartStatus, SystemctlCommandError>>;
|
|
1419
|
+
/**
|
|
1420
|
+
* Queries the standard service-status `show` properties for a service unit
|
|
1421
|
+
* and returns a parsed snapshot.
|
|
1422
|
+
*/
|
|
1423
|
+
showServiceStatus(unit: string): Promise<Result<SystemctlServiceStatus, SystemctlCommandError>>;
|
|
1424
|
+
/**
|
|
1425
|
+
* Queries `systemctl list-timers` and returns the observed JSON payload
|
|
1426
|
+
* shape directly, with raw integer timestamps left intact.
|
|
1427
|
+
*/
|
|
1428
|
+
listTimers(options?: SystemctlListTimersOptions): Promise<Result<readonly SystemctlTimerListEntry[], SystemctlCommandError>>;
|
|
1429
|
+
/** Starts a unit by filename. */
|
|
1430
|
+
start(unit: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1431
|
+
/** Stops a unit by filename. */
|
|
1432
|
+
stop(unit: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1433
|
+
/** Restarts a unit by filename. */
|
|
1434
|
+
restart(unit: string): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1435
|
+
/**
|
|
1436
|
+
* Resets the failed state for matching units, or for all units when no
|
|
1437
|
+
* patterns are provided.
|
|
1438
|
+
*/
|
|
1439
|
+
resetFailed(patterns?: readonly string[]): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1440
|
+
/**
|
|
1441
|
+
* Queries `systemctl status` for a unit.
|
|
1442
|
+
*
|
|
1443
|
+
* This is primarily a human-oriented command and is best paired with
|
|
1444
|
+
* `show()` when structured state is needed.
|
|
1445
|
+
*/
|
|
1446
|
+
status(unit: string, options?: SystemctlStatusOptions): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1447
|
+
/** Executes a raw `systemctl` invocation with scope-aware arguments. */
|
|
1448
|
+
run(...args: readonly string[]): Promise<Result<CommandOutput, SystemctlCommandError>>;
|
|
1449
|
+
private scopeArgs;
|
|
1450
|
+
}
|
|
1451
|
+
//#endregion
|
|
1281
1452
|
//#region src/main/systemd.d.ts
|
|
1453
|
+
interface SystemdTsAttachOptions {
|
|
1454
|
+
readonly owner: string;
|
|
1455
|
+
readonly enable?: boolean;
|
|
1456
|
+
readonly start?: boolean;
|
|
1457
|
+
}
|
|
1458
|
+
interface SystemdTsDetachOptions {
|
|
1459
|
+
readonly deleteUnitFiles?: boolean;
|
|
1460
|
+
readonly disable?: boolean;
|
|
1461
|
+
readonly stop?: boolean;
|
|
1462
|
+
}
|
|
1463
|
+
interface SystemdTsReattachOptions extends SystemdTsAttachOptions {
|
|
1464
|
+
readonly disableRemoved?: boolean;
|
|
1465
|
+
readonly prune?: boolean;
|
|
1466
|
+
readonly restartUpdated?: boolean;
|
|
1467
|
+
readonly stopRemoved?: boolean;
|
|
1468
|
+
}
|
|
1469
|
+
interface SystemdTsAttachmentSelector {
|
|
1470
|
+
readonly owner: string;
|
|
1471
|
+
}
|
|
1472
|
+
interface SystemdTsAttachmentResult {
|
|
1473
|
+
readonly added: readonly string[];
|
|
1474
|
+
readonly directory: string;
|
|
1475
|
+
readonly enabled: readonly string[];
|
|
1476
|
+
readonly manifestPath: string;
|
|
1477
|
+
readonly owner: string;
|
|
1478
|
+
readonly removed: readonly string[];
|
|
1479
|
+
readonly restarted: readonly string[];
|
|
1480
|
+
readonly started: readonly string[];
|
|
1481
|
+
readonly unchanged: readonly string[];
|
|
1482
|
+
readonly updated: readonly string[];
|
|
1483
|
+
}
|
|
1282
1484
|
/**
|
|
1283
1485
|
* A successful systemd unit materialization.
|
|
1284
1486
|
*
|
|
@@ -1309,6 +1511,10 @@ declare class Systemd {
|
|
|
1309
1511
|
readonly linkUnits: boolean;
|
|
1310
1512
|
/** The target manager scope, either `system` or `user`. */
|
|
1311
1513
|
readonly scope: `system` | `user`;
|
|
1514
|
+
/** The low-level `systemctl` client for this configured target. */
|
|
1515
|
+
readonly systemctl: Systemctl;
|
|
1516
|
+
/** Higher-level `systemd-ts` upkeep helpers for application-owned units. */
|
|
1517
|
+
readonly ts: SystemdTs;
|
|
1312
1518
|
/** The directory used when materializing unit files. */
|
|
1313
1519
|
readonly unitDir: string;
|
|
1314
1520
|
/**
|
|
@@ -1359,6 +1565,35 @@ declare class Systemd {
|
|
|
1359
1565
|
private materializeUnits;
|
|
1360
1566
|
private tryBestEffortCommand;
|
|
1361
1567
|
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Opinionated `systemd-ts` upkeep helpers for application-owned units.
|
|
1570
|
+
*
|
|
1571
|
+
* This layer is intentionally analogous to a subset of `portablectl`'s
|
|
1572
|
+
* ownership workflow, but it operates on regular materialized units instead of
|
|
1573
|
+
* portable service images.
|
|
1574
|
+
*/
|
|
1575
|
+
declare class SystemdTs {
|
|
1576
|
+
private readonly systemd;
|
|
1577
|
+
constructor(systemd: Systemd);
|
|
1578
|
+
attach(units: readonly SystemdUnit[], options: SystemdTsAttachOptions): Promise<Result<SystemdTsAttachmentResult, ExecutableInferenceError | InvalidExecDirectiveError | NoUnitsProvidedError | UnitEnableError | UnitMaterializationError | UnitStartError>>;
|
|
1579
|
+
detach(selector: SystemdTsAttachmentSelector, options?: SystemdTsDetachOptions): Promise<Result<{
|
|
1580
|
+
readonly deleted: readonly string[];
|
|
1581
|
+
readonly detached: readonly string[];
|
|
1582
|
+
readonly disabled: readonly string[];
|
|
1583
|
+
readonly owner: string;
|
|
1584
|
+
readonly stopped: readonly string[];
|
|
1585
|
+
}, Error>>;
|
|
1586
|
+
reattach(units: readonly SystemdUnit[], options: SystemdTsReattachOptions): Promise<Result<SystemdTsAttachmentResult, ExecutableInferenceError | InvalidExecDirectiveError | NoUnitsProvidedError | UnitEnableError | UnitMaterializationError | UnitStartError | Error>>;
|
|
1587
|
+
private describeUnits;
|
|
1588
|
+
private diffManifest;
|
|
1589
|
+
private manifestDir;
|
|
1590
|
+
private manifestPathFor;
|
|
1591
|
+
private readManifest;
|
|
1592
|
+
private runSystemctl;
|
|
1593
|
+
private removeManagedUnitPath;
|
|
1594
|
+
private scopeArgs;
|
|
1595
|
+
private writeManifest;
|
|
1596
|
+
}
|
|
1362
1597
|
/**
|
|
1363
1598
|
* Returns the lazily created default `Systemd` instance.
|
|
1364
1599
|
*
|
|
@@ -1374,4 +1609,4 @@ declare function defaultSystemd(): Systemd;
|
|
|
1374
1609
|
*/
|
|
1375
1610
|
declare const Internal: typeof internal_d_exports;
|
|
1376
1611
|
//#endregion
|
|
1377
|
-
export { type AnySystemdService, type AnySystemdTimer, type CommandExecutor, type CommandOutput, Executable, ExecutableInferenceError, type ExecutableOptions, Internal, InvalidExecDirectiveError, type LogsOptions, type MaterializedUnit, NoUnitsProvidedError, type NotifyOptions, NotifySendError, type NotifySendErrorReason, type Result, type ServiceBaseName, type ServiceFilename, type StartStatus, Systemd, type SystemdCommandEnvironmentReason, SystemdMaterialization, type SystemdOptions, SystemdService, type SystemdServiceOptions, SystemdTimer, type SystemdTimerOptions, SystemdTsError, type SystemdUnit, type TimerBaseName, type TimerFilename, type TimerTargetServiceName, type TimerTargetUnit, UnitEnableError, UnitLogsReadError, type UnitLogsReadErrorReason, UnitMaterializationError, type UnitMaterializationErrorReason, UnitStartError, type UnitValue, type ValidInstallUnits, defaultSystemd, defineExecutable, notify };
|
|
1612
|
+
export { type AnySystemdService, type AnySystemdTimer, type CommandExecutionOptions, type CommandExecutor, type CommandOutput, Executable, ExecutableInferenceError, type ExecutableOptions, Internal, InvalidExecDirectiveError, type LogsOptions, type MaterializedUnit, NoUnitsProvidedError, type NotifyOptions, NotifySendError, type NotifySendErrorReason, type Result, type ServiceBaseName, type ServiceFilename, type StartStatus, Systemctl, type SystemctlActiveState, SystemctlCommandError, type SystemctlCommandErrorReason, type SystemctlEnablementState, type SystemctlIsEnabledOptions, type SystemctlIsSystemRunningOptions, type SystemctlListTimersOptions, type SystemctlOptions, type SystemctlServiceStatus, type SystemctlShowOptions, type SystemctlStateQueryOptions, type SystemctlStatusOptions, type SystemctlSystemRunningState, type SystemctlTimerListEntry, Systemd, type SystemdCommandEnvironmentReason, SystemdMaterialization, type SystemdOptions, SystemdService, type SystemdServiceOptions, SystemdTimer, type SystemdTimerOptions, SystemdTs, type SystemdTsAttachOptions, type SystemdTsAttachmentResult, type SystemdTsAttachmentSelector, type SystemdTsDetachOptions, SystemdTsError, type SystemdTsReattachOptions, type SystemdUnit, type TimerBaseName, type TimerFilename, type TimerTargetServiceName, type TimerTargetUnit, UnitEnableError, UnitLogsReadError, type UnitLogsReadErrorReason, UnitMaterializationError, type UnitMaterializationErrorReason, UnitStartError, type UnitValue, type ValidInstallUnits, defaultSystemd, defineExecutable, notify };
|