@webpod/ps 1.1.0 → 1.2.1
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 +63 -122
- package/package.json +23 -8
- package/target/cjs/index.cjs +159 -183
- package/target/dts/index.d.ts +3 -3
- package/target/dts/ps.d.ts +23 -36
- package/target/esm/index.mjs +159 -181
package/README.md
CHANGED
|
@@ -2,163 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
> A Node.js module for looking up running processes. Originated from [neekey/ps](https://github.com/neekey/ps), [UmbraEngineering/ps](https://github.com/UmbraEngineering/ps) and completely reforged.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* [x] Builds a process subtree by parent
|
|
5
|
+
## Features
|
|
6
|
+
- Written in TypeScript, ships with types
|
|
7
|
+
- CJS and ESM entry points
|
|
8
|
+
- Promise and callback API, sync variants
|
|
9
|
+
- Process tree traversal by parent pid
|
|
10
|
+
- Uses `@webpod/ingrid` instead of `table-parser` ([neekey/ps#76](https://github.com/neekey/ps/issues/76), [neekey/ps#62](https://github.com/neekey/ps/issues/62))
|
|
12
11
|
|
|
13
12
|
## Install
|
|
14
13
|
```bash
|
|
15
|
-
|
|
14
|
+
npm install @webpod/ps
|
|
16
15
|
```
|
|
17
16
|
|
|
18
17
|
## Internals
|
|
19
|
-
This module uses different approaches for getting process list:
|
|
20
18
|
|
|
21
|
-
| Platform
|
|
22
|
-
|
|
23
|
-
| Unix/
|
|
24
|
-
| Windows (kernel >= 26000)| `pwsh -NoProfile -Command "Get-CimInstance Win32_Process \| Select-Object ProcessId,ParentProcessId,CommandLine \| ConvertTo-Json -Compress"` |
|
|
25
|
-
| Windows (kernel < 26000)
|
|
19
|
+
| Platform | Command |
|
|
20
|
+
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
21
|
+
| Unix / macOS | `ps -eo pid,ppid,args` |
|
|
22
|
+
| Windows (kernel >= 26000) | `pwsh -NoProfile -Command "Get-CimInstance Win32_Process \| Select-Object ProcessId,ParentProcessId,CommandLine \| ConvertTo-Json -Compress"` |
|
|
23
|
+
| Windows (kernel < 26000) | [`wmic`](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic) `process get ProcessId,CommandLine` |
|
|
26
24
|
|
|
27
|
-
##
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
### lookup(query?, callback?)
|
|
28
|
+
Returns a list of processes matching the query.
|
|
28
29
|
|
|
29
|
-
### lookup()
|
|
30
|
-
Searches for the process by the specified `pid`.
|
|
31
30
|
```ts
|
|
32
|
-
import {lookup} from '@webpod/ps'
|
|
33
|
-
|
|
34
|
-
// Both callback and promise styles are supported
|
|
35
|
-
const list = await lookup({pid: 12345})
|
|
36
|
-
|
|
37
|
-
// or
|
|
38
|
-
lookup({pid: 12345}, (err, list) => {
|
|
39
|
-
if (err) {
|
|
40
|
-
throw new Error(err)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const [found] = list
|
|
44
|
-
if (found) {
|
|
45
|
-
console.log('PID: %s, COMMAND: %s, ARGUMENTS: %s', found.pid, found.command, found.arguments)
|
|
46
|
-
} else {
|
|
47
|
-
console.log('No such process found!')
|
|
48
|
-
}
|
|
49
|
-
})
|
|
31
|
+
import { lookup } from '@webpod/ps'
|
|
50
32
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
|
|
33
|
+
// Find by pid
|
|
34
|
+
const list = await lookup({ pid: 12345 })
|
|
35
|
+
// [{ pid: '12345', ppid: '123', command: '/usr/bin/node', arguments: ['server.js', '--port=3000'] }]
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const list = await lookup({
|
|
58
|
-
command: 'node', // it will be used to build a regex
|
|
59
|
-
arguments: '--debug',
|
|
60
|
-
})
|
|
37
|
+
// Filter by command and/or arguments (treated as RegExp)
|
|
38
|
+
const nodes = await lookup({ command: 'node', arguments: '--debug' })
|
|
61
39
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
40
|
+
// Filter by parent pid
|
|
41
|
+
const children = await lookup({ ppid: 82292 })
|
|
42
|
+
|
|
43
|
+
// Synchronous
|
|
44
|
+
const all = lookup.sync()
|
|
65
45
|
```
|
|
66
46
|
|
|
67
|
-
Unix
|
|
47
|
+
On Unix, you can override the default `ps` arguments via `psargs`:
|
|
68
48
|
```ts
|
|
69
|
-
lookup({
|
|
70
|
-
command: 'node',
|
|
71
|
-
psargs: 'ux'
|
|
72
|
-
}, (err, resultList) => {
|
|
73
|
-
// ...
|
|
74
|
-
})
|
|
49
|
+
const list = await lookup({ command: 'node', psargs: '-eo pid,ppid,comm' })
|
|
75
50
|
```
|
|
76
51
|
|
|
77
|
-
|
|
52
|
+
Callback style is also supported:
|
|
78
53
|
```ts
|
|
79
|
-
lookup({
|
|
80
|
-
command: 'mongod',
|
|
81
|
-
psargs: '-l',
|
|
82
|
-
ppid: 82292
|
|
83
|
-
}, (err, resultList) => {
|
|
84
|
-
// ...
|
|
85
|
-
})
|
|
54
|
+
lookup({ pid: 12345 }, (err, list) => { /* ... */ })
|
|
86
55
|
```
|
|
87
56
|
|
|
88
|
-
### tree()
|
|
89
|
-
Returns
|
|
57
|
+
### tree(opts?, callback?)
|
|
58
|
+
Returns child processes of a given parent pid.
|
|
59
|
+
|
|
90
60
|
```ts
|
|
91
61
|
import { tree } from '@webpod/ps'
|
|
92
62
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
[
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
]
|
|
99
|
-
|
|
63
|
+
// Direct children
|
|
64
|
+
const children = await tree(123)
|
|
65
|
+
// [
|
|
66
|
+
// { pid: '124', ppid: '123', command: 'node', arguments: ['worker.js'] },
|
|
67
|
+
// { pid: '125', ppid: '123', command: 'node', arguments: ['worker.js'] }
|
|
68
|
+
// ]
|
|
69
|
+
|
|
70
|
+
// All descendants
|
|
71
|
+
const all = await tree({ pid: 123, recursive: true })
|
|
72
|
+
// [
|
|
73
|
+
// { pid: '124', ppid: '123', ... },
|
|
74
|
+
// { pid: '125', ppid: '123', ... },
|
|
75
|
+
// { pid: '126', ppid: '124', ... },
|
|
76
|
+
// { pid: '127', ppid: '125', ... }
|
|
77
|
+
// ]
|
|
78
|
+
|
|
79
|
+
// Synchronous
|
|
80
|
+
const list = tree.sync({ pid: 123, recursive: true })
|
|
100
81
|
```
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const children = await tree({pid: 123, recursive: true})
|
|
105
|
-
/**
|
|
106
|
-
[
|
|
107
|
-
{pid: 124, ppid: 123},
|
|
108
|
-
{pid: 125, ppid: 123},
|
|
109
|
-
|
|
110
|
-
{pid: 126, ppid: 124},
|
|
111
|
-
{pid: 127, ppid: 124},
|
|
112
|
-
{pid: 128, ppid: 124},
|
|
113
|
-
|
|
114
|
-
{pid: 129, ppid: 125},
|
|
115
|
-
{pid: 130, ppid: 125},
|
|
116
|
-
]
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
// or syncronously
|
|
120
|
-
const list = tree.sync({pid: 123, recursive: true})
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### kill()
|
|
124
|
-
Eliminates the process by its `pid`.
|
|
83
|
+
### kill(pid, opts?, callback?)
|
|
84
|
+
Kills a process and waits for it to exit. The returned promise resolves once the process is confirmed dead, or rejects on timeout.
|
|
125
85
|
|
|
126
86
|
```ts
|
|
127
87
|
import { kill } from '@webpod/ps'
|
|
128
88
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
throw new Error(err)
|
|
132
|
-
} else {
|
|
133
|
-
console.log('Process %s has been killed!', pid)
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
```
|
|
89
|
+
// Sends SIGTERM, polls until the process is gone (default timeout 30s)
|
|
90
|
+
await kill(12345)
|
|
137
91
|
|
|
138
|
-
|
|
92
|
+
// With signal
|
|
93
|
+
await kill(12345, 'SIGKILL')
|
|
139
94
|
|
|
140
|
-
|
|
141
|
-
|
|
95
|
+
// With custom timeout (seconds) and polling interval (ms)
|
|
96
|
+
await kill(12345, { signal: 'SIGKILL', timeout: 10, interval: 250 })
|
|
142
97
|
|
|
143
|
-
//
|
|
144
|
-
kill(
|
|
145
|
-
|
|
146
|
-
throw new Error(err)
|
|
147
|
-
} else {
|
|
148
|
-
console.log('Process %s has been killed without a clean-up!', pid)
|
|
149
|
-
}
|
|
98
|
+
// With callback
|
|
99
|
+
await kill(12345, (err, pid) => {
|
|
100
|
+
// called when the process is confirmed dead or timeout is reached
|
|
150
101
|
})
|
|
151
102
|
```
|
|
152
103
|
|
|
153
|
-
You can also use object notation to specify more opts:
|
|
154
|
-
```ts
|
|
155
|
-
kill( '12345', {
|
|
156
|
-
signal: 'SIGKILL',
|
|
157
|
-
timeout: 10, // will set up a ten seconds timeout if the killing is not successful
|
|
158
|
-
}, () => {})
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Notice that the nodejs build-in `process.kill()` does not accept number as a signal, you will have to use string format.
|
|
162
|
-
|
|
163
104
|
## License
|
|
164
105
|
[MIT](./LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpod/ps",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A process lookup utility",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,12 +23,23 @@
|
|
|
23
23
|
"build:dts": "tsc --emitDeclarationOnly --outDir target/dts",
|
|
24
24
|
"build:docs": "typedoc --options src/main/typedoc",
|
|
25
25
|
"build:stamp": "npx buildstamp",
|
|
26
|
-
"test": "concurrently 'npm:test:lint' 'npm:test:unit' && npm run test:legacy",
|
|
26
|
+
"test": "concurrently 'npm:test:lint' 'npm:test:unit' 'npm:test:size' && npm run test:legacy",
|
|
27
27
|
"test:lint": "oxlint -c oxlintrc.json src/main/ts src/test/ts",
|
|
28
28
|
"test:unit": "c8 -r lcov -r text -o target/coverage -x src/scripts -x src/test -x target node --loader ts-node/esm --experimental-specifier-resolution=node src/scripts/test.mjs",
|
|
29
|
-
"test:legacy": "
|
|
29
|
+
"test:legacy": "mocha -t 0 -R spec src/test/legacy/test.cjs",
|
|
30
|
+
"test:size": "size-limit",
|
|
30
31
|
"publish:draft": "npm run build && npm publish --no-git-tag-version"
|
|
31
32
|
},
|
|
33
|
+
"size-limit": [
|
|
34
|
+
{
|
|
35
|
+
"path": "target/esm/index.mjs",
|
|
36
|
+
"limit": "4 kB"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"path": "target/cjs/index.cjs",
|
|
40
|
+
"limit": "5 kB"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
32
43
|
"files": [
|
|
33
44
|
"target/cjs",
|
|
34
45
|
"target/esm",
|
|
@@ -45,24 +56,28 @@
|
|
|
45
56
|
"zurk": "^0.11.10"
|
|
46
57
|
},
|
|
47
58
|
"devDependencies": {
|
|
59
|
+
"@size-limit/file": "^12.0.1",
|
|
48
60
|
"@types/node": "^25.5.2",
|
|
49
61
|
"c8": "^11.0.0",
|
|
50
62
|
"concurrently": "^9.2.1",
|
|
51
63
|
"esbuild": "^0.28.0",
|
|
52
64
|
"esbuild-node-externals": "^1.21.0",
|
|
53
65
|
"esbuild-plugin-entry-chunks": "^0.1.18",
|
|
54
|
-
"oxlint": "^1.58.0",
|
|
55
66
|
"fast-glob": "^3.3.3",
|
|
56
67
|
"minimist": "^1.2.8",
|
|
57
|
-
"mocha": "^
|
|
58
|
-
"
|
|
68
|
+
"mocha": "^12.0.0-beta-9.2",
|
|
69
|
+
"oxlint": "^1.59.0",
|
|
70
|
+
"size-limit": "^12.0.1",
|
|
59
71
|
"ts-node": "^10.9.2",
|
|
60
72
|
"typedoc": "^0.28.18",
|
|
61
|
-
"typescript": "^
|
|
73
|
+
"typescript": "^6.0.2"
|
|
62
74
|
},
|
|
63
75
|
"repository": {
|
|
64
76
|
"type": "git",
|
|
65
77
|
"url": "git://github.com/webpod/ps.git"
|
|
66
78
|
},
|
|
67
|
-
"license": "MIT"
|
|
79
|
+
"license": "MIT",
|
|
80
|
+
"overrides": {
|
|
81
|
+
"chokidar": "^4.0.3"
|
|
82
|
+
}
|
|
68
83
|
}
|
package/target/cjs/index.cjs
CHANGED
|
@@ -65,234 +65,210 @@ var import_node_fs = __toESM(require("node:fs"), 1);
|
|
|
65
65
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
66
66
|
var import_ingrid = require("@webpod/ingrid");
|
|
67
67
|
var import_spawn = require("zurk/spawn");
|
|
68
|
+
var noop = () => {
|
|
69
|
+
};
|
|
68
70
|
var IS_WIN = import_node_process.default.platform === "win32";
|
|
69
71
|
var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(import_node_os.default.release().split(".")[2], 10) >= 26e3;
|
|
72
|
+
var LOOKUP_FLOW = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
70
73
|
var LOOKUPS = {
|
|
71
74
|
wmic: {
|
|
72
75
|
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
73
76
|
args: [],
|
|
74
|
-
parse(stdout) {
|
|
75
|
-
return (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" });
|
|
76
|
-
}
|
|
77
|
+
parse: (stdout) => (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" })
|
|
77
78
|
},
|
|
78
79
|
ps: {
|
|
79
80
|
cmd: "ps",
|
|
80
81
|
args: ["-eo", "pid,ppid,args"],
|
|
81
|
-
parse(stdout) {
|
|
82
|
-
return (0, import_ingrid.parse)(stdout, { format: "unix" });
|
|
83
|
-
}
|
|
82
|
+
parse: (stdout) => (0, import_ingrid.parse)(stdout, { format: "unix" })
|
|
84
83
|
},
|
|
85
84
|
pwsh: {
|
|
86
85
|
cmd: "pwsh",
|
|
87
86
|
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
88
87
|
parse(stdout) {
|
|
89
|
-
let arr = [];
|
|
90
88
|
try {
|
|
91
|
-
arr = JSON.parse(stdout);
|
|
89
|
+
const arr = JSON.parse(stdout);
|
|
90
|
+
return arr.map((p) => ({
|
|
91
|
+
ProcessId: [String(p.ProcessId)],
|
|
92
|
+
ParentProcessId: [String(p.ParentProcessId)],
|
|
93
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
94
|
+
}));
|
|
92
95
|
} catch (e) {
|
|
93
96
|
return [];
|
|
94
97
|
}
|
|
95
|
-
return arr.map((p) => ({
|
|
96
|
-
ProcessId: [p.ProcessId + ""],
|
|
97
|
-
ParentProcessId: [p.ParentProcessId + ""],
|
|
98
|
-
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
99
|
-
}));
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
100
|
};
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
106
|
-
if (f.length > 3 && f[0] === '"')
|
|
107
|
-
return f[f.length - 1] === '"' ? isBin(f.slice(1, -1)) : false;
|
|
108
|
-
try {
|
|
109
|
-
if (!import_node_fs.default.existsSync(f)) return false;
|
|
110
|
-
const stat = import_node_fs.default.lstatSync(f);
|
|
111
|
-
return stat.isFile() || stat.isSymbolicLink();
|
|
112
|
-
} catch (e) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
|
|
117
|
-
var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
|
|
101
|
+
var lookup = (query = {}, cb = noop) => runLookup(query, cb, false);
|
|
102
|
+
var lookupSync = (query = {}, cb = noop) => runLookup(query, cb, true);
|
|
118
103
|
lookup.sync = lookupSync;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
const { promise, resolve, reject } = pFactory();
|
|
126
|
-
const result = [];
|
|
127
|
-
const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
128
|
-
const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
|
|
129
|
-
const callback = (err, { stdout }) => {
|
|
104
|
+
function runLookup(query, cb, sync) {
|
|
105
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
|
|
106
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
107
|
+
let result = [];
|
|
108
|
+
let error;
|
|
109
|
+
const handle = (err, { stdout }) => {
|
|
130
110
|
if (err) {
|
|
131
|
-
|
|
132
|
-
cb(err);
|
|
111
|
+
error = err;
|
|
133
112
|
return;
|
|
134
113
|
}
|
|
135
|
-
result
|
|
136
|
-
resolve(result);
|
|
137
|
-
cb(null, result);
|
|
114
|
+
result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
|
|
138
115
|
};
|
|
139
|
-
(
|
|
140
|
-
cmd,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
return Object.assign(promise, result);
|
|
149
|
-
};
|
|
150
|
-
var filterProcessList = (processList, query = {}) => {
|
|
151
|
-
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
152
|
-
const filters = [
|
|
153
|
-
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
154
|
-
(p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
|
|
155
|
-
(p) => query.ppid ? query.ppid + "" === p.ppid : true
|
|
156
|
-
];
|
|
157
|
-
return processList.filter(
|
|
158
|
-
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
|
|
159
|
-
);
|
|
160
|
-
};
|
|
161
|
-
var removeWmicPrefix = (stdout) => {
|
|
162
|
-
const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
|
|
163
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
|
|
164
|
-
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
165
|
-
};
|
|
166
|
-
var pickTree = (list, pid, recursive = false) => {
|
|
167
|
-
const children = list.filter((p) => p.ppid === pid + "");
|
|
168
|
-
return [
|
|
169
|
-
...children,
|
|
170
|
-
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
171
|
-
];
|
|
172
|
-
};
|
|
173
|
-
var _tree = ({
|
|
174
|
-
cb = noop,
|
|
175
|
-
opts,
|
|
176
|
-
sync = false
|
|
177
|
-
}) => {
|
|
178
|
-
if (typeof opts === "string" || typeof opts === "number") {
|
|
179
|
-
return _tree({ opts: { pid: opts }, cb, sync });
|
|
116
|
+
if (sync) {
|
|
117
|
+
(0, import_spawn.exec)({ cmd, args, sync: true, callback: handle, run(c) {
|
|
118
|
+
c();
|
|
119
|
+
} });
|
|
120
|
+
cb(error != null ? error : null, error ? void 0 : result);
|
|
121
|
+
if (error) throw error;
|
|
122
|
+
return result;
|
|
180
123
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
(0, import_spawn.exec)({
|
|
126
|
+
cmd,
|
|
127
|
+
args,
|
|
128
|
+
sync: false,
|
|
129
|
+
run(c) {
|
|
130
|
+
c();
|
|
131
|
+
},
|
|
132
|
+
callback(err, ctx) {
|
|
133
|
+
handle(err, ctx);
|
|
134
|
+
if (error) {
|
|
135
|
+
cb(error);
|
|
136
|
+
reject(error);
|
|
137
|
+
} else {
|
|
138
|
+
cb(null, result);
|
|
139
|
+
resolve(result);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
var tree = (_0, ..._1) => __async(null, [_0, ..._1], function* (opts, cb = noop) {
|
|
146
|
+
try {
|
|
147
|
+
const list = pickFromTree(yield lookup(), opts);
|
|
186
148
|
cb(null, list);
|
|
187
149
|
return list;
|
|
188
|
-
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
cb(err);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
var treeSync = (opts, cb = noop) => {
|
|
189
156
|
try {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
throw err;
|
|
194
|
-
});
|
|
157
|
+
const list = pickFromTree(lookupSync(), opts);
|
|
158
|
+
cb(null, list);
|
|
159
|
+
return list;
|
|
195
160
|
} catch (err) {
|
|
196
|
-
|
|
197
|
-
|
|
161
|
+
cb(err);
|
|
162
|
+
throw err;
|
|
198
163
|
}
|
|
199
164
|
};
|
|
200
|
-
var tree = (opts, cb) => __async(null, null, function* () {
|
|
201
|
-
return _tree({ opts, cb });
|
|
202
|
-
});
|
|
203
|
-
var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
|
|
204
165
|
tree.sync = treeSync;
|
|
166
|
+
var pickFromTree = (all, opts) => {
|
|
167
|
+
if (opts === void 0) return all;
|
|
168
|
+
const { pid, recursive = false } = typeof opts === "object" ? opts : { pid: opts };
|
|
169
|
+
return pickTree(all, pid, recursive);
|
|
170
|
+
};
|
|
171
|
+
var pickTree = (list, pid, recursive = false) => {
|
|
172
|
+
const children = list.filter((p) => p.ppid === String(pid));
|
|
173
|
+
return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
|
|
174
|
+
};
|
|
205
175
|
var kill = (pid, opts, next) => {
|
|
206
|
-
if (typeof opts
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (err) {
|
|
230
|
-
clearTimeout(checkTimeoutTimer);
|
|
231
|
-
reject(err);
|
|
232
|
-
finishCallback == null ? void 0 : finishCallback(err, pid);
|
|
233
|
-
} else if (list.length > 0) {
|
|
234
|
-
checkConfident = checkConfident - 1 || 0;
|
|
235
|
-
checkKilled(finishCallback);
|
|
236
|
-
} else {
|
|
237
|
-
checkConfident++;
|
|
238
|
-
if (checkConfident === 5) {
|
|
239
|
-
clearTimeout(checkTimeoutTimer);
|
|
240
|
-
resolve(pid);
|
|
241
|
-
finishCallback == null ? void 0 : finishCallback(null, pid);
|
|
242
|
-
} else {
|
|
243
|
-
checkKilled(finishCallback);
|
|
244
|
-
}
|
|
176
|
+
if (typeof opts === "function") return kill(pid, void 0, opts);
|
|
177
|
+
if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
|
|
178
|
+
const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
|
|
179
|
+
const sPid = String(pid);
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
let done = false;
|
|
182
|
+
const entry = { pid: sPid, registered: 0, interval, settle: noop };
|
|
183
|
+
const settle = (err) => {
|
|
184
|
+
if (done) return;
|
|
185
|
+
done = true;
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
killPending.delete(entry);
|
|
188
|
+
if (err) reject(err);
|
|
189
|
+
else resolve(pid);
|
|
190
|
+
next == null ? void 0 : next(err != null ? err : null, pid);
|
|
191
|
+
};
|
|
192
|
+
entry.settle = settle;
|
|
193
|
+
const timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
|
|
194
|
+
try {
|
|
195
|
+
import_node_process.default.kill(+pid, signal);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
settle(e);
|
|
198
|
+
return;
|
|
245
199
|
}
|
|
200
|
+
entry.registered = Date.now();
|
|
201
|
+
killPending.add(entry);
|
|
202
|
+
scheduleKillTick();
|
|
246
203
|
});
|
|
247
|
-
if (next) {
|
|
248
|
-
checkKilled(next);
|
|
249
|
-
checkTimeoutTimer = setTimeout(() => {
|
|
250
|
-
checkIsTimeout = true;
|
|
251
|
-
next(new Error("Kill process timeout"));
|
|
252
|
-
}, timeout * 1e3);
|
|
253
|
-
} else {
|
|
254
|
-
resolve(pid);
|
|
255
|
-
}
|
|
256
|
-
return promise;
|
|
257
204
|
};
|
|
258
|
-
var
|
|
205
|
+
var killPending = /* @__PURE__ */ new Set();
|
|
206
|
+
var killTickTimer = null;
|
|
207
|
+
var killTickRunning = false;
|
|
208
|
+
var scheduleKillTick = (lastStart = 0) => {
|
|
209
|
+
if (killTickTimer || killTickRunning || killPending.size === 0) return;
|
|
210
|
+
let minInterval = Infinity;
|
|
211
|
+
for (const k of killPending) if (k.interval < minInterval) minInterval = k.interval;
|
|
212
|
+
const delay = lastStart === 0 ? 0 : Math.max(0, lastStart + minInterval - Date.now());
|
|
213
|
+
killTickTimer = setTimeout(runKillTick, delay);
|
|
214
|
+
};
|
|
215
|
+
var runKillTick = () => {
|
|
216
|
+
killTickTimer = null;
|
|
217
|
+
if (killPending.size === 0) return;
|
|
218
|
+
killTickRunning = true;
|
|
219
|
+
const startedAt = Date.now();
|
|
220
|
+
lookup().then((list) => {
|
|
221
|
+
const alive = new Set(list.map((p) => p.pid));
|
|
222
|
+
for (const k of killPending) {
|
|
223
|
+
if (k.registered >= startedAt) continue;
|
|
224
|
+
if (!alive.has(k.pid)) k.settle();
|
|
225
|
+
}
|
|
226
|
+
killTickRunning = false;
|
|
227
|
+
scheduleKillTick(startedAt);
|
|
228
|
+
}, (err) => {
|
|
229
|
+
for (const k of killPending) k.settle(err);
|
|
230
|
+
killTickRunning = false;
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
259
234
|
var _a, _b;
|
|
260
235
|
const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
|
|
261
236
|
const ppid = (_b = d.PPID || d.ParentProcessId) == null ? void 0 : _b[0];
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
if (pid
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
let resolve;
|
|
279
|
-
let reject;
|
|
280
|
-
const promise = new Promise((res, rej) => {
|
|
281
|
-
resolve = res;
|
|
282
|
-
reject = rej;
|
|
283
|
-
});
|
|
284
|
-
return { resolve, reject, promise };
|
|
237
|
+
const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
238
|
+
const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
|
|
239
|
+
if (!pid || parts.length === 0) return [];
|
|
240
|
+
const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
|
|
241
|
+
const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
|
|
242
|
+
const args = binIdx === -1 ? [] : parts.slice(binIdx);
|
|
243
|
+
return [{ pid, ppid, command, arguments: args }];
|
|
244
|
+
});
|
|
245
|
+
var filterProcessList = (processList, query = {}) => {
|
|
246
|
+
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
|
|
247
|
+
const commandRe = query.command ? new RegExp(query.command, "i") : null;
|
|
248
|
+
const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
|
|
249
|
+
const ppid = query.ppid === void 0 ? null : String(query.ppid);
|
|
250
|
+
return processList.filter(
|
|
251
|
+
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
|
|
252
|
+
);
|
|
285
253
|
};
|
|
286
|
-
var
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
254
|
+
var removeWmicPrefix = (stdout) => {
|
|
255
|
+
const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
|
|
256
|
+
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
|
|
257
|
+
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
258
|
+
};
|
|
259
|
+
var isBin = (f) => {
|
|
260
|
+
if (f === "") return false;
|
|
261
|
+
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
262
|
+
if (f.length > 3 && f[0] === '"')
|
|
263
|
+
return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
|
|
264
|
+
try {
|
|
265
|
+
if (!import_node_fs.default.existsSync(f)) return false;
|
|
266
|
+
const stat = import_node_fs.default.lstatSync(f);
|
|
267
|
+
return stat.isFile() || stat.isSymbolicLink();
|
|
268
|
+
} catch (e) {
|
|
269
|
+
return false;
|
|
291
270
|
}
|
|
292
|
-
});
|
|
293
|
-
var noop = () => {
|
|
294
271
|
};
|
|
295
|
-
var identity = (v) => v;
|
|
296
272
|
|
|
297
273
|
// src/main/ts/index.ts
|
|
298
274
|
var index_default = { kill, lookup, lookupSync, tree, treeSync };
|
package/target/dts/index.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ declare const _default: {
|
|
|
8
8
|
};
|
|
9
9
|
lookupSync: (query?: import("./ps.js").TPsLookupQuery, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
|
|
10
10
|
tree: {
|
|
11
|
-
(opts?: string | number | import("./ps.js").TPsTreeOpts
|
|
12
|
-
sync: (opts?: string | number | import("./ps.js").TPsTreeOpts
|
|
11
|
+
(opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback): Promise<import("./ps.js").TPsLookupEntry[]>;
|
|
12
|
+
sync: (opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
|
|
13
13
|
};
|
|
14
|
-
treeSync: (opts?: string | number | import("./ps.js").TPsTreeOpts
|
|
14
|
+
treeSync: (opts?: string | number | import("./ps.js").TPsTreeOpts, cb?: import("./ps.js").TPsLookupCallback) => import("./ps.js").TPsLookupEntry[];
|
|
15
15
|
};
|
|
16
16
|
export default _default;
|
package/target/dts/ps.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type TIngridResponse } from '@webpod/ingrid';
|
|
2
|
-
export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
|
|
3
2
|
export type TPsLookupEntry = {
|
|
4
3
|
pid: string;
|
|
5
4
|
ppid?: string;
|
|
@@ -11,57 +10,45 @@ export type TPsLookupQuery = {
|
|
|
11
10
|
command?: string;
|
|
12
11
|
arguments?: string;
|
|
13
12
|
ppid?: number | string;
|
|
13
|
+
psargs?: string;
|
|
14
14
|
};
|
|
15
|
+
export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
|
|
15
16
|
export type TPsKillOptions = {
|
|
16
17
|
timeout?: number;
|
|
17
18
|
signal?: string | number | NodeJS.Signals;
|
|
19
|
+
/** Polling interval in ms between exit checks (default 200). */
|
|
20
|
+
interval?: number;
|
|
21
|
+
};
|
|
22
|
+
export type TPsTreeOpts = {
|
|
23
|
+
pid: string | number;
|
|
24
|
+
recursive?: boolean;
|
|
18
25
|
};
|
|
19
26
|
export type TPsNext = (err?: any, data?: any) => void;
|
|
20
27
|
/**
|
|
21
|
-
* Query
|
|
22
|
-
*
|
|
23
|
-
* @param {String|String[]} query.pid
|
|
24
|
-
* @param {String} query.command RegExp String
|
|
25
|
-
* @param {String} query.arguments RegExp String
|
|
26
|
-
* @param {String|String[]} query.psargs
|
|
27
|
-
* @param {TPsLookupCallback} cb
|
|
28
|
-
* @return {Promise<TPsLookupEntry[]>}
|
|
28
|
+
* Query running processes by pid, command, arguments or ppid.
|
|
29
|
+
* Supports both promise and callback styles.
|
|
29
30
|
*/
|
|
30
31
|
export declare const lookup: {
|
|
31
32
|
(query?: TPsLookupQuery, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]>;
|
|
32
33
|
sync: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => TPsLookupEntry[];
|
|
33
34
|
};
|
|
34
|
-
/**
|
|
35
|
-
* Looks up the process list synchronously
|
|
36
|
-
* @param query
|
|
37
|
-
* @param {String|String[]} query.pid
|
|
38
|
-
* @param {String} query.command RegExp String
|
|
39
|
-
* @param {String} query.arguments RegExp String
|
|
40
|
-
* @param {String|String[]} query.psargs
|
|
41
|
-
* @param {TPsLookupCallback} cb
|
|
42
|
-
* @return {TPsLookupEntry[]}
|
|
43
|
-
*/
|
|
35
|
+
/** Synchronous version of {@link lookup}. */
|
|
44
36
|
export declare const lookupSync: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => TPsLookupEntry[];
|
|
45
|
-
|
|
46
|
-
export declare const removeWmicPrefix: (stdout: string) => string;
|
|
47
|
-
export type TPsTreeOpts = {
|
|
48
|
-
pid: string | number;
|
|
49
|
-
recursive?: boolean;
|
|
50
|
-
};
|
|
51
|
-
export declare const pickTree: (list: TPsLookupEntry[], pid: string | number, recursive?: boolean) => TPsLookupEntry[];
|
|
37
|
+
/** Returns child processes of the given parent pid. */
|
|
52
38
|
export declare const tree: {
|
|
53
|
-
(opts?: string | number | TPsTreeOpts
|
|
54
|
-
sync: (opts?: string | number | TPsTreeOpts
|
|
39
|
+
(opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback): Promise<TPsLookupEntry[]>;
|
|
40
|
+
sync: (opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback) => TPsLookupEntry[];
|
|
55
41
|
};
|
|
56
|
-
|
|
42
|
+
/** Synchronous version of {@link tree}. */
|
|
43
|
+
export declare const treeSync: (opts?: string | number | TPsTreeOpts, cb?: TPsLookupCallback) => TPsLookupEntry[];
|
|
44
|
+
export declare const pickTree: (list: TPsLookupEntry[], pid: string | number, recursive?: boolean) => TPsLookupEntry[];
|
|
57
45
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @param pid
|
|
60
|
-
* @param
|
|
61
|
-
* @param
|
|
62
|
-
* @param {number} opts.timeout
|
|
63
|
-
* @param next
|
|
46
|
+
* Kills a process by pid.
|
|
47
|
+
* @param pid - Process ID to kill
|
|
48
|
+
* @param opts - Signal, options object, or callback
|
|
49
|
+
* @param next - Callback invoked when kill is confirmed or timed out
|
|
64
50
|
*/
|
|
65
51
|
export declare const kill: (pid: string | number, opts?: TPsNext | TPsKillOptions | TPsKillOptions["signal"], next?: TPsNext) => Promise<void>;
|
|
66
52
|
export declare const normalizeOutput: (data: TIngridResponse) => TPsLookupEntry[];
|
|
67
|
-
export
|
|
53
|
+
export declare const filterProcessList: (processList: TPsLookupEntry[], query?: TPsLookupQuery) => TPsLookupEntry[];
|
|
54
|
+
export declare const removeWmicPrefix: (stdout: string) => string;
|
package/target/esm/index.mjs
CHANGED
|
@@ -4,231 +4,209 @@ import fs from "node:fs";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import { parse } from "@webpod/ingrid";
|
|
6
6
|
import { exec } from "zurk/spawn";
|
|
7
|
+
var noop = () => {
|
|
8
|
+
};
|
|
7
9
|
var IS_WIN = process.platform === "win32";
|
|
8
10
|
var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(os.release().split(".")[2], 10) >= 26e3;
|
|
11
|
+
var LOOKUP_FLOW = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
9
12
|
var LOOKUPS = {
|
|
10
13
|
wmic: {
|
|
11
14
|
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
12
15
|
args: [],
|
|
13
|
-
parse(stdout) {
|
|
14
|
-
return parse(removeWmicPrefix(stdout), { format: "win" });
|
|
15
|
-
}
|
|
16
|
+
parse: (stdout) => parse(removeWmicPrefix(stdout), { format: "win" })
|
|
16
17
|
},
|
|
17
18
|
ps: {
|
|
18
19
|
cmd: "ps",
|
|
19
20
|
args: ["-eo", "pid,ppid,args"],
|
|
20
|
-
parse(stdout) {
|
|
21
|
-
return parse(stdout, { format: "unix" });
|
|
22
|
-
}
|
|
21
|
+
parse: (stdout) => parse(stdout, { format: "unix" })
|
|
23
22
|
},
|
|
24
23
|
pwsh: {
|
|
25
24
|
cmd: "pwsh",
|
|
26
25
|
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
27
26
|
parse(stdout) {
|
|
28
|
-
let arr = [];
|
|
29
27
|
try {
|
|
30
|
-
arr = JSON.parse(stdout);
|
|
28
|
+
const arr = JSON.parse(stdout);
|
|
29
|
+
return arr.map((p) => ({
|
|
30
|
+
ProcessId: [String(p.ProcessId)],
|
|
31
|
+
ParentProcessId: [String(p.ParentProcessId)],
|
|
32
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
33
|
+
}));
|
|
31
34
|
} catch {
|
|
32
35
|
return [];
|
|
33
36
|
}
|
|
34
|
-
return arr.map((p) => ({
|
|
35
|
-
ProcessId: [p.ProcessId + ""],
|
|
36
|
-
ParentProcessId: [p.ParentProcessId + ""],
|
|
37
|
-
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
38
|
-
}));
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
39
|
};
|
|
42
|
-
var
|
|
43
|
-
|
|
44
|
-
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
45
|
-
if (f.length > 3 && f[0] === '"')
|
|
46
|
-
return f[f.length - 1] === '"' ? isBin(f.slice(1, -1)) : false;
|
|
47
|
-
try {
|
|
48
|
-
if (!fs.existsSync(f)) return false;
|
|
49
|
-
const stat = fs.lstatSync(f);
|
|
50
|
-
return stat.isFile() || stat.isSymbolicLink();
|
|
51
|
-
} catch {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
|
|
56
|
-
var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
|
|
40
|
+
var lookup = (query = {}, cb = noop) => runLookup(query, cb, false);
|
|
41
|
+
var lookupSync = (query = {}, cb = noop) => runLookup(query, cb, true);
|
|
57
42
|
lookup.sync = lookupSync;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
const { promise, resolve, reject } = pFactory();
|
|
65
|
-
const result = [];
|
|
66
|
-
const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
67
|
-
const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
|
|
68
|
-
const callback = (err, { stdout }) => {
|
|
43
|
+
function runLookup(query, cb, sync) {
|
|
44
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[LOOKUP_FLOW];
|
|
45
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
46
|
+
let result = [];
|
|
47
|
+
let error;
|
|
48
|
+
const handle = (err, { stdout }) => {
|
|
69
49
|
if (err) {
|
|
70
|
-
|
|
71
|
-
cb(err);
|
|
50
|
+
error = err;
|
|
72
51
|
return;
|
|
73
52
|
}
|
|
74
|
-
result
|
|
75
|
-
resolve(result);
|
|
76
|
-
cb(null, result);
|
|
53
|
+
result = filterProcessList(normalizeOutput(parseOutput(stdout)), query);
|
|
77
54
|
};
|
|
78
|
-
|
|
79
|
-
cmd,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return Object.assign(promise, result);
|
|
88
|
-
};
|
|
89
|
-
var filterProcessList = (processList, query = {}) => {
|
|
90
|
-
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
91
|
-
const filters = [
|
|
92
|
-
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
93
|
-
(p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
|
|
94
|
-
(p) => query.ppid ? query.ppid + "" === p.ppid : true
|
|
95
|
-
];
|
|
96
|
-
return processList.filter(
|
|
97
|
-
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
|
|
98
|
-
);
|
|
99
|
-
};
|
|
100
|
-
var removeWmicPrefix = (stdout) => {
|
|
101
|
-
const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
|
|
102
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
|
|
103
|
-
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
104
|
-
};
|
|
105
|
-
var pickTree = (list, pid, recursive = false) => {
|
|
106
|
-
const children = list.filter((p) => p.ppid === pid + "");
|
|
107
|
-
return [
|
|
108
|
-
...children,
|
|
109
|
-
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
110
|
-
];
|
|
111
|
-
};
|
|
112
|
-
var _tree = ({
|
|
113
|
-
cb = noop,
|
|
114
|
-
opts,
|
|
115
|
-
sync = false
|
|
116
|
-
}) => {
|
|
117
|
-
if (typeof opts === "string" || typeof opts === "number") {
|
|
118
|
-
return _tree({ opts: { pid: opts }, cb, sync });
|
|
55
|
+
if (sync) {
|
|
56
|
+
exec({ cmd, args, sync: true, callback: handle, run(c) {
|
|
57
|
+
c();
|
|
58
|
+
} });
|
|
59
|
+
cb(error ?? null, error ? void 0 : result);
|
|
60
|
+
if (error) throw error;
|
|
61
|
+
return result;
|
|
119
62
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
exec({
|
|
65
|
+
cmd,
|
|
66
|
+
args,
|
|
67
|
+
sync: false,
|
|
68
|
+
run(c) {
|
|
69
|
+
c();
|
|
70
|
+
},
|
|
71
|
+
callback(err, ctx) {
|
|
72
|
+
handle(err, ctx);
|
|
73
|
+
if (error) {
|
|
74
|
+
cb(error);
|
|
75
|
+
reject(error);
|
|
76
|
+
} else {
|
|
77
|
+
cb(null, result);
|
|
78
|
+
resolve(result);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
var tree = async (opts, cb = noop) => {
|
|
85
|
+
try {
|
|
86
|
+
const list = pickFromTree(await lookup(), opts);
|
|
125
87
|
cb(null, list);
|
|
126
88
|
return list;
|
|
127
|
-
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
cb(err);
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var treeSync = (opts, cb = noop) => {
|
|
128
95
|
try {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
throw err;
|
|
133
|
-
});
|
|
96
|
+
const list = pickFromTree(lookupSync(), opts);
|
|
97
|
+
cb(null, list);
|
|
98
|
+
return list;
|
|
134
99
|
} catch (err) {
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
cb(err);
|
|
101
|
+
throw err;
|
|
137
102
|
}
|
|
138
103
|
};
|
|
139
|
-
var tree = async (opts, cb) => _tree({ opts, cb });
|
|
140
|
-
var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
|
|
141
104
|
tree.sync = treeSync;
|
|
105
|
+
var pickFromTree = (all, opts) => {
|
|
106
|
+
if (opts === void 0) return all;
|
|
107
|
+
const { pid, recursive = false } = typeof opts === "object" ? opts : { pid: opts };
|
|
108
|
+
return pickTree(all, pid, recursive);
|
|
109
|
+
};
|
|
110
|
+
var pickTree = (list, pid, recursive = false) => {
|
|
111
|
+
const children = list.filter((p) => p.ppid === String(pid));
|
|
112
|
+
return recursive ? children.flatMap((p) => [p, ...pickTree(list, p.pid, true)]) : children;
|
|
113
|
+
};
|
|
142
114
|
var kill = (pid, opts, next) => {
|
|
143
|
-
if (typeof opts
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (err) {
|
|
167
|
-
clearTimeout(checkTimeoutTimer);
|
|
168
|
-
reject(err);
|
|
169
|
-
finishCallback?.(err, pid);
|
|
170
|
-
} else if (list.length > 0) {
|
|
171
|
-
checkConfident = checkConfident - 1 || 0;
|
|
172
|
-
checkKilled(finishCallback);
|
|
173
|
-
} else {
|
|
174
|
-
checkConfident++;
|
|
175
|
-
if (checkConfident === 5) {
|
|
176
|
-
clearTimeout(checkTimeoutTimer);
|
|
177
|
-
resolve(pid);
|
|
178
|
-
finishCallback?.(null, pid);
|
|
179
|
-
} else {
|
|
180
|
-
checkKilled(finishCallback);
|
|
181
|
-
}
|
|
115
|
+
if (typeof opts === "function") return kill(pid, void 0, opts);
|
|
116
|
+
if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
|
|
117
|
+
const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
|
|
118
|
+
const sPid = String(pid);
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
let done = false;
|
|
121
|
+
const entry = { pid: sPid, registered: 0, interval, settle: noop };
|
|
122
|
+
const settle = (err) => {
|
|
123
|
+
if (done) return;
|
|
124
|
+
done = true;
|
|
125
|
+
clearTimeout(timer);
|
|
126
|
+
killPending.delete(entry);
|
|
127
|
+
if (err) reject(err);
|
|
128
|
+
else resolve(pid);
|
|
129
|
+
next?.(err ?? null, pid);
|
|
130
|
+
};
|
|
131
|
+
entry.settle = settle;
|
|
132
|
+
const timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
|
|
133
|
+
try {
|
|
134
|
+
process.kill(+pid, signal);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
settle(e);
|
|
137
|
+
return;
|
|
182
138
|
}
|
|
139
|
+
entry.registered = Date.now();
|
|
140
|
+
killPending.add(entry);
|
|
141
|
+
scheduleKillTick();
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
var killPending = /* @__PURE__ */ new Set();
|
|
145
|
+
var killTickTimer = null;
|
|
146
|
+
var killTickRunning = false;
|
|
147
|
+
var scheduleKillTick = (lastStart = 0) => {
|
|
148
|
+
if (killTickTimer || killTickRunning || killPending.size === 0) return;
|
|
149
|
+
let minInterval = Infinity;
|
|
150
|
+
for (const k of killPending) if (k.interval < minInterval) minInterval = k.interval;
|
|
151
|
+
const delay = lastStart === 0 ? 0 : Math.max(0, lastStart + minInterval - Date.now());
|
|
152
|
+
killTickTimer = setTimeout(runKillTick, delay);
|
|
153
|
+
};
|
|
154
|
+
var runKillTick = () => {
|
|
155
|
+
killTickTimer = null;
|
|
156
|
+
if (killPending.size === 0) return;
|
|
157
|
+
killTickRunning = true;
|
|
158
|
+
const startedAt = Date.now();
|
|
159
|
+
lookup().then((list) => {
|
|
160
|
+
const alive = new Set(list.map((p) => p.pid));
|
|
161
|
+
for (const k of killPending) {
|
|
162
|
+
if (k.registered >= startedAt) continue;
|
|
163
|
+
if (!alive.has(k.pid)) k.settle();
|
|
164
|
+
}
|
|
165
|
+
killTickRunning = false;
|
|
166
|
+
scheduleKillTick(startedAt);
|
|
167
|
+
}, (err) => {
|
|
168
|
+
for (const k of killPending) k.settle(err);
|
|
169
|
+
killTickRunning = false;
|
|
183
170
|
});
|
|
184
|
-
if (next) {
|
|
185
|
-
checkKilled(next);
|
|
186
|
-
checkTimeoutTimer = setTimeout(() => {
|
|
187
|
-
checkIsTimeout = true;
|
|
188
|
-
next(new Error("Kill process timeout"));
|
|
189
|
-
}, timeout * 1e3);
|
|
190
|
-
} else {
|
|
191
|
-
resolve(pid);
|
|
192
|
-
}
|
|
193
|
-
return promise;
|
|
194
171
|
};
|
|
195
|
-
var normalizeOutput = (data) => data.
|
|
172
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
196
173
|
const pid = (d.PID || d.ProcessId)?.[0];
|
|
197
174
|
const ppid = (d.PPID || d.ParentProcessId)?.[0];
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
if (pid
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
});
|
|
220
|
-
return { resolve, reject, promise };
|
|
175
|
+
const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
176
|
+
const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
|
|
177
|
+
if (!pid || parts.length === 0) return [];
|
|
178
|
+
const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
|
|
179
|
+
const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
|
|
180
|
+
const args = binIdx === -1 ? [] : parts.slice(binIdx);
|
|
181
|
+
return [{ pid, ppid, command, arguments: args }];
|
|
182
|
+
});
|
|
183
|
+
var filterProcessList = (processList, query = {}) => {
|
|
184
|
+
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
|
|
185
|
+
const commandRe = query.command ? new RegExp(query.command, "i") : null;
|
|
186
|
+
const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
|
|
187
|
+
const ppid = query.ppid === void 0 ? null : String(query.ppid);
|
|
188
|
+
return processList.filter(
|
|
189
|
+
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
var removeWmicPrefix = (stdout) => {
|
|
193
|
+
const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
|
|
194
|
+
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
|
|
195
|
+
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
221
196
|
};
|
|
222
|
-
var
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
197
|
+
var isBin = (f) => {
|
|
198
|
+
if (f === "") return false;
|
|
199
|
+
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
200
|
+
if (f.length > 3 && f[0] === '"')
|
|
201
|
+
return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
|
|
202
|
+
try {
|
|
203
|
+
if (!fs.existsSync(f)) return false;
|
|
204
|
+
const stat = fs.lstatSync(f);
|
|
205
|
+
return stat.isFile() || stat.isSymbolicLink();
|
|
206
|
+
} catch {
|
|
207
|
+
return false;
|
|
227
208
|
}
|
|
228
|
-
});
|
|
229
|
-
var noop = () => {
|
|
230
209
|
};
|
|
231
|
-
var identity = (v) => v;
|
|
232
210
|
|
|
233
211
|
// src/main/ts/index.ts
|
|
234
212
|
var index_default = { kill, lookup, lookupSync, tree, treeSync };
|