systemd-ts 0.1.1 → 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.
Files changed (4) hide show
  1. package/README.md +297 -25
  2. package/dist/index.d.mts +461 -117
  3. package/dist/index.mjs +1179 -242
  4. package/package.json +2 -2
package/dist/index.d.mts CHANGED
@@ -1,3 +1,96 @@
1
+ declare namespace internal_d_exports {
2
+ export { Result, cloneUnitSection, defaultCommandExecutor, defaultUnitDirForScope, extractCommandOutput, fileExists, freezeUnitOptions, normalizeUnitName, parseStartStatus, renderUnitFile, resolveTimerTargetUnit, sendNotify, validateServiceSection };
3
+ }
4
+ type Result<TValue, TError> = {
5
+ readonly ok: true;
6
+ readonly value: TValue;
7
+ } | {
8
+ readonly ok: false;
9
+ readonly error: TError;
10
+ };
11
+ type SectionLike = object;
12
+ declare function normalizeUnitName(name: string, suffix: `.service` | `.timer`): string;
13
+ declare function resolveTimerTargetUnit(options: SystemdTimerOptions): string;
14
+ declare function defaultUnitDirForScope(scope: `system` | `user`): string;
15
+ declare function freezeUnitOptions<TOptions extends SystemdServiceOptions | SystemdTimerOptions>(options: TOptions): Readonly<TOptions>;
16
+ declare function cloneUnitSection<TSection extends SectionLike | undefined>(section: TSection): TSection;
17
+ declare function validateServiceSection(service: SystemdServiceSection): Result<void, InvalidExecDirectiveError>;
18
+ declare function parseStartStatus(unit: string, output: string): StartStatus;
19
+ declare function renderUnitFile(sections: ReadonlyArray<readonly [string, SectionLike | undefined]>): Result<string, ExecutableInferenceError>;
20
+ declare function defaultCommandExecutor(command: string, args: readonly string[], options?: CommandExecutionOptions): Promise<CommandOutput>;
21
+ declare function fileExists(path: string): Promise<boolean>;
22
+ declare function sendNotify(state: `READY=1` | `WATCHDOG=1`, options: NotifyOptions): Promise<void>;
23
+ declare function extractCommandOutput(cause: unknown): CommandOutput | undefined;
24
+ //#endregion
25
+ //#region src/main/executable.d.ts
26
+ /**
27
+ * An immutable description of a runnable module entrypoint that can be used in
28
+ * executable-valued systemd unit directives.
29
+ *
30
+ * `Executable` captures three pieces of information:
31
+ * - the absolute runtime binary that should launch the module
32
+ * - the absolute path to the module itself
33
+ * - any additional arguments that should be passed after the module path
34
+ *
35
+ * Most consumers will create one via {@link defineExecutable} and then pass it
36
+ * into a {@link SystemdService} directive such as `ExecStart`, `ExecStop`, or
37
+ * `ExecReload`.
38
+ */
39
+ declare class Executable {
40
+ /** Additional arguments passed after the module path. */
41
+ readonly args: readonly string[];
42
+ /** Inference issue captured when no explicit module path was provided. */
43
+ readonly inferenceError: ExecutableInferenceError | undefined;
44
+ /** Absolute path to the module that should be executed. */
45
+ readonly modulePath: string;
46
+ /** Absolute path to the runtime binary that should launch the module. */
47
+ readonly runtimeEntrypoint: string;
48
+ /**
49
+ * Creates an executable description.
50
+ *
51
+ * When `modulePath` is omitted, the calling module is inferred from the
52
+ * current stack so `defineExecutable()` can be used inline from the module
53
+ * that should become runnable.
54
+ *
55
+ * When `runtimeEntrypoint` is omitted, the current process executable is
56
+ * used. This works well for the common case where the same runtime that is
57
+ * evaluating your module should also be used by systemd.
58
+ */
59
+ constructor(options?: ExecutableOptions);
60
+ /**
61
+ * Returns the executable as raw command parts.
62
+ *
63
+ * The first element is always the runtime entrypoint, followed by the module
64
+ * path and any configured arguments.
65
+ */
66
+ toCommandParts(): Result<readonly [string, ...string[]], ExecutableInferenceError>;
67
+ /**
68
+ * Renders the executable as a shell-quoted command string suitable for
69
+ * executable-valued systemd directives such as `ExecStart=`.
70
+ */
71
+ toExecStart(): Result<string, ExecutableInferenceError>;
72
+ }
73
+ /**
74
+ * Defines a module as a runnable executable and returns its immutable
75
+ * `Executable` description.
76
+ *
77
+ * This helper is designed for the pattern:
78
+ *
79
+ * ```ts
80
+ * export default defineExecutable(async () => {
81
+ * // do work here
82
+ * });
83
+ * ```
84
+ *
85
+ * When the defining module is executed as the main entrypoint, `fn` is invoked.
86
+ * When the module is merely imported, `fn` is not run and only the executable
87
+ * description is returned.
88
+ *
89
+ * Pass `options.runtimeEntrypoint` to override the default runtime binary when
90
+ * the current process is not the exact runtime you want systemd to use.
91
+ */
92
+ declare function defineExecutable(fn: () => void | Promise<void>, options?: ExecutableOptions): Executable;
93
+ //#endregion
1
94
  //#region src/main/systemd-service.d.ts
