@lde/task-runner-native 0.0.5 → 0.2.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 +34 -6
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -4
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Task Runner Native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run shell commands natively on the host system using Node.js `child_process`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```typescript
|
|
8
|
+
import { NativeTaskRunner } from '@lde/task-runner-native';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const runner = new NativeTaskRunner({
|
|
11
|
+
cwd: '/path/to/working/dir', // Optional working directory
|
|
12
|
+
gracefulShutdownTimeout: 5000, // Optional timeout before SIGKILL (default: 5000ms)
|
|
13
|
+
});
|
|
10
14
|
|
|
11
|
-
Run
|
|
15
|
+
// Run a command
|
|
16
|
+
const task = await runner.run('echo "Hello World"');
|
|
17
|
+
|
|
18
|
+
// Wait for completion
|
|
19
|
+
const output = await runner.wait(task);
|
|
20
|
+
console.log(output); // "Hello World"
|
|
21
|
+
|
|
22
|
+
// Or stop a long-running task
|
|
23
|
+
const task2 = await runner.run('sleep 60');
|
|
24
|
+
await runner.stop(task2); // Sends SIGTERM, then SIGKILL after timeout
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Options
|
|
28
|
+
|
|
29
|
+
| Option | Type | Default | Description |
|
|
30
|
+
| ------------------------- | -------- | ----------------- | --------------------------------------------------------- |
|
|
31
|
+
| `cwd` | `string` | Current directory | Working directory for spawned processes |
|
|
32
|
+
| `gracefulShutdownTimeout` | `number` | `5000` | Milliseconds to wait after SIGTERM before sending SIGKILL |
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- Spawns commands in a detached process group
|
|
37
|
+
- Graceful shutdown with SIGTERM → SIGKILL escalation
|
|
38
|
+
- Handles already-exited processes in `stop()`
|
|
39
|
+
- Captures stdout and stderr output
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { TaskRunner } from '@lde/task-runner';
|
|
2
2
|
import { ChildProcess } from 'node:child_process';
|
|
3
|
+
export interface NativeTaskRunnerOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Working directory for spawned processes.
|
|
6
|
+
* Defaults to the current working directory.
|
|
7
|
+
*/
|
|
8
|
+
cwd?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Timeout in milliseconds to wait for graceful shutdown (SIGTERM)
|
|
11
|
+
* before escalating to SIGKILL.
|
|
12
|
+
* @default 5000
|
|
13
|
+
*/
|
|
14
|
+
gracefulShutdownTimeout?: number;
|
|
15
|
+
}
|
|
3
16
|
export declare class NativeTaskRunner implements TaskRunner<ChildProcess> {
|
|
4
17
|
private stdout;
|
|
5
18
|
private stderr;
|
|
6
19
|
private shell;
|
|
20
|
+
private cwd?;
|
|
21
|
+
private gracefulShutdownTimeout;
|
|
22
|
+
constructor(options?: NativeTaskRunnerOptions);
|
|
7
23
|
run(command: string): Promise<ChildProcess>;
|
|
8
24
|
wait(task: ChildProcess): Promise<string>;
|
|
9
25
|
stop(task: ChildProcess): Promise<string | null>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,qBAAa,gBAAiB,YAAW,UAAU,CAAC,YAAY,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAAQ;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,qBAAa,gBAAiB,YAAW,UAAU,CAAC,YAAY,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAC,CAAS;IACrB,OAAO,CAAC,uBAAuB,CAAS;gBAE5B,OAAO,CAAC,EAAE,uBAAuB;IAKvC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsC3C,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzC,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwCtD,OAAO,CAAC,UAAU;CAQnB"}
|
package/dist/index.js
CHANGED
|
@@ -4,11 +4,17 @@ export class NativeTaskRunner {
|
|
|
4
4
|
stdout = new Map();
|
|
5
5
|
stderr = new Map();
|
|
6
6
|
shell = true;
|
|
7
|
+
cwd;
|
|
8
|
+
gracefulShutdownTimeout;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.cwd = options?.cwd;
|
|
11
|
+
this.gracefulShutdownTimeout = options?.gracefulShutdownTimeout ?? 5000;
|
|
12
|
+
}
|
|
7
13
|
async run(command) {
|
|
8
14
|
const task = spawn(command, {
|
|
9
15
|
detached: true,
|
|
10
16
|
shell: this.shell,
|
|
11
|
-
cwd:
|
|
17
|
+
cwd: this.cwd,
|
|
12
18
|
});
|
|
13
19
|
task.on('close', (code) => {
|
|
14
20
|
/** code is null when the process was killed, which is expected when
|
|
@@ -49,12 +55,38 @@ export class NativeTaskRunner {
|
|
|
49
55
|
}
|
|
50
56
|
async stop(task) {
|
|
51
57
|
return new Promise((resolve) => {
|
|
52
|
-
|
|
58
|
+
// Handle already-exited processes.
|
|
59
|
+
if (task.exitCode !== null || task.killed) {
|
|
53
60
|
resolve(this.taskOutput(task));
|
|
54
|
-
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const sigkillTimer = {};
|
|
64
|
+
const cleanup = () => {
|
|
65
|
+
if (sigkillTimer.current) {
|
|
66
|
+
clearTimeout(sigkillTimer.current);
|
|
67
|
+
}
|
|
68
|
+
resolve(this.taskOutput(task));
|
|
69
|
+
};
|
|
70
|
+
task.on('close', cleanup);
|
|
55
71
|
// Negative PID to kill whole process group: the {shell: true} argument
|
|
56
72
|
// to spawn splits off a separate process.
|
|
57
|
-
|
|
73
|
+
try {
|
|
74
|
+
process.kill(-task.pid, 'SIGTERM');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Process may have already exited (ESRCH error).
|
|
78
|
+
cleanup();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Escalate to SIGKILL after timeout if process doesn't terminate.
|
|
82
|
+
sigkillTimer.current = setTimeout(() => {
|
|
83
|
+
try {
|
|
84
|
+
process.kill(-task.pid, 'SIGKILL');
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Process may have exited between SIGTERM and SIGKILL.
|
|
88
|
+
}
|
|
89
|
+
}, this.gracefulShutdownTimeout);
|
|
58
90
|
});
|
|
59
91
|
}
|
|
60
92
|
taskOutput(task) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/task-runner-native",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"repository": {
|
|
5
|
-
"url": "https://github.com/ldengine/lde"
|
|
5
|
+
"url": "https://github.com/ldengine/lde",
|
|
6
|
+
"directory": "packages/task-runner-native"
|
|
6
7
|
},
|
|
7
8
|
"type": "module",
|
|
8
9
|
"exports": {
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
"!**/*.tsbuildinfo"
|
|
23
24
|
],
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@lde/task-runner": "0.0
|
|
26
|
+
"@lde/task-runner": "0.2.0",
|
|
26
27
|
"tslib": "^2.3.0"
|
|
27
28
|
}
|
|
28
29
|
}
|