@push.rocks/smartdaemon 2.22.0 → 3.0.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.22.0',
6
+ version: '3.0.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  };
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHdEQUF3RDtDQUN0RSxDQUFBIn0=
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHdEQUF3RDtDQUN0RSxDQUFBIn0=
@@ -4,3 +4,4 @@ export * from './classes.systemdunit.js';
4
4
  export * from './classes.systemdservicedefinition.js';
5
5
  export * from './classes.systemdunitfile.js';
6
6
  export * from './classes.systemdtransientscope.js';
7
+ export * from './smartdaemon.classes.commanderror.js';
package/dist_ts/index.js CHANGED
@@ -4,4 +4,5 @@ export * from './classes.systemdunit.js';
4
4
  export * from './classes.systemdservicedefinition.js';
5
5
  export * from './classes.systemdunitfile.js';
6
6
  export * from './classes.systemdtransientscope.js';
7
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHNDQUFzQyxDQUFDO0FBQ3JELGNBQWMsa0NBQWtDLENBQUM7QUFDakQsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLHVDQUF1QyxDQUFDO0FBQ3RELGNBQWMsOEJBQThCLENBQUM7QUFDN0MsY0FBYyxvQ0FBb0MsQ0FBQyJ9
7
+ export * from './smartdaemon.classes.commanderror.js';
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHNDQUFzQyxDQUFDO0FBQ3JELGNBQWMsa0NBQWtDLENBQUM7QUFDakQsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLHVDQUF1QyxDQUFDO0FBQ3RELGNBQWMsOEJBQThCLENBQUM7QUFDN0MsY0FBYyxvQ0FBb0MsQ0FBQztBQUNuRCxjQUFjLHVDQUF1QyxDQUFDIn0=
@@ -0,0 +1,25 @@
1
+ /**
2
+ * A privileged command run by `SmartDaemon` (through `systemdManager.execute()`) did not exit 0.
3
+ * The exit status alone decides failure; `stderr` carries what the command or sudo reported.
4
+ */
5
+ export declare class SmartDaemonCommandError extends Error {
6
+ /** The command as run, including any `sudo` prefix. It never contains the sudo password. */
7
+ readonly command: string;
8
+ /** The exit code; `1` when a signal ended the command. */
9
+ readonly exitCode: number;
10
+ /** The signal that ended the command, if one did. */
11
+ readonly signal: NodeJS.Signals | undefined;
12
+ /** The command's standard error, as captured. */
13
+ readonly stderr: string;
14
+ constructor(
15
+ /** The command as run, including any `sudo` prefix. It never contains the sudo password. */
16
+ command: string,
17
+ /** The exit code; `1` when a signal ended the command. */
18
+ exitCode: number,
19
+ /** The signal that ended the command, if one did. */
20
+ signal: NodeJS.Signals | undefined,
21
+ /** The command's standard error, as captured. */
22
+ stderr: string,
23
+ /** `cause`: the stdin write error when sudo exited before reading the password. */
24
+ options?: ErrorOptions);
25
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A privileged command run by `SmartDaemon` (through `systemdManager.execute()`) did not exit 0.
3
+ * The exit status alone decides failure; `stderr` carries what the command or sudo reported.
4
+ */
5
+ export class SmartDaemonCommandError extends Error {
6
+ constructor(
7
+ /** The command as run, including any `sudo` prefix. It never contains the sudo password. */
8
+ command,
9
+ /** The exit code; `1` when a signal ended the command. */
10
+ exitCode,
11
+ /** The signal that ended the command, if one did. */
12
+ signal,
13
+ /** The command's standard error, as captured. */
14
+ stderr,
15
+ /** `cause`: the stdin write error when sudo exited before reading the password. */
16
+ options) {
17
+ const reason = signal ? `was terminated by signal ${signal}` : `exited with code ${exitCode}`;
18
+ const detail = stderr.trim();
19
+ super(`Command "${command}" ${reason}${detail ? `: ${detail}` : '.'}`, options);
20
+ this.command = command;
21
+ this.exitCode = exitCode;
22
+ this.signal = signal;
23
+ this.stderr = stderr;
24
+ this.name = 'SmartDaemonCommandError';
25
+ }
26
+ }
27
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24uY2xhc3Nlcy5jb21tYW5kZXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGRhZW1vbi5jbGFzc2VzLmNvbW1hbmRlcnJvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sdUJBQXdCLFNBQVEsS0FBSztJQUNoRDtJQUNFLDRGQUE0RjtJQUM1RSxPQUFlO0lBQy9CLDBEQUEwRDtJQUMxQyxRQUFnQjtJQUNoQyxxREFBcUQ7SUFDckMsTUFBa0M7SUFDbEQsaURBQWlEO0lBQ2pDLE1BQWM7SUFDOUIsbUZBQW1GO0lBQ25GLE9BQXNCO1FBRXRCLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsNEJBQTRCLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxvQkFBb0IsUUFBUSxFQUFFLENBQUM7UUFDOUYsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzdCLEtBQUssQ0FBQyxZQUFZLE9BQU8sS0FBSyxNQUFNLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxLQUFLLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQzt1QkFaaEUsT0FBTzt3QkFFUCxRQUFRO3NCQUVSLE1BQU07c0JBRU4sTUFBTTtRQU90QixJQUFJLENBQUMsSUFBSSxHQUFHLHlCQUF5QixDQUFDO0lBQ3hDLENBQUM7Q0FDRiJ9
@@ -10,12 +10,24 @@ export declare class SmartDaemonSystemdManager {
10
10
  smartshellInstance: plugins.smartshell.Smartshell;
11
11
  smartFs: plugins.smartfs.SmartFs;
12
12
  shouldExecute: boolean;
13
+ /** Whether this process runs as root; set by the first privileged operation's check. */
13
14
  isRoot: boolean;
14
15
  sudoPassword?: string;
16
+ private rootStatus?;
15
17
  constructor(smartdaemonRefArg: SmartDaemon, sudoPasswordArg?: string);
16
18
  private checkIsRoot;
19
+ /** Checks once whether this process runs as root and sets `isRoot`; later calls reuse that check. */
20
+ private ensureRootStatus;
17
21
  checkElegibility(): Promise<boolean>;
22
+ /**
23
+ * Runs a command, through sudo when this process is not root and the command does not start with
24
+ * `sudo` already. With a `sudoPassword`, sudo reads it from standard input (`sudo -k -S -p ''`):
25
+ * it never reaches argv, the environment or a terminal. Rejects with `SmartDaemonCommandError`
26
+ * when the command does not exit 0.
27
+ */
18
28
  execute(commandArg: string): Promise<plugins.smartshell.IExecResult | undefined>;
29
+ private runChecked;
30
+ private assertSucceeded;
19
31
  /**
20
32
  * gets all services that are already present
21
33
  */
@@ -1,6 +1,7 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
2
  import * as paths from './smartdaemon.paths.js';
3
3
  import { SmartDaemon } from './smartdaemon.classes.smartdaemon.js';
4
+ import { SmartDaemonCommandError } from './smartdaemon.classes.commanderror.js';
4
5
  import { SmartDaemonService, } from './smartdaemon.classes.service.js';
5
6
  export class SmartDaemonSystemdManager {
6
7
  // STATIC
@@ -16,6 +17,7 @@ export class SmartDaemonSystemdManager {
16
17
  }
17
18
  constructor(smartdaemonRefArg, sudoPasswordArg) {
18
19
  this.shouldExecute = false;
20
+ /** Whether this process runs as root; set by the first privileged operation's check. */
19
21
  this.isRoot = false;
20
22
  this.smartdaemonRef = smartdaemonRefArg;
21
23
  this.smartshellInstance = new plugins.smartshell.Smartshell({
@@ -23,18 +25,27 @@ export class SmartDaemonSystemdManager {
23
25
  });
24
26
  this.smartFs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
25
27
  this.sudoPassword = sudoPasswordArg;
26
- // Check if we're running as root
27
- this.checkIsRoot().then(isRoot => {
28
- this.isRoot = isRoot;
29
- if (!isRoot) {
30
- console.log('Not running as root. Sudo will be used for privileged operations.');
31
- }
32
- });
33
28
  }
34
29
  async checkIsRoot() {
35
30
  const result = await this.smartshellInstance.exec('id -u');
36
31
  return result.stdout.trim() === '0';
37
32
  }
33
+ /** Checks once whether this process runs as root and sets `isRoot`; later calls reuse that check. */
34
+ ensureRootStatus() {
35
+ if (!this.rootStatus) {
36
+ this.rootStatus = this.checkIsRoot().then((isRoot) => {
37
+ this.isRoot = isRoot;
38
+ if (!isRoot) {
39
+ console.log('Not running as root. Sudo will be used for privileged operations.');
40
+ }
41
+ });
42
+ // A failed check is not memoized, so the next privileged operation asks again.
43
+ this.rootStatus.catch(() => {
44
+ this.rootStatus = undefined;
45
+ });
46
+ }
47
+ return this.rootStatus;
48
+ }
38
49
  async checkElegibility() {
39
50
  if (plugins.os.platform() === 'linux') {
40
51
  this.shouldExecute = true;
@@ -45,30 +56,50 @@ export class SmartDaemonSystemdManager {
45
56
  }
46
57
  return this.shouldExecute;
47
58
  }
59
+ /**
60
+ * Runs a command, through sudo when this process is not root and the command does not start with
61
+ * `sudo` already. With a `sudoPassword`, sudo reads it from standard input (`sudo -k -S -p ''`):
62
+ * it never reaches argv, the environment or a terminal. Rejects with `SmartDaemonCommandError`
63
+ * when the command does not exit 0.
64
+ */
48
65
  async execute(commandArg) {
49
- if (await this.checkElegibility()) {
50
- // Only use sudo if we're not root and command doesn't already have sudo
51
- if (!this.isRoot && !commandArg.startsWith('sudo')) {
52
- commandArg = `sudo ${commandArg}`;
53
- if (this.sudoPassword) {
54
- // Use interactive PTY mode for password input
55
- const interactiveExec = await this.smartshellInstance.execInteractiveControlPty(commandArg);
56
- await interactiveExec.sendLine(this.sudoPassword);
57
- const result = await interactiveExec.finalPromise;
58
- return result;
59
- }
60
- }
61
- // Execute command (with or without sudo)
62
- try {
63
- await this.smartshellInstance.exec(commandArg);
64
- }
65
- catch (error) {
66
- if (!this.isRoot && error instanceof Error && error.message.includes('authentication')) {
67
- throw new Error('Sudo authentication failed. Please configure passwordless sudo or provide a sudo password.');
68
- }
69
- throw error;
70
- }
66
+ if (!(await this.checkElegibility())) {
67
+ return undefined;
68
+ }
69
+ await this.ensureRootStatus();
70
+ if (this.isRoot || commandArg.startsWith('sudo')) {
71
+ return await this.runChecked(commandArg);
72
+ }
73
+ if (!this.sudoPassword) {
74
+ return await this.runChecked(`sudo ${commandArg}`);
75
+ }
76
+ // -k ignores cached credentials, so sudo reads the password line whenever its policy asks for
77
+ // one; ending stdin right after it makes a rejected password fail instead of waiting for a retry.
78
+ const sudoCommand = `sudo -k -S -p '' -- ${commandArg}`;
79
+ const interactiveExec = await this.smartshellInstance.execInteractiveControl(sudoCommand);
80
+ let writeError;
81
+ try {
82
+ await interactiveExec.sendLine(this.sudoPassword);
83
+ }
84
+ catch (error) {
85
+ // sudo can exit before reading stdin (EPIPE); its exit status below decides the outcome.
86
+ writeError = error;
87
+ }
88
+ interactiveExec.endInput();
89
+ const result = await interactiveExec.finalPromise;
90
+ this.assertSucceeded(sudoCommand, result, writeError);
91
+ return result;
92
+ }
93
+ async runChecked(commandArg) {
94
+ const result = await this.smartshellInstance.exec(commandArg);
95
+ this.assertSucceeded(commandArg, result);
96
+ return result;
97
+ }
98
+ assertSucceeded(commandArg, resultArg, causeArg) {
99
+ if (resultArg.exitCode === 0 && !resultArg.signal) {
100
+ return;
71
101
  }
102
+ throw new SmartDaemonCommandError(commandArg, resultArg.exitCode, resultArg.signal, resultArg.stderr, causeArg === undefined ? undefined : { cause: causeArg });
72
103
  }
73
104
  /**
74
105
  * gets all services that are already present
@@ -101,6 +132,7 @@ export class SmartDaemonSystemdManager {
101
132
  if (await this.checkElegibility()) {
102
133
  const content = this.smartdaemonRef.templateManager.generateUnitFileForService(serviceArg);
103
134
  const targetPath = SmartDaemonSystemdManager.createFilePathFromServiceName(serviceArg.name);
135
+ await this.ensureRootStatus();
104
136
  if (this.isRoot) {
105
137
  // Direct write when running as root
106
138
  await this.smartFs.file(targetPath).encoding('utf8').mode(0o644).write(content);
@@ -117,6 +149,7 @@ export class SmartDaemonSystemdManager {
117
149
  async deleteService(serviceArg) {
118
150
  if (await this.checkElegibility()) {
119
151
  const filePath = SmartDaemonSystemdManager.createFilePathFromServiceName(serviceArg.name);
152
+ await this.ensureRootStatus();
120
153
  if (this.isRoot) {
121
154
  await this.smartFs.file(filePath).delete();
122
155
  }
@@ -145,4 +178,4 @@ export class SmartDaemonSystemdManager {
145
178
  }
146
179
  }
147
180
  }
148
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24uY2xhc3Nlcy5zeXN0ZW1kbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLmNsYXNzZXMuc3lzdGVtZG1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEtBQUssS0FBSyxNQUFNLHdCQUF3QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBRUwsa0JBQWtCLEdBQ25CLE1BQU0sa0NBQWtDLENBQUM7QUFFMUMsTUFBTSxPQUFPLHlCQUF5QjtJQUNwQyxTQUFTO2FBQ00seUJBQW9CLEdBQUcsYUFBYSxBQUFoQixDQUFpQjtJQUU3QyxNQUFNLENBQUMsZ0NBQWdDLENBQUMsY0FBc0I7UUFDbkUsT0FBTyxHQUFHLHlCQUF5QixDQUFDLG9CQUFvQixJQUFJLGNBQWMsRUFBRSxDQUFDO0lBQy9FLENBQUM7SUFFTSxNQUFNLENBQUMsNkJBQTZCLENBQUMsY0FBc0I7UUFDaEUsT0FBTyxHQUFHLHlCQUF5QixDQUFDLGdDQUFnQyxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUM7SUFDakcsQ0FBQztJQUVNLE1BQU0sQ0FBQyw2QkFBNkIsQ0FBQyxjQUFzQjtRQUNoRSxPQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUN0QixLQUFLLENBQUMsVUFBVSxFQUNoQix5QkFBeUIsQ0FBQyw2QkFBNkIsQ0FBQyxjQUFjLENBQUMsQ0FDeEUsQ0FBQztJQUNKLENBQUM7SUFXRCxZQUFZLGlCQUE4QixFQUFFLGVBQXdCO1FBSjdELGtCQUFhLEdBQVksS0FBSyxDQUFDO1FBQy9CLFdBQU0sR0FBWSxLQUFLLENBQUM7UUFJN0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxpQkFBaUIsQ0FBQztRQUN4QyxJQUFJLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxPQUFP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
181
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24uY2xhc3Nlcy5zeXN0ZW1kbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLmNsYXNzZXMuc3lzdGVtZG1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUNwRCxPQUFPLEtBQUssS0FBSyxNQUFNLHdCQUF3QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNoRixPQUFPLEVBRUwsa0JBQWtCLEdBQ25CLE1BQU0sa0NBQWtDLENBQUM7QUFFMUMsTUFBTSxPQUFPLHlCQUF5QjtJQUNwQyxTQUFTO2FBQ00seUJBQW9CLEdBQUcsYUFBYSxBQUFoQixDQUFpQjtJQUU3QyxNQUFNLENBQUMsZ0NBQWdDLENBQUMsY0FBc0I7UUFDbkUsT0FBTyxHQUFHLHlCQUF5QixDQUFDLG9CQUFvQixJQUFJLGNBQWMsRUFBRSxDQUFDO0lBQy9FLENBQUM7SUFFTSxNQUFNLENBQUMsNkJBQTZCLENBQUMsY0FBc0I7UUFDaEUsT0FBTyxHQUFHLHlCQUF5QixDQUFDLGdDQUFnQyxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUM7SUFDakcsQ0FBQztJQUVNLE1BQU0sQ0FBQyw2QkFBNkIsQ0FBQyxjQUFzQjtRQUNoRSxPQUFPLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUN0QixLQUFLLENBQUMsVUFBVSxFQUNoQix5QkFBeUIsQ0FBQyw2QkFBNkIsQ0FBQyxjQUFjLENBQUMsQ0FDeEUsQ0FBQztJQUNKLENBQUM7SUFhRCxZQUFZLGlCQUE4QixFQUFFLGVBQXdCO1FBTjdELGtCQUFhLEdBQVksS0FBSyxDQUFDO1FBQ3RDLHdGQUF3RjtRQUNqRixXQUFNLEdBQVksS0FBSyxDQUFDO1FBSzdCLElBQUksQ0FBQyxjQUFjLEdBQUcsaUJBQWlCLENBQUM7UUFDeEMsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUM7WUFDMUQsUUFBUSxFQUFFLE1BQU07U0FDakIsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLENBQUM7UUFDdEYsSUFBSSxDQUFDLFlBQVksR0FBRyxlQUFlLENBQUM7SUFDdEMsQ0FBQztJQUVPLEtBQUssQ0FBQyxXQUFXO1FBQ3ZCLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMzRCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLEtBQUssR0FBRyxDQUFDO0lBQ3RDLENBQUM7SUFFRCxxR0FBcUc7SUFDN0YsZ0JBQWdCO1FBQ3RCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDckIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JBQ25ELElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO2dCQUNyQixJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7b0JBQ1osT0FBTyxDQUFDLEdBQUcsQ0FBQyxtRUFBbUUsQ0FBQyxDQUFDO2dCQUNuRixDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQUM7WUFDSCwrRUFBK0U7WUFDL0UsSUFBSSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO2dCQUN6QixJQUFJLENBQUMsVUFBVSxHQUFHLFNBQVMsQ0FBQztZQUM5QixDQUFDLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDekIsQ0FBQztJQUVNLEtBQUssQ0FBQyxnQkFBZ0I7UUFDM0IsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxLQUFLLE9BQU8sRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO1FBQzVCLENBQUM7YUFBTSxDQUFDO1lBQ04sT0FBTyxDQUFDLEdBQUcsQ0FBQyw4RUFBOEUsQ0FBQyxDQUFDO1lBQzVGLElBQUksQ0FBQyxhQUFhLEdBQUcsS0FBSyxDQUFDO1FBQzdCLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxhQUFhLENBQUM7SUFDNUIsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFrQjtRQUNyQyxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUNyQyxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBQ0QsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM5QixJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksVUFBVSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1lBQ2pELE9BQU8sTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzNDLENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3ZCLE9BQU8sTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsVUFBVSxFQUFFLENBQUMsQ0FBQztRQUNyRCxDQUFDO1FBQ0QsOEZBQThGO1FBQzlGLGtHQUFrRztRQUNsRyxNQUFNLFdBQVcsR0FBRyx1QkFBdUIsVUFBVSxFQUFFLENBQUM7UUFDeEQsTUFBTSxlQUFlLEdBQUcsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDMUYsSUFBSSxVQUFtQixDQUFDO1FBQ3hCLElBQUksQ0FBQztZQUNILE1BQU0sZUFBZSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDcEQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZix5RkFBeUY7WUFDekYsVUFBVSxHQUFHLEtBQUssQ0FBQztRQUNyQixDQUFDO1FBQ0QsZUFBZSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzNCLE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDLFlBQVksQ0FBQztRQUNsRCxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsRUFBRSxNQUFNLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDdEQsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVPLEtBQUssQ0FBQyxVQUFVLENBQUMsVUFBa0I7UUFDekMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzlELElBQUksQ0FBQyxlQUFlLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3pDLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFTyxlQUFlLENBQ3JCLFVBQWtCLEVBQ2xCLFNBQXlDLEVBQ3pDLFFBQWtCO1FBRWxCLElBQUksU0FBUyxDQUFDLFFBQVEsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbEQsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLElBQUksdUJBQXVCLENBQy9CLFVBQVUsRUFDVixTQUFTLENBQUMsUUFBUSxFQUNsQixTQUFTLENBQUMsTUFBTSxFQUNoQixTQUFTLENBQUMsTUFBTSxFQUNoQixRQUFRLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxDQUN6RCxDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFdBQVc7UUFDdEIsTUFBTSxnQkFBZ0IsR0FBeUIsRUFBRSxDQUFDO1FBQ2xELElBQUksTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO1lBQ2xDLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUMsTUFBTSxDQUFDLHVCQUF1QixDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDaEgsS0FBSyxNQUFNLFdBQVcsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO2dCQUM1QyxNQUFNLGtCQUFrQixHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDN0YsTUFBTSxJQUFJLEdBQUcsQ0FDWCxNQUFNLE9BQU8sQ0FBQyxhQUFhLENBQUMsdUJBQXVCLENBQUMsa0JBQWtCLENBQUMsUUFBUSxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQ3pGLENBQUMsSUFBd0QsQ0FBQztnQkFDM0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxrQkFBa0IsQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxDQUFDO2dCQUN0RixPQUFPLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQztnQkFDN0IsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2pDLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxnQkFBZ0IsQ0FBQztJQUMxQixDQUFDO0lBRU0sS0FBSyxDQUFDLFlBQVksQ0FBQyxVQUE4QjtRQUN0RCxJQUFJLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsQ0FBQztZQUNsQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQ2hCLG1CQUFtQix5QkFBeUIsQ0FBQyxnQ0FBZ0MsQ0FDM0UsVUFBVSxDQUFDLElBQUksQ0FDaEIsRUFBRSxDQUNKLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUNNLEtBQUssQ0FBQyxXQUFXLENBQUMsVUFBOEI7UUFDckQsSUFBSSxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLENBQUM7WUFDbEMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUNoQixrQkFBa0IseUJBQXlCLENBQUMsZ0NBQWdDLENBQzFFLFVBQVUsQ0FBQyxJQUFJLENBQ2hCLEVBQUUsQ0FDSixDQUFDO1FBQ0osQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMsV0FBVyxDQUFDLFVBQThCO1FBQ3JELElBQUksTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO1lBQ2xDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsZUFBZSxDQUFDLDBCQUEwQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQzNGLE1BQU0sVUFBVSxHQUFHLHlCQUF5QixDQUFDLDZCQUE2QixDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUU1RixNQUFNLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzlCLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO2dCQUNoQixvQ0FBb0M7Z0JBQ3BDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDbEYsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGtDQUFrQztnQkFDbEMsTUFBTSxRQUFRLEdBQUcsb0JBQW9CLFVBQVUsQ0FBQyxJQUFJLFVBQVUsQ0FBQztnQkFDL0QsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFDOUUsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sUUFBUSxJQUFJLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQywwQkFBMEI7Z0JBQzlFLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQywwQkFBMEI7WUFDM0UsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0lBRU0sS0FBSyxDQUFDLGFBQWEsQ0FBQyxVQUE4QjtRQUN2RCxJQUFJLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsQ0FBQztZQUNsQyxNQUFNLFFBQVEsR0FBRyx5QkFBeUIsQ0FBQyw2QkFBNkIsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFMUYsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUM5QixJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDaEIsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM3QyxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLDBCQUEwQjtZQUNsRSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFTSxLQUFLLENBQUMsYUFBYSxDQUFDLFVBQThCO1FBQ3ZELElBQUksTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO1lBQ2xDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNuQyxJQUFJLFVBQVUsQ0FBQyxhQUFhLEVBQUUsQ0FBQztnQkFDN0IsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLHlCQUF5QixDQUFDLENBQUM7WUFDaEQsQ0FBQztZQUNELE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FDaEIsb0JBQW9CLHlCQUF5QixDQUFDLGdDQUFnQyxDQUM1RSxVQUFVLENBQUMsSUFBSSxDQUNoQixFQUFFLENBQ0osQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBQ00sS0FBSyxDQUFDLGNBQWMsQ0FBQyxVQUE4QjtRQUN4RCxJQUFJLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixFQUFFLEVBQUUsQ0FBQztZQUNsQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQ2hCLHFCQUFxQix5QkFBeUIsQ0FBQyxnQ0FBZ0MsQ0FDN0UsVUFBVSxDQUFDLElBQUksQ0FDaEIsRUFBRSxDQUNKLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUssQ0FBQyxNQUFNO1FBQ2pCLElBQUksTUFBTSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO1lBQ2xDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBQ2hELENBQUM7SUFDSCxDQUFDO0NBQ0YifQ==
@@ -9,9 +9,7 @@ export type { Stats } from 'node:fs';
9
9
  export { path, childProcess, fs, fsConstants, crypto, util, os };
10
10
  import * as lik from '@push.rocks/lik';
11
11
  import * as smartfs from '@push.rocks/smartfs';
12
- import * as smartlog from '@push.rocks/smartlog';
13
- import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
14
12
  import * as smartmarkdown from '@push.rocks/smartmarkdown/iso';
15
13
  import * as smartpath from '@push.rocks/smartpath';
16
14
  import * as smartshell from '@push.rocks/smartshell';
17
- export { lik, smartfs, smartlog, smartlogDestinationLocal, smartmarkdown, smartpath, smartshell, };
15
+ export { lik, smartfs, smartmarkdown, smartpath, smartshell, };
@@ -10,11 +10,9 @@ export { path, childProcess, fs, fsConstants, crypto, util, os };
10
10
  // @pushrocks scope
11
11
  import * as lik from '@push.rocks/lik';
12
12
  import * as smartfs from '@push.rocks/smartfs';
13
- import * as smartlog from '@push.rocks/smartlog';
14
- import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
15
13
  import * as smartmarkdown from '@push.rocks/smartmarkdown/iso';
16
14
  import * as smartpath from '@push.rocks/smartpath';
17
15
  import * as smartshell from '@push.rocks/smartshell';
18
- export { lik, smartfs, smartlog, smartlogDestinationLocal, smartmarkdown, smartpath, smartshell, };
16
+ export { lik, smartfs, smartmarkdown, smartpath, smartshell, };
19
17
  // third party
20
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24ucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLEVBQUUsU0FBUyxJQUFJLFdBQVcsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNuRCxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUc5QixPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFFakUsbUJBQW1CO0FBQ25CLE9BQU8sS0FBSyxHQUFHLE1BQU0saUJBQWlCLENBQUM7QUFDdkMsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssUUFBUSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pELE9BQU8sS0FBSyx3QkFBd0IsTUFBTSx3Q0FBd0MsQ0FBQztBQUNuRixPQUFPLEtBQUssYUFBYSxNQUFNLCtCQUErQixDQUFDO0FBQy9ELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUVyRCxPQUFPLEVBQ0wsR0FBRyxFQUNILE9BQU8sRUFDUCxRQUFRLEVBQ1Isd0JBQXdCLEVBQ3hCLGFBQWEsRUFDYixTQUFTLEVBQ1QsVUFBVSxHQUNYLENBQUM7QUFFRixjQUFjIn0=
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYWVtb24ucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0ZGFlbW9uLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLEVBQUUsU0FBUyxJQUFJLFdBQVcsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNuRCxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUc5QixPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFFakUsbUJBQW1CO0FBQ25CLE9BQU8sS0FBSyxHQUFHLE1BQU0saUJBQWlCLENBQUM7QUFDdkMsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEtBQUssYUFBYSxNQUFNLCtCQUErQixDQUFDO0FBQy9ELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUVyRCxPQUFPLEVBQ0wsR0FBRyxFQUNILE9BQU8sRUFDUCxhQUFhLEVBQ2IsU0FBUyxFQUNULFVBQVUsR0FDWCxDQUFDO0FBRUYsY0FBYyJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdaemon",
3
- "version": "2.22.0",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
5
  "description": "Start scripts as long running daemons and manage them.",
6
6
  "main": "dist_ts/index.js",
@@ -39,16 +39,15 @@
39
39
  "devDependencies": {
40
40
  "@git.zone/tsbuild": "^6.0.2",
41
41
  "@git.zone/tsrun": "^3.0.3",
42
- "@git.zone/tstest": "^7.2.1",
42
+ "@git.zone/tstest": "^8.0.0",
43
43
  "@types/node": "^26.6.4"
44
44
  },
45
45
  "dependencies": {
46
46
  "@push.rocks/lik": "^6.4.4",
47
47
  "@push.rocks/smartfs": "^1.5.1",
48
- "@push.rocks/smartlog": "^4.0.4",
49
48
  "@push.rocks/smartmarkdown": "^3.3.0",
50
49
  "@push.rocks/smartpath": "^6.0.0",
51
- "@push.rocks/smartshell": "^4.2.1"
50
+ "@push.rocks/smartshell": "^5.0.0"
52
51
  },
53
52
  "files": [
54
53
  "ts/**/*",
package/readme.md CHANGED
@@ -21,7 +21,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
21
21
  - 🔄 **Systemd Integration**: Full Linux systemd support for production-grade service management
22
22
  - 🛡️ **Auto-Recovery**: Built-in service restart on failure
23
23
  - 📝 **Declarative Service Definitions**: Simple, clear service configuration
24
- - 🔌 **Interactive & Non-Interactive Modes**: Support for both passwordless sudo and interactive authentication
24
+ - 🔌 **Passwordless or Password-Based Sudo**: Works with passwordless sudo, or hands a configured sudo password to sudo on standard input (no terminal or PTY needed)
25
25
  - 🎛️ **Complete Lifecycle Management**: Start, stop, enable, disable, and reload services with ease
26
26
 
27
27
  ## 📦 Installation
@@ -115,6 +115,20 @@ const service = await daemon.addService({
115
115
  await service.enable(); // Automatically uses sudo with password
116
116
  ```
117
117
 
118
+ Each privileged command runs as `sudo -k -S -p '' -- <command>`: sudo reads the password from
119
+ standard input, which is closed right after the password line, so a rejected password fails at
120
+ once instead of waiting for a retry. The password is never placed in argv, the environment or the
121
+ output, and no PTY (`node-pty`) is involved. `-k` makes sudo ask for the password every time and
122
+ leaves no cached credentials behind. If a sudoers rule lets the command run without a password
123
+ (`NOPASSWD`), sudo does not read standard input and the password line reaches the command's
124
+ standard input instead (`systemctl`, `mv`, `chmod` and `rm` never read it); leave `sudoPassword`
125
+ unset when sudo is passwordless.
126
+
127
+ A command that does not exit 0 rejects with `SmartDaemonCommandError` (`command`, `exitCode`,
128
+ `signal`, `stderr`), with or without a sudo password: a rejected password, a missing `sudo`
129
+ (exit code 127) or a failing `systemctl` call all surface this way. Besides `systemctl`, saving
130
+ and deleting a unit file as a non-root user runs `mv`, `chmod` and `rm` through sudo.
131
+
118
132
  ### Passwordless Sudo (Recommended for Production)
119
133
 
120
134
  For production environments, configure passwordless sudo for specific systemctl commands:
@@ -1398,7 +1412,8 @@ WantedBy=multi-user.target
1398
1412
 
1399
1413
  ### Permission Denied Errors
1400
1414
 
1401
- If you encounter "Interactive authentication required" errors:
1415
+ If an operation rejects with a `SmartDaemonCommandError` whose `stderr` reports that sudo needs a
1416
+ password or a terminal (for example "a terminal is required to read the password"):
1402
1417
 
1403
1418
  1. **Option 1**: Run your application with sudo
1404
1419
  ```bash
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdaemon',
6
- version: '2.22.0',
6
+ version: '3.0.0',
7
7
  description: 'Start scripts as long running daemons and manage them.'
8
8
  }
package/ts/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from './classes.systemdunit.js';
4
4
  export * from './classes.systemdservicedefinition.js';
5
5
  export * from './classes.systemdunitfile.js';
6
6
  export * from './classes.systemdtransientscope.js';
7
+ export * from './smartdaemon.classes.commanderror.js';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * A privileged command run by `SmartDaemon` (through `systemdManager.execute()`) did not exit 0.
3
+ * The exit status alone decides failure; `stderr` carries what the command or sudo reported.
4
+ */
5
+ export class SmartDaemonCommandError extends Error {
6
+ constructor(
7
+ /** The command as run, including any `sudo` prefix. It never contains the sudo password. */
8
+ public readonly command: string,
9
+ /** The exit code; `1` when a signal ended the command. */
10
+ public readonly exitCode: number,
11
+ /** The signal that ended the command, if one did. */
12
+ public readonly signal: NodeJS.Signals | undefined,
13
+ /** The command's standard error, as captured. */
14
+ public readonly stderr: string,
15
+ /** `cause`: the stdin write error when sudo exited before reading the password. */
16
+ options?: ErrorOptions,
17
+ ) {
18
+ const reason = signal ? `was terminated by signal ${signal}` : `exited with code ${exitCode}`;
19
+ const detail = stderr.trim();
20
+ super(`Command "${command}" ${reason}${detail ? `: ${detail}` : '.'}`, options);
21
+ this.name = 'SmartDaemonCommandError';
22
+ }
23
+ }
@@ -1,6 +1,7 @@
1
1
  import * as plugins from './smartdaemon.plugins.js';
2
2
  import * as paths from './smartdaemon.paths.js';
3
3
  import { SmartDaemon } from './smartdaemon.classes.smartdaemon.js';
4
+ import { SmartDaemonCommandError } from './smartdaemon.classes.commanderror.js';
4
5
  import {
5
6
  type ISmartDaemonServiceConstructorOptions,
6
7
  SmartDaemonService,
@@ -31,8 +32,10 @@ export class SmartDaemonSystemdManager {
31
32
  public smartFs: plugins.smartfs.SmartFs;
32
33
 
33
34
  public shouldExecute: boolean = false;
35
+ /** Whether this process runs as root; set by the first privileged operation's check. */
34
36
  public isRoot: boolean = false;
35
37
  public sudoPassword?: string;
38
+ private rootStatus?: Promise<void>;
36
39
 
37
40
  constructor(smartdaemonRefArg: SmartDaemon, sudoPasswordArg?: string) {
38
41
  this.smartdaemonRef = smartdaemonRefArg;
@@ -41,14 +44,6 @@ export class SmartDaemonSystemdManager {
41
44
  });
42
45
  this.smartFs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
43
46
  this.sudoPassword = sudoPasswordArg;
44
-
45
- // Check if we're running as root
46
- this.checkIsRoot().then(isRoot => {
47
- this.isRoot = isRoot;
48
- if (!isRoot) {
49
- console.log('Not running as root. Sudo will be used for privileged operations.');
50
- }
51
- });
52
47
  }
53
48
 
54
49
  private async checkIsRoot(): Promise<boolean> {
@@ -56,6 +51,23 @@ export class SmartDaemonSystemdManager {
56
51
  return result.stdout.trim() === '0';
57
52
  }
58
53
 
54
+ /** Checks once whether this process runs as root and sets `isRoot`; later calls reuse that check. */
55
+ private ensureRootStatus(): Promise<void> {
56
+ if (!this.rootStatus) {
57
+ this.rootStatus = this.checkIsRoot().then((isRoot) => {
58
+ this.isRoot = isRoot;
59
+ if (!isRoot) {
60
+ console.log('Not running as root. Sudo will be used for privileged operations.');
61
+ }
62
+ });
63
+ // A failed check is not memoized, so the next privileged operation asks again.
64
+ this.rootStatus.catch(() => {
65
+ this.rootStatus = undefined;
66
+ });
67
+ }
68
+ return this.rootStatus;
69
+ }
70
+
59
71
  public async checkElegibility() {
60
72
  if (plugins.os.platform() === 'linux') {
61
73
  this.shouldExecute = true;
@@ -66,31 +78,61 @@ export class SmartDaemonSystemdManager {
66
78
  return this.shouldExecute;
67
79
  }
68
80
 
69
- public async execute(commandArg: string) {
70
- if (await this.checkElegibility()) {
71
- // Only use sudo if we're not root and command doesn't already have sudo
72
- if (!this.isRoot && !commandArg.startsWith('sudo')) {
73
- commandArg = `sudo ${commandArg}`;
74
-
75
- if (this.sudoPassword) {
76
- // Use interactive PTY mode for password input
77
- const interactiveExec = await this.smartshellInstance.execInteractiveControlPty(commandArg);
78
- await interactiveExec.sendLine(this.sudoPassword);
79
- const result = await interactiveExec.finalPromise;
80
- return result;
81
- }
82
- }
83
-
84
- // Execute command (with or without sudo)
85
- try {
86
- await this.smartshellInstance.exec(commandArg);
87
- } catch (error) {
88
- if (!this.isRoot && error instanceof Error && error.message.includes('authentication')) {
89
- throw new Error('Sudo authentication failed. Please configure passwordless sudo or provide a sudo password.');
90
- }
91
- throw error;
92
- }
81
+ /**
82
+ * Runs a command, through sudo when this process is not root and the command does not start with
83
+ * `sudo` already. With a `sudoPassword`, sudo reads it from standard input (`sudo -k -S -p ''`):
84
+ * it never reaches argv, the environment or a terminal. Rejects with `SmartDaemonCommandError`
85
+ * when the command does not exit 0.
86
+ */
87
+ public async execute(commandArg: string): Promise<plugins.smartshell.IExecResult | undefined> {
88
+ if (!(await this.checkElegibility())) {
89
+ return undefined;
90
+ }
91
+ await this.ensureRootStatus();
92
+ if (this.isRoot || commandArg.startsWith('sudo')) {
93
+ return await this.runChecked(commandArg);
94
+ }
95
+ if (!this.sudoPassword) {
96
+ return await this.runChecked(`sudo ${commandArg}`);
97
+ }
98
+ // -k ignores cached credentials, so sudo reads the password line whenever its policy asks for
99
+ // one; ending stdin right after it makes a rejected password fail instead of waiting for a retry.
100
+ const sudoCommand = `sudo -k -S -p '' -- ${commandArg}`;
101
+ const interactiveExec = await this.smartshellInstance.execInteractiveControl(sudoCommand);
102
+ let writeError: unknown;
103
+ try {
104
+ await interactiveExec.sendLine(this.sudoPassword);
105
+ } catch (error) {
106
+ // sudo can exit before reading stdin (EPIPE); its exit status below decides the outcome.
107
+ writeError = error;
108
+ }
109
+ interactiveExec.endInput();
110
+ const result = await interactiveExec.finalPromise;
111
+ this.assertSucceeded(sudoCommand, result, writeError);
112
+ return result;
113
+ }
114
+
115
+ private async runChecked(commandArg: string): Promise<plugins.smartshell.IExecResult> {
116
+ const result = await this.smartshellInstance.exec(commandArg);
117
+ this.assertSucceeded(commandArg, result);
118
+ return result;
119
+ }
120
+
121
+ private assertSucceeded(
122
+ commandArg: string,
123
+ resultArg: plugins.smartshell.IExecResult,
124
+ causeArg?: unknown,
125
+ ): void {
126
+ if (resultArg.exitCode === 0 && !resultArg.signal) {
127
+ return;
93
128
  }
129
+ throw new SmartDaemonCommandError(
130
+ commandArg,
131
+ resultArg.exitCode,
132
+ resultArg.signal,
133
+ resultArg.stderr,
134
+ causeArg === undefined ? undefined : { cause: causeArg },
135
+ );
94
136
  }
95
137
 
96
138
  /**
@@ -136,7 +178,8 @@ export class SmartDaemonSystemdManager {
136
178
  if (await this.checkElegibility()) {
137
179
  const content = this.smartdaemonRef.templateManager.generateUnitFileForService(serviceArg);
138
180
  const targetPath = SmartDaemonSystemdManager.createFilePathFromServiceName(serviceArg.name);
139
-
181
+
182
+ await this.ensureRootStatus();
140
183
  if (this.isRoot) {
141
184
  // Direct write when running as root
142
185
  await this.smartFs.file(targetPath).encoding('utf8').mode(0o644).write(content);
@@ -153,7 +196,8 @@ export class SmartDaemonSystemdManager {
153
196
  public async deleteService(serviceArg: SmartDaemonService) {
154
197
  if (await this.checkElegibility()) {
155
198
  const filePath = SmartDaemonSystemdManager.createFilePathFromServiceName(serviceArg.name);
156
-
199
+
200
+ await this.ensureRootStatus();
157
201
  if (this.isRoot) {
158
202
  await this.smartFs.file(filePath).delete();
159
203
  } else {
@@ -13,8 +13,6 @@ export { path, childProcess, fs, fsConstants, crypto, util, os };
13
13
  // @pushrocks scope
14
14
  import * as lik from '@push.rocks/lik';
15
15
  import * as smartfs from '@push.rocks/smartfs';
16
- import * as smartlog from '@push.rocks/smartlog';
17
- import * as smartlogDestinationLocal from '@push.rocks/smartlog/destination-local';
18
16
  import * as smartmarkdown from '@push.rocks/smartmarkdown/iso';
19
17
  import * as smartpath from '@push.rocks/smartpath';
20
18
  import * as smartshell from '@push.rocks/smartshell';
@@ -22,8 +20,6 @@ import * as smartshell from '@push.rocks/smartshell';
22
20
  export {
23
21
  lik,
24
22
  smartfs,
25
- smartlog,
26
- smartlogDestinationLocal,
27
23
  smartmarkdown,
28
24
  smartpath,
29
25
  smartshell,
@@ -1,2 +0,0 @@
1
- import * as plugins from './smartdaemon.plugins.js';
2
- export declare const logger: plugins.smartlog.Smartlog;
@@ -1,14 +0,0 @@
1
- import * as plugins from './smartdaemon.plugins.js';
2
- export const logger = new plugins.smartlog.Smartlog({
3
- logContext: {
4
- company: 'Some Company',
5
- companyunit: 'Some CompanyUnit',
6
- containerName: 'Some Containername',
7
- environment: 'local',
8
- runtime: 'node',
9
- zone: 'gitzone',
10
- },
11
- minimumLogLevel: 'silly',
12
- });
13
- logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());
14
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRkYW1vbi5sb2dnaW5nLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRkYW1vbi5sb2dnaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sMEJBQTBCLENBQUM7QUFFcEQsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7SUFDbEQsVUFBVSxFQUFFO1FBQ1YsT0FBTyxFQUFFLGNBQWM7UUFDdkIsV0FBVyxFQUFFLGtCQUFrQjtRQUMvQixhQUFhLEVBQUUsb0JBQW9CO1FBQ25DLFdBQVcsRUFBRSxPQUFPO1FBQ3BCLE9BQU8sRUFBRSxNQUFNO1FBQ2YsSUFBSSxFQUFFLFNBQVM7S0FDaEI7SUFDRCxlQUFlLEVBQUUsT0FBTztDQUN6QixDQUFDLENBQUM7QUFFSCxNQUFNLENBQUMsaUJBQWlCLENBQUMsSUFBSSxPQUFPLENBQUMsd0JBQXdCLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDIn0=
@@ -1,15 +0,0 @@
1
- import * as plugins from './smartdaemon.plugins.js';
2
-
3
- export const logger = new plugins.smartlog.Smartlog({
4
- logContext: {
5
- company: 'Some Company',
6
- companyunit: 'Some CompanyUnit',
7
- containerName: 'Some Containername',
8
- environment: 'local',
9
- runtime: 'node',
10
- zone: 'gitzone',
11
- },
12
- minimumLogLevel: 'silly',
13
- });
14
-
15
- logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());