2
95
  /**
3
96
  * An immutable definition of a `.service` unit.
@@ -38,7 +131,7 @@ declare class SystemdService<const TOptions extends SystemdServiceOptions = Syst
38
131
  * and `ExecStop`, ensuring they use absolute runtime entrypoints as required
39
132
  * by systemd.
40
133
  */
41
- render(): string;
134
+ render(): Result<string, ExecutableInferenceError | InvalidExecDirectiveError>;
42
135
  }
43
136
  //#endregion
44
137
  //#region src/main/systemd-timer.d.ts
@@ -77,7 +170,7 @@ declare class SystemdTimer<const TOptions extends SystemdTimerOptions = SystemdT
77
170
  /** The canonical unit filename, including the `.timer` suffix. */
78
171
  get filename(): TimerFilename<TOptions[`name`]>;
79
172
  /** Renders the timer as a complete unit file. */
80
- render(): string;
173
+ render(): Result<string, ExecutableInferenceError>;
81
174
  }
82
175
  //#endregion
83
176
  //#region src/main/types.d.ts
@@ -963,14 +1056,6 @@ interface SystemdServiceSection extends CustomDirectiveSection {
963
1056
  /** Takes a directory path relative to the service's root directory specified by RootDirectory=, or the special value ~. Sets the working directory for executed processes. If set to ~, the home directory of the user specified in User= is used. If not set, defaults to the root directory when systemd is running as a system instance and the respective user's home directory if run as user. If the setting is prefixed with the - character, a missing working directory is not considered fatal. If RootDirectory=/RootImage= is not set, then WorkingDirectory= is relative to the root of the system running the service manager. Note that setting this parameter might result in additional dependencies to be added to the unit (see above). Source: systemd v260.1, `systemd.exec(5)`. */
964
1057
  readonly WorkingDirectory?: string;
965
1058
  }
966
- /**
967
- * @deprecated Use one of the specific section interfaces instead:
968
- * `SystemdUnitSection`, `SystemdInstallSection`, `SystemdServiceSection`, or
969
- * `SystemdTimerSection`.
970
- */
971
- interface UnitSection {
972
- readonly [key: string]: UnitSectionValue;
973
- }
974
1059
  interface SystemdServiceOptions {
975
1060
  readonly install?: SystemdInstallSection;
976
1061
  readonly name: string;
@@ -1001,27 +1086,81 @@ interface SystemdOptions {
1001
1086
  readonly scope?: `system` | `user`;
1002
1087
  readonly unitDir?: string;
1003
1088
  }
1004
- interface CommandResult {
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`;
1095
+ interface CommandOutput {
1005
1096
  readonly stderr: string;
1006
1097
  readonly stdout: string;
1007
1098
  }
1008
- type CommandExecutor = (command: string, args: readonly string[]) => Promise<CommandResult>;
1099
+ interface CommandExecutionOptions {
1100
+ readonly env?: Readonly<Record<string, string | undefined>>;
1101
+ }
1102
+ type CommandExecutor = (command: string, args: readonly string[], options?: CommandExecutionOptions) => Promise<CommandOutput>;
1009
1103
  interface LogsOptions {
1010
1104
  readonly lines?: number;
1011
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`;
1012
1144
  interface NotifyOptions {
1013
1145
  readonly executor?: CommandExecutor;
1014
1146
  readonly pid?: number;
1015
1147
  readonly socketPath?: string;
1016
1148
  readonly status?: string;
1017
1149
  }
1018
- interface StartResult {
1150
+ interface StartStatus {
1019
1151
  readonly activeState: string;
1020
1152
  readonly execMainStatus: number | undefined;
1021
1153
  readonly result: string;
1022
1154
  readonly subState: string;
1023
1155
  readonly unit: string;
1024
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 {}
1025
1164
  interface ExecutableOptions {
1026
1165
  readonly args?: readonly string[];
1027
1166
  readonly modulePath?: string;
@@ -1040,7 +1179,7 @@ type IsWideString<Value extends string> = string extends Value ? true : false;
1040
1179
  type AnySystemdService = SystemdService<SystemdServiceOptions>;
1041
1180
  type AnySystemdTimer = SystemdTimer<SystemdTimerOptions>;
1042
1181
  type SystemdUnit = AnySystemdService | AnySystemdTimer;
1043
- interface InstalledUnit<TUnit extends SystemdUnit = SystemdUnit> {
1182
+ interface MaterializedUnit<TUnit extends SystemdUnit = SystemdUnit> {
1044
1183
  readonly path: string;
1045
1184
  readonly unit: TUnit;
1046
1185
  }
@@ -1051,72 +1190,132 @@ type HasServices<TUnits extends readonly SystemdUnit[]> = [ServiceNamesIn<TUnits
1051
1190
  type HasMismatchedServiceTimerPairs<TUnits extends readonly SystemdUnit[]> = HasServices<TUnits> extends true ? [MismatchedTimers<TUnits>] extends [never] ? false : true : false;
1052
1191
  type ValidInstallUnits<TUnits extends readonly SystemdUnit[]> = HasMismatchedServiceTimerPairs<TUnits> extends true ? never : TUnits;
1053
1192
  //#endregion
1054
- //#region src/main/executable.d.ts
1055
- /**
1056
- * An immutable description of a runnable module entrypoint that can be used in
1057
- * executable-valued systemd unit directives.
1058
- *
1059
- * `Executable` captures three pieces of information:
1060
- * - the absolute runtime binary that should launch the module
1061
- * - the absolute path to the module itself
1062
- * - any additional arguments that should be passed after the module path
1063
- *
1064
- * Most consumers will create one via {@link defineExecutable} and then pass it
1065
- * into a {@link SystemdService} directive such as `ExecStart`, `ExecStop`, or
1066
- * `ExecReload`.
1067
- */
1068
- declare class Executable {
1069
- /** Additional arguments passed after the module path. */
1070
- readonly args: readonly string[];
1071
- /** Absolute path to the module that should be executed. */
1072
- readonly modulePath: string;
1073
- /** Absolute path to the runtime binary that should launch the module. */
1074
- readonly runtimeEntrypoint: string;
1075
- /**
1076
- * Creates an executable description.
1077
- *
1078
- * When `modulePath` is omitted, the calling module is inferred from the
1079
- * current stack so `defineExecutable()` can be used inline from the module
1080
- * that should become runnable.
1081
- *
1082
- * When `runtimeEntrypoint` is omitted, the current process executable is
1083
- * used. This works well for the common case where the same runtime that is
1084
- * evaluating your module should also be used by systemd.
1085
- */
1086
- constructor(options?: ExecutableOptions);
1087
- /**
1088
- * Returns the executable as raw command parts.
1089
- *
1090
- * The first element is always the runtime entrypoint, followed by the module
1091
- * path and any configured arguments.
1092
- */
1093
- toCommandParts(): readonly [string, ...string[]];
1094
- /**
1095
- * Renders the executable as a shell-quoted command string suitable for
1096
- * executable-valued systemd directives such as `ExecStart=`.
1097
- */
1098
- toExecStart(): string;
1193
+ //#region src/main/errors.d.ts
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`;
1195
+ interface SystemdTsErrorOptions {
1196
+ readonly cause?: unknown;
1197
+ }
1198
+ interface SystemdCommandErrorOptions extends SystemdTsErrorOptions {
1199
+ readonly args?: readonly string[];
1200
+ readonly command?: string;
1201
+ readonly environmentReason?: SystemdCommandEnvironmentReason;
1202
+ readonly stage?: string;
1203
+ readonly unitName?: string;
1204
+ readonly unitPath?: string;
1205
+ }
1206
+ interface UnitStartDiagnostics {
1207
+ readonly showOutput?: string;
1208
+ readonly showStatus?: StartStatus;
1209
+ readonly statusOutput?: string;
1210
+ }
1211
+ interface UnitStartErrorOptions extends SystemdCommandErrorOptions {
1212
+ readonly diagnostics?: UnitStartDiagnostics;
1213
+ }
1214
+ interface UnitMaterializationErrorOptions extends SystemdTsErrorOptions {
1215
+ readonly reason?: UnitMaterializationErrorReason;
1216
+ readonly operation?: `create-directory` | `write-file`;
1217
+ readonly unitName?: string;
1218
+ readonly unitPath?: string;
1219
+ }
1220
+ type UnitMaterializationErrorReason = `file-system-failed` | `invalid-unit-directory` | `permission-denied`;
1221
+ interface UnitLogsReadErrorOptions extends SystemdCommandErrorOptions {
1222
+ readonly reason?: UnitLogsReadErrorReason;
1223
+ readonly unitPath?: string;
1224
+ }
1225
+ type UnitLogsReadErrorReason = `missing-log-file` | `log-file-read-failed` | `status-command-failed`;
1226
+ interface NotifySendErrorOptions extends SystemdCommandErrorOptions {
1227
+ readonly reason?: NotifySendErrorReason;
1228
+ }
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`;
1235
+ type SystemdCommandEnvironmentReason = `manager-unavailable` | `missing-command` | `permission-denied`;
1236
+ declare class SystemdTsError extends Error {
1237
+ readonly code: SystemdTsErrorCode;
1238
+ constructor(code: SystemdTsErrorCode, message: string, options?: SystemdTsErrorOptions);
1239
+ }
1240
+ declare class InvalidExecDirectiveError extends SystemdTsError {
1241
+ readonly directive: string;
1242
+ constructor(directive: string, options?: SystemdTsErrorOptions);
1243
+ }
1244
+ declare class NoUnitsProvidedError extends SystemdTsError {
1245
+ readonly operation: string;
1246
+ constructor(operation: string, options?: SystemdTsErrorOptions);
1247
+ }
1248
+ declare class ExecutableInferenceError extends SystemdTsError {
1249
+ constructor(options?: SystemdTsErrorOptions);
1250
+ }
1251
+ declare class UnitMaterializationError extends SystemdTsError {
1252
+ readonly operation: `create-directory` | `write-file` | undefined;
1253
+ readonly reason: UnitMaterializationErrorReason | undefined;
1254
+ readonly unitName: string | undefined;
1255
+ readonly unitPath: string | undefined;
1256
+ constructor(message: string, options?: UnitMaterializationErrorOptions);
1257
+ }
1258
+ declare class UnitEnableError extends SystemdTsError {
1259
+ readonly args: readonly string[] | undefined;
1260
+ readonly command: string | undefined;
1261
+ readonly environmentReason: SystemdCommandEnvironmentReason | undefined;
1262
+ readonly exitCode: number | undefined;
1263
+ readonly stage: string | undefined;
1264
+ readonly stderr: string | undefined;
1265
+ readonly stdout: string | undefined;
1266
+ readonly unitName: string | undefined;
1267
+ readonly unitPath: string | undefined;
1268
+ constructor(message: string, options?: SystemdCommandErrorOptions);
1269
+ }
1270
+ declare class UnitStartError extends SystemdTsError {
1271
+ readonly args: readonly string[] | undefined;
1272
+ readonly command: string | undefined;
1273
+ readonly diagnostics: UnitStartDiagnostics | undefined;
1274
+ readonly environmentReason: SystemdCommandEnvironmentReason | undefined;
1275
+ readonly exitCode: number | undefined;
1276
+ readonly stage: string | undefined;
1277
+ readonly stderr: string | undefined;
1278
+ readonly stdout: string | undefined;
1279
+ readonly unitName: string | undefined;
1280
+ readonly unitPath: string | undefined;
1281
+ constructor(message: string, options?: UnitStartErrorOptions);
1282
+ }
1283
+ declare class UnitLogsReadError extends SystemdTsError {
1284
+ readonly args: readonly string[] | undefined;
1285
+ readonly command: string | undefined;
1286
+ readonly environmentReason: SystemdCommandEnvironmentReason | undefined;
1287
+ readonly exitCode: number | undefined;
1288
+ readonly reason: UnitLogsReadErrorReason | undefined;
1289
+ readonly stage: string | undefined;
1290
+ readonly stderr: string | undefined;
1291
+ readonly stdout: string | undefined;
1292
+ readonly unitName: string | undefined;
1293
+ readonly unitPath: string | undefined;
1294
+ constructor(message: string, options?: UnitLogsReadErrorOptions);
1295
+ }
1296
+ declare class NotifySendError extends SystemdTsError {
1297
+ readonly args: readonly string[] | undefined;
1298
+ readonly command: string | undefined;
1299
+ readonly environmentReason: SystemdCommandEnvironmentReason | undefined;
1300
+ readonly exitCode: number | undefined;
1301
+ readonly reason: NotifySendErrorReason | undefined;
1302
+ readonly stage: string | undefined;
1303
+ readonly stderr: string | undefined;
1304
+ readonly stdout: string | undefined;
1305
+ constructor(message: string, options?: NotifySendErrorOptions);
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);
1099
1318
  }
1100
- /**
1101
- * Defines a module as a runnable executable and returns its immutable
1102
- * `Executable` description.
1103
- *
1104
- * This helper is designed for the pattern:
1105
- *
1106
- * ```ts
1107
- * export default defineExecutable(async () => {
1108
- * // do work here
1109
- * });
1110
- * ```
1111
- *
1112
- * When the defining module is executed as the main entrypoint, `fn` is invoked.
1113
- * When the module is merely imported, `fn` is not run and only the executable
1114
- * description is returned.
1115
- *
1116
- * Pass `options.runtimeEntrypoint` to override the default runtime binary when
1117
- * the current process is not the exact runtime you want systemd to use.
1118
- */
1119
- declare function defineExecutable(fn: () => void | Promise<void>, options?: ExecutableOptions): Executable;
1120
1319
  //#endregion
1121
1320
  //#region src/main/notify.d.ts
1122
1321
  /**
@@ -1139,7 +1338,7 @@ declare const notify: {
1139
1338
  * ready to be considered fully started. If `options.status` is provided, it is
1140
1339
  * sent as an additional `STATUS=...` field.
1141
1340
  */
1142
- ready(options?: NotifyOptions): Promise<void>;
1341
+ ready(options?: NotifyOptions): Promise<Result<void, NotifySendError>>;
1143
1342
  /**
1144
1343
  * Sends `WATCHDOG=1` to systemd.
1145
1344
  *
@@ -1147,25 +1346,153 @@ declare const notify: {
1147
1346
  * watchdog heartbeat. If `options.pid` is provided, it is sent as
1148
1347
  * `MAINPID=...`, and `options.status` is forwarded as `STATUS=...`.
1149
1348
  */
1150
- watchdog(options?: NotifyOptions): Promise<void>;
1349
+ watchdog(options?: NotifyOptions): Promise<Result<void, NotifySendError>>;
1151
1350
  };
1152
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
1153
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
+ }
1154
1484
  /**
1155
- * The result of installing one or more units with {@link Systemd.install}.
1485
+ * A successful systemd unit materialization.
1156
1486
  *
1157
- * It records the installation directory and the on-disk path associated with
1158
- * each installed unit.
1487
+ * It records the target directory and the on-disk path associated with each
1488
+ * materialized unit.
1159
1489
  */
1160
- declare class SystemdInstallResult<TUnits extends readonly SystemdUnit[] = readonly SystemdUnit[]> {
1490
+ declare class SystemdMaterialization<TUnits extends readonly SystemdUnit[] = readonly SystemdUnit[]> {
1161
1491
  /** The directory units were written into. */
1162
1492
  readonly directory: string;
1163
- /** The installed units together with their resolved on-disk paths. */
1164
- readonly installed: readonly InstalledUnit<TUnits[number]>[];
1165
- private readonly pathByFilename;
1166
- constructor(directory: string, installed: readonly InstalledUnit<TUnits[number]>[]);
1167
- /** Returns the installed on-disk path for a previously installed unit. */
1168
- pathFor(unit: TUnits[number]): string;
1493
+ /** The materialized units together with their resolved on-disk paths. */
1494
+ readonly materialized: readonly MaterializedUnit<TUnits[number]>[];
1495
+ constructor(directory: string, materialized: readonly MaterializedUnit<TUnits[number]>[]);
1169
1496
  }
1170
1497
  /**
1171
1498
  * A configured interface to a specific systemd environment.
@@ -1184,6 +1511,10 @@ declare class Systemd {
1184
1511
  readonly linkUnits: boolean;
1185
1512
  /** The target manager scope, either `system` or `user`. */
1186
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;
1187
1518
  /** The directory used when materializing unit files. */
1188
1519
  readonly unitDir: string;
1189
1520
  /**
@@ -1198,19 +1529,17 @@ declare class Systemd {
1198
1529
  * Renders and writes one or more units into this instance's `unitDir`.
1199
1530
  *
1200
1531
  * This is intentionally a file-materialization step. It does not enable or
1201
- * start the units on its own. When both timers and services are installed
1202
- * together, compile-time and runtime attachment checks ensure the timer points
1203
- * at one of the accompanying services.
1532
+ * start the units on its own.
1204
1533
  */
1205
- install<const TUnits extends readonly SystemdUnit[]>(...units: TUnits & ValidInstallUnits<TUnits>): Promise<SystemdInstallResult<TUnits>>;
1534
+ materialize<const TUnits extends readonly SystemdUnit[]>(...units: TUnits & ValidInstallUnits<TUnits>): Promise<Result<SystemdMaterialization<TUnits>, ExecutableInferenceError | InvalidExecDirectiveError | NoUnitsProvidedError | UnitMaterializationError>>;
1206
1535
  /**
1207
1536
  * Enables one or more units via `systemctl enable`.
1208
1537
  *
1209
- * When `linkUnits` is enabled, this first links installed unit files into the
1538
+ * When `linkUnits` is enabled, this first links materialized unit files into the
1210
1539
  * target manager and reloads systemd so the units are visible before the
1211
1540
  * enable operation runs.
1212
1541
  */
1213
- enable(...units: readonly SystemdUnit[]): Promise<void>;
1542
+ enable(...units: readonly SystemdUnit[]): Promise<Result<void, NoUnitsProvidedError | UnitEnableError>>;
1214
1543
  /**
1215
1544
  * Starts a unit and returns a parsed `systemctl show` snapshot of its final
1216
1545
  * observed state.
@@ -1218,7 +1547,7 @@ declare class Systemd {
1218
1547
  * For oneshot services, a successful result commonly means `ActiveState` is
1219
1548
  * already back to `inactive` by the time the status snapshot is collected.
1220
1549
  */
1221
- start(unit: SystemdUnit): Promise<StartResult>;
1550
+ start(unit: SystemdUnit): Promise<Result<StartStatus, UnitStartError>>;
1222
1551
  /**
1223
1552
  * Reads recent output for a managed unit.
1224
1553
  *
@@ -1226,12 +1555,44 @@ declare class Systemd {
1226
1555
  * `StandardError`, this reads directly from that file. Otherwise it falls back
1227
1556
  * to `systemctl status --lines ...`.
1228
1557
  */
1229
- logs(unit: SystemdUnit, options?: LogsOptions): Promise<string>;
1558
+ logs(unit: SystemdUnit, options?: LogsOptions): Promise<Result<string, UnitLogsReadError>>;
1230
1559
  /** Returns the on-disk unit-file path this instance uses for the given unit. */
1231
1560
  pathFor(unit: SystemdUnit): string;
1232
1561
  private prepareUnits;
1233
1562
  private scopeArgs;
1563
+ private collectStartDiagnostics;
1234
1564
  private collectLinkPaths;
1565
+ private materializeUnits;
1566
+ private tryBestEffortCommand;
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;
1235
1596
  }
1236
1597
  /**
1237
1598
  * Returns the lazily created default `Systemd` instance.
@@ -1240,23 +1601,6 @@ declare class Systemd {
1240
1601
  * command execution.
1241
1602
  */
1242
1603
  declare function defaultSystemd(): Systemd;
1243
- declare namespace internal_d_exports {
1244
- export { assertInstallableTogether, cloneUnitSection, defaultCommandExecutor, defaultUnitDirForScope, fileExists, freezeUnitOptions, normalizeUnitName, parseStartResult, renderUnitFile, resolveTimerTargetUnit, sendNotify, shellQuote, validateServiceSection };
1245
- }
1246
- type SectionLike = object;
1247
- declare function normalizeUnitName(name: string, suffix: `.service` | `.timer`): string;
1248
- declare function resolveTimerTargetUnit(options: SystemdTimerOptions): string;
1249
- declare function defaultUnitDirForScope(scope: `system` | `user`): string;
1250
- declare function freezeUnitOptions<TOptions extends SystemdServiceOptions | SystemdTimerOptions>(options: TOptions): Readonly<TOptions>;
1251
- declare function cloneUnitSection<TSection extends SectionLike | undefined>(section: TSection): TSection;
1252
- declare function validateServiceSection(service: SystemdServiceSection): void;
1253
- declare function assertInstallableTogether(units: readonly SystemdUnit[]): void;
1254
- declare function parseStartResult(unit: string, output: string): StartResult;
1255
- declare function renderUnitFile(sections: ReadonlyArray<readonly [string, SectionLike | undefined]>): string;
1256
- declare function shellQuote(value: string): string;
1257
- declare function defaultCommandExecutor(command: string, args: readonly string[]): Promise<CommandResult>;
1258
- declare function fileExists(path: string): Promise<boolean>;
1259
- declare function sendNotify(state: `READY=1` | `WATCHDOG=1`, options: NotifyOptions): Promise<void>;
1260
1604
  //#endregion
1261
1605
  //#region src/main/index.d.ts
1262
1606
  /**
@@ -1265,4 +1609,4 @@ declare function sendNotify(state: `READY=1` | `WATCHDOG=1`, options: NotifyOpti
1265
1609
  */
1266
1610
  declare const Internal: typeof internal_d_exports;
1267
1611
  //#endregion
1268
- export { type AnySystemdService, type AnySystemdTimer, type CommandExecutor, type CommandResult, Executable, type ExecutableOptions, type InstalledUnit, Internal, type LogsOptions, type NotifyOptions, type ServiceBaseName, type ServiceFilename, type StartResult, Systemd, SystemdInstallResult, type SystemdOptions, SystemdService, type SystemdServiceOptions, SystemdTimer, type SystemdTimerOptions, type SystemdUnit, type TimerBaseName, type TimerFilename, type TimerTargetServiceName, type TimerTargetUnit, type UnitSection, 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 };