@webpod/ps 1.0.0 → 1.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 +63 -122
- package/package.json +26 -15
- package/target/cjs/index.cjs +120 -152
- package/target/dts/index.d.ts +3 -3
- package/target/dts/ps.d.ts +23 -36
- package/target/esm/index.mjs +116 -150
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.
|
|
3
|
+
"version": "1.2.0",
|
|
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
|
|
27
|
-
"test:lint": "
|
|
26
|
+
"test": "concurrently 'npm:test:lint' 'npm:test:unit' 'npm:test:size' && npm run test:legacy",
|
|
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
|
-
"
|
|
29
|
+
"test:legacy": "node ./node_modules/mocha/bin/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",
|
|
@@ -42,23 +53,23 @@
|
|
|
42
53
|
],
|
|
43
54
|
"dependencies": {
|
|
44
55
|
"@webpod/ingrid": "^1.1.1",
|
|
45
|
-
"zurk": "^0.11.
|
|
56
|
+
"zurk": "^0.11.10"
|
|
46
57
|
},
|
|
47
58
|
"devDependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
59
|
+
"@size-limit/file": "^12.0.1",
|
|
60
|
+
"@types/node": "^25.5.2",
|
|
61
|
+
"c8": "^11.0.0",
|
|
50
62
|
"concurrently": "^9.2.1",
|
|
51
|
-
"esbuild": "^0.
|
|
52
|
-
"esbuild-node-externals": "^1.
|
|
53
|
-
"esbuild-plugin-entry-chunks": "^0.1.
|
|
54
|
-
"eslint": "^8.57.0",
|
|
55
|
-
"eslint-config-qiwi": "^2.1.3",
|
|
63
|
+
"esbuild": "^0.28.0",
|
|
64
|
+
"esbuild-node-externals": "^1.21.0",
|
|
65
|
+
"esbuild-plugin-entry-chunks": "^0.1.18",
|
|
56
66
|
"fast-glob": "^3.3.3",
|
|
57
67
|
"minimist": "^1.2.8",
|
|
58
|
-
"mocha": "^
|
|
59
|
-
"
|
|
68
|
+
"mocha": "^11.7.5",
|
|
69
|
+
"oxlint": "^1.58.0",
|
|
70
|
+
"size-limit": "^12.0.1",
|
|
60
71
|
"ts-node": "^10.9.2",
|
|
61
|
-
"typedoc": "^0.28.
|
|
72
|
+
"typedoc": "^0.28.18",
|
|
62
73
|
"typescript": "^5.9.2"
|
|
63
74
|
},
|
|
64
75
|
"repository": {
|
package/target/cjs/index.cjs
CHANGED
|
@@ -71,213 +71,181 @@ var LOOKUPS = {
|
|
|
71
71
|
wmic: {
|
|
72
72
|
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
73
73
|
args: [],
|
|
74
|
-
parse(stdout) {
|
|
75
|
-
return (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" });
|
|
76
|
-
}
|
|
74
|
+
parse: (stdout) => (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" })
|
|
77
75
|
},
|
|
78
76
|
ps: {
|
|
79
77
|
cmd: "ps",
|
|
80
|
-
args: ["-
|
|
81
|
-
parse(stdout) {
|
|
82
|
-
return (0, import_ingrid.parse)(stdout, { format: "unix" });
|
|
83
|
-
}
|
|
78
|
+
args: ["-eo", "pid,ppid,args"],
|
|
79
|
+
parse: (stdout) => (0, import_ingrid.parse)(stdout, { format: "unix" })
|
|
84
80
|
},
|
|
85
81
|
pwsh: {
|
|
86
82
|
cmd: "pwsh",
|
|
87
83
|
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
88
84
|
parse(stdout) {
|
|
89
|
-
let arr = [];
|
|
90
85
|
try {
|
|
91
|
-
arr = JSON.parse(stdout);
|
|
86
|
+
const arr = JSON.parse(stdout);
|
|
87
|
+
return arr.map((p) => ({
|
|
88
|
+
ProcessId: [String(p.ProcessId)],
|
|
89
|
+
ParentProcessId: [String(p.ParentProcessId)],
|
|
90
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
91
|
+
}));
|
|
92
92
|
} catch (e) {
|
|
93
93
|
return [];
|
|
94
94
|
}
|
|
95
|
-
return arr.map((p) => ({
|
|
96
|
-
ProcessId: [p.ProcessId + ""],
|
|
97
|
-
ParentProcessId: [p.ParentProcessId + ""],
|
|
98
|
-
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
99
|
-
}));
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
97
|
};
|
|
103
|
-
var
|
|
104
|
-
if (f === "") return false;
|
|
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
|
-
};
|
|
98
|
+
var lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
116
99
|
var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
|
|
117
100
|
var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
|
|
118
101
|
lookup.sync = lookupSync;
|
|
119
|
-
var _lookup = ({
|
|
120
|
-
|
|
121
|
-
cb = noop,
|
|
122
|
-
sync = false
|
|
123
|
-
}) => {
|
|
124
|
-
const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
|
|
125
|
-
const { promise, resolve, reject } = pFactory();
|
|
102
|
+
var _lookup = ({ query = {}, cb = noop, sync = false }) => {
|
|
103
|
+
const { promise, resolve, reject } = sync ? makeSyncDeferred([]) : makeDeferred();
|
|
126
104
|
const result = [];
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
parse: parse2,
|
|
130
|
-
cmd,
|
|
131
|
-
args
|
|
132
|
-
} = LOOKUPS[lookupFlow];
|
|
105
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
|
|
106
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
133
107
|
const callback = (err, { stdout }) => {
|
|
134
108
|
if (err) {
|
|
135
109
|
reject(err);
|
|
136
110
|
cb(err);
|
|
137
111
|
return;
|
|
138
112
|
}
|
|
139
|
-
result.push(...filterProcessList(normalizeOutput(
|
|
113
|
+
result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
|
|
140
114
|
resolve(result);
|
|
141
115
|
cb(null, result);
|
|
142
116
|
};
|
|
143
|
-
(0, import_spawn.exec)({
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
callback,
|
|
147
|
-
sync,
|
|
148
|
-
run(cb2) {
|
|
149
|
-
cb2();
|
|
150
|
-
}
|
|
151
|
-
});
|
|
117
|
+
(0, import_spawn.exec)({ cmd, args, callback, sync, run(cb2) {
|
|
118
|
+
cb2();
|
|
119
|
+
} });
|
|
152
120
|
return Object.assign(promise, result);
|
|
153
121
|
};
|
|
154
|
-
var
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
];
|
|
161
|
-
return processList.filter(
|
|
162
|
-
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
|
|
163
|
-
);
|
|
164
|
-
};
|
|
165
|
-
var removeWmicPrefix = (stdout) => {
|
|
166
|
-
const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
|
|
167
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
|
|
168
|
-
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
169
|
-
};
|
|
170
|
-
var pickTree = (list, pid, recursive = false) => {
|
|
171
|
-
const children = list.filter((p) => p.ppid === pid + "");
|
|
172
|
-
return [
|
|
173
|
-
...children,
|
|
174
|
-
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
175
|
-
];
|
|
176
|
-
};
|
|
177
|
-
var _tree = ({
|
|
178
|
-
cb = noop,
|
|
179
|
-
opts,
|
|
180
|
-
sync = false
|
|
181
|
-
}) => {
|
|
122
|
+
var tree = (opts, cb) => __async(null, null, function* () {
|
|
123
|
+
return _tree({ opts, cb });
|
|
124
|
+
});
|
|
125
|
+
var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
|
|
126
|
+
tree.sync = treeSync;
|
|
127
|
+
var _tree = ({ cb = noop, opts, sync = false }) => {
|
|
182
128
|
if (typeof opts === "string" || typeof opts === "number") {
|
|
183
129
|
return _tree({ opts: { pid: opts }, cb, sync });
|
|
184
130
|
}
|
|
185
|
-
const onError = (err) => cb(err);
|
|
186
131
|
const onData = (all) => {
|
|
132
|
+
var _a;
|
|
187
133
|
if (opts === void 0) return all;
|
|
188
|
-
const
|
|
189
|
-
const list = pickTree(all, pid, recursive);
|
|
134
|
+
const list = pickTree(all, opts.pid, (_a = opts.recursive) != null ? _a : false);
|
|
190
135
|
cb(null, list);
|
|
191
136
|
return list;
|
|
192
137
|
};
|
|
138
|
+
const onError = (err) => {
|
|
139
|
+
cb(err);
|
|
140
|
+
throw err;
|
|
141
|
+
};
|
|
193
142
|
try {
|
|
194
143
|
const all = _lookup({ sync });
|
|
195
|
-
return sync ? onData(all) : all.then(onData,
|
|
196
|
-
onError(err);
|
|
197
|
-
throw err;
|
|
198
|
-
});
|
|
144
|
+
return sync ? onData(all) : all.then(onData, onError);
|
|
199
145
|
} catch (err) {
|
|
200
|
-
|
|
146
|
+
cb(err);
|
|
201
147
|
return Promise.reject(err);
|
|
202
148
|
}
|
|
203
149
|
};
|
|
204
|
-
var
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
var
|
|
208
|
-
|
|
150
|
+
var inflight = null;
|
|
151
|
+
var queued = null;
|
|
152
|
+
var sharedSnapshot = (since) => {
|
|
153
|
+
var _a;
|
|
154
|
+
if (inflight && inflight.startedAt >= since) return inflight.promise;
|
|
155
|
+
if (queued) return queued;
|
|
156
|
+
const after = (_a = inflight == null ? void 0 : inflight.promise.catch(noop)) != null ? _a : Promise.resolve();
|
|
157
|
+
return queued = after.then(() => {
|
|
158
|
+
queued = null;
|
|
159
|
+
const startedAt = Date.now();
|
|
160
|
+
const promise = lookup().then((list) => ({ startedAt, list }));
|
|
161
|
+
inflight = { startedAt, promise };
|
|
162
|
+
return promise.finally(() => {
|
|
163
|
+
inflight = (inflight == null ? void 0 : inflight.promise) === promise ? null : inflight;
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
var pickTree = (list, pid, recursive = false) => {
|
|
168
|
+
const children = list.filter((p) => p.ppid === String(pid));
|
|
169
|
+
return [
|
|
170
|
+
...children,
|
|
171
|
+
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
172
|
+
];
|
|
173
|
+
};
|
|
209
174
|
var kill = (pid, opts, next) => {
|
|
210
|
-
if (typeof opts
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
if (typeof opts == "string" || typeof opts == "number") {
|
|
214
|
-
return kill(pid, { signal: opts }, next);
|
|
215
|
-
}
|
|
175
|
+
if (typeof opts === "function") return kill(pid, void 0, opts);
|
|
176
|
+
if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
|
|
216
177
|
const { promise, resolve, reject } = makeDeferred();
|
|
217
|
-
const {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
178
|
+
const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
|
|
179
|
+
const sPid = String(pid);
|
|
180
|
+
let done = false;
|
|
181
|
+
const state = {};
|
|
182
|
+
const settle = (err) => {
|
|
183
|
+
if (done) return;
|
|
184
|
+
done = true;
|
|
185
|
+
clearTimeout(state.timer);
|
|
186
|
+
if (err) reject(err);
|
|
187
|
+
else resolve(pid);
|
|
188
|
+
next == null ? void 0 : next(err != null ? err : null, pid);
|
|
189
|
+
};
|
|
221
190
|
try {
|
|
222
191
|
import_node_process.default.kill(+pid, signal);
|
|
223
192
|
} catch (e) {
|
|
224
|
-
|
|
225
|
-
next == null ? void 0 : next(e);
|
|
193
|
+
settle(e);
|
|
226
194
|
return promise;
|
|
227
195
|
}
|
|
228
|
-
let
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
reject(err);
|
|
236
|
-
finishCallback == null ? void 0 : finishCallback(err, pid);
|
|
237
|
-
} else if (list.length > 0) {
|
|
238
|
-
checkConfident = checkConfident - 1 || 0;
|
|
239
|
-
checkKilled(finishCallback);
|
|
196
|
+
let since = Date.now();
|
|
197
|
+
state.timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
|
|
198
|
+
const poll = () => sharedSnapshot(since).then(({ startedAt, list }) => {
|
|
199
|
+
if (done) return;
|
|
200
|
+
since = startedAt + 1;
|
|
201
|
+
if (list.some((p) => p.pid === sPid)) {
|
|
202
|
+
setTimeout(poll, Math.max(0, startedAt + interval - Date.now()));
|
|
240
203
|
} else {
|
|
241
|
-
|
|
242
|
-
if (checkConfident === 5) {
|
|
243
|
-
clearTimeout(checkTimeoutTimer);
|
|
244
|
-
resolve(pid);
|
|
245
|
-
finishCallback == null ? void 0 : finishCallback(null, pid);
|
|
246
|
-
} else {
|
|
247
|
-
checkKilled(finishCallback);
|
|
248
|
-
}
|
|
204
|
+
settle();
|
|
249
205
|
}
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
checkKilled(next);
|
|
253
|
-
checkTimeoutTimer = setTimeout(() => {
|
|
254
|
-
checkIsTimeout = true;
|
|
255
|
-
next(new Error("Kill process timeout"));
|
|
256
|
-
}, timeout * 1e3);
|
|
257
|
-
} else {
|
|
258
|
-
resolve(pid);
|
|
259
|
-
}
|
|
206
|
+
}, settle);
|
|
207
|
+
poll();
|
|
260
208
|
return promise;
|
|
261
209
|
};
|
|
262
|
-
var normalizeOutput = (data) => data.
|
|
210
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
263
211
|
var _a, _b;
|
|
264
212
|
const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
|
|
265
213
|
const ppid = (_b = d.PPID || d.ParentProcessId) == null ? void 0 : _b[0];
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
if (pid
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
214
|
+
const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
215
|
+
const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
|
|
216
|
+
if (!pid || parts.length === 0) return [];
|
|
217
|
+
const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
|
|
218
|
+
const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
|
|
219
|
+
const args = binIdx === -1 ? [] : parts.slice(binIdx);
|
|
220
|
+
return [{ pid, ppid, command, arguments: args }];
|
|
221
|
+
});
|
|
222
|
+
var filterProcessList = (processList, query = {}) => {
|
|
223
|
+
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
|
|
224
|
+
const commandRe = query.command ? new RegExp(query.command, "i") : null;
|
|
225
|
+
const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
|
|
226
|
+
const ppid = query.ppid === void 0 ? null : String(query.ppid);
|
|
227
|
+
return processList.filter(
|
|
228
|
+
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
var removeWmicPrefix = (stdout) => {
|
|
232
|
+
const s = stdout.indexOf(LOOKUPS.wmic.cmd + import_node_os.default.EOL);
|
|
233
|
+
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.default.EOL) : stdout.length;
|
|
234
|
+
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
235
|
+
};
|
|
236
|
+
var isBin = (f) => {
|
|
237
|
+
if (f === "") return false;
|
|
238
|
+
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
239
|
+
if (f.length > 3 && f[0] === '"')
|
|
240
|
+
return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
|
|
241
|
+
try {
|
|
242
|
+
if (!import_node_fs.default.existsSync(f)) return false;
|
|
243
|
+
const stat = import_node_fs.default.lstatSync(f);
|
|
244
|
+
return stat.isFile() || stat.isSymbolicLink();
|
|
245
|
+
} catch (e) {
|
|
246
|
+
return false;
|
|
278
247
|
}
|
|
279
|
-
|
|
280
|
-
}, []);
|
|
248
|
+
};
|
|
281
249
|
var makeDeferred = () => {
|
|
282
250
|
let resolve;
|
|
283
251
|
let reject;
|
|
@@ -287,16 +255,16 @@ var makeDeferred = () => {
|
|
|
287
255
|
});
|
|
288
256
|
return { resolve, reject, promise };
|
|
289
257
|
};
|
|
290
|
-
var
|
|
291
|
-
promise:
|
|
292
|
-
resolve:
|
|
258
|
+
var makeSyncDeferred = (result) => ({
|
|
259
|
+
promise: result,
|
|
260
|
+
resolve: () => {
|
|
261
|
+
},
|
|
293
262
|
reject(e) {
|
|
294
263
|
throw e;
|
|
295
264
|
}
|
|
296
265
|
});
|
|
297
266
|
var noop = () => {
|
|
298
267
|
};
|
|
299
|
-
var identity = (v) => v;
|
|
300
268
|
|
|
301
269
|
// src/main/ts/index.ts
|
|
302
270
|
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
|
@@ -10,210 +10,176 @@ var LOOKUPS = {
|
|
|
10
10
|
wmic: {
|
|
11
11
|
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
12
12
|
args: [],
|
|
13
|
-
parse(stdout) {
|
|
14
|
-
return parse(removeWmicPrefix(stdout), { format: "win" });
|
|
15
|
-
}
|
|
13
|
+
parse: (stdout) => parse(removeWmicPrefix(stdout), { format: "win" })
|
|
16
14
|
},
|
|
17
15
|
ps: {
|
|
18
16
|
cmd: "ps",
|
|
19
|
-
args: ["-
|
|
20
|
-
parse(stdout) {
|
|
21
|
-
return parse(stdout, { format: "unix" });
|
|
22
|
-
}
|
|
17
|
+
args: ["-eo", "pid,ppid,args"],
|
|
18
|
+
parse: (stdout) => parse(stdout, { format: "unix" })
|
|
23
19
|
},
|
|
24
20
|
pwsh: {
|
|
25
21
|
cmd: "pwsh",
|
|
26
22
|
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
27
23
|
parse(stdout) {
|
|
28
|
-
let arr = [];
|
|
29
24
|
try {
|
|
30
|
-
arr = JSON.parse(stdout);
|
|
25
|
+
const arr = JSON.parse(stdout);
|
|
26
|
+
return arr.map((p) => ({
|
|
27
|
+
ProcessId: [String(p.ProcessId)],
|
|
28
|
+
ParentProcessId: [String(p.ParentProcessId)],
|
|
29
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
30
|
+
}));
|
|
31
31
|
} catch {
|
|
32
32
|
return [];
|
|
33
33
|
}
|
|
34
|
-
return arr.map((p) => ({
|
|
35
|
-
ProcessId: [p.ProcessId + ""],
|
|
36
|
-
ParentProcessId: [p.ParentProcessId + ""],
|
|
37
|
-
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
38
|
-
}));
|
|
39
34
|
}
|
|
40
35
|
}
|
|
41
36
|
};
|
|
42
|
-
var
|
|
43
|
-
if (f === "") return false;
|
|
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
|
-
};
|
|
37
|
+
var lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
55
38
|
var lookup = (query = {}, cb = noop) => _lookup({ query, cb, sync: false });
|
|
56
39
|
var lookupSync = (query = {}, cb = noop) => _lookup({ query, cb, sync: true });
|
|
57
40
|
lookup.sync = lookupSync;
|
|
58
|
-
var _lookup = ({
|
|
59
|
-
|
|
60
|
-
cb = noop,
|
|
61
|
-
sync = false
|
|
62
|
-
}) => {
|
|
63
|
-
const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
|
|
64
|
-
const { promise, resolve, reject } = pFactory();
|
|
41
|
+
var _lookup = ({ query = {}, cb = noop, sync = false }) => {
|
|
42
|
+
const { promise, resolve, reject } = sync ? makeSyncDeferred([]) : makeDeferred();
|
|
65
43
|
const result = [];
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
parse: parse2,
|
|
69
|
-
cmd,
|
|
70
|
-
args
|
|
71
|
-
} = LOOKUPS[lookupFlow];
|
|
44
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
|
|
45
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
72
46
|
const callback = (err, { stdout }) => {
|
|
73
47
|
if (err) {
|
|
74
48
|
reject(err);
|
|
75
49
|
cb(err);
|
|
76
50
|
return;
|
|
77
51
|
}
|
|
78
|
-
result.push(...filterProcessList(normalizeOutput(
|
|
52
|
+
result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
|
|
79
53
|
resolve(result);
|
|
80
54
|
cb(null, result);
|
|
81
55
|
};
|
|
82
|
-
exec({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
callback,
|
|
86
|
-
sync,
|
|
87
|
-
run(cb2) {
|
|
88
|
-
cb2();
|
|
89
|
-
}
|
|
90
|
-
});
|
|
56
|
+
exec({ cmd, args, callback, sync, run(cb2) {
|
|
57
|
+
cb2();
|
|
58
|
+
} });
|
|
91
59
|
return Object.assign(promise, result);
|
|
92
60
|
};
|
|
93
|
-
var
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
(p) => query.arguments ? new RegExp(query.arguments, "i").test(p.arguments.join(" ")) : true,
|
|
98
|
-
(p) => query.ppid ? query.ppid + "" === p.ppid : true
|
|
99
|
-
];
|
|
100
|
-
return processList.filter(
|
|
101
|
-
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && filters.every((f) => f(p))
|
|
102
|
-
);
|
|
103
|
-
};
|
|
104
|
-
var removeWmicPrefix = (stdout) => {
|
|
105
|
-
const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
|
|
106
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
|
|
107
|
-
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
108
|
-
};
|
|
109
|
-
var pickTree = (list, pid, recursive = false) => {
|
|
110
|
-
const children = list.filter((p) => p.ppid === pid + "");
|
|
111
|
-
return [
|
|
112
|
-
...children,
|
|
113
|
-
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
114
|
-
];
|
|
115
|
-
};
|
|
116
|
-
var _tree = ({
|
|
117
|
-
cb = noop,
|
|
118
|
-
opts,
|
|
119
|
-
sync = false
|
|
120
|
-
}) => {
|
|
61
|
+
var tree = async (opts, cb) => _tree({ opts, cb });
|
|
62
|
+
var treeSync = (opts, cb) => _tree({ opts, cb, sync: true });
|
|
63
|
+
tree.sync = treeSync;
|
|
64
|
+
var _tree = ({ cb = noop, opts, sync = false }) => {
|
|
121
65
|
if (typeof opts === "string" || typeof opts === "number") {
|
|
122
66
|
return _tree({ opts: { pid: opts }, cb, sync });
|
|
123
67
|
}
|
|
124
|
-
const onError = (err) => cb(err);
|
|
125
68
|
const onData = (all) => {
|
|
126
69
|
if (opts === void 0) return all;
|
|
127
|
-
const
|
|
128
|
-
const list = pickTree(all, pid, recursive);
|
|
70
|
+
const list = pickTree(all, opts.pid, opts.recursive ?? false);
|
|
129
71
|
cb(null, list);
|
|
130
72
|
return list;
|
|
131
73
|
};
|
|
74
|
+
const onError = (err) => {
|
|
75
|
+
cb(err);
|
|
76
|
+
throw err;
|
|
77
|
+
};
|
|
132
78
|
try {
|
|
133
79
|
const all = _lookup({ sync });
|
|
134
|
-
return sync ? onData(all) : all.then(onData,
|
|
135
|
-
onError(err);
|
|
136
|
-
throw err;
|
|
137
|
-
});
|
|
80
|
+
return sync ? onData(all) : all.then(onData, onError);
|
|
138
81
|
} catch (err) {
|
|
139
|
-
|
|
82
|
+
cb(err);
|
|
140
83
|
return Promise.reject(err);
|
|
141
84
|
}
|
|
142
85
|
};
|
|
143
|
-
var
|
|
144
|
-
var
|
|
145
|
-
|
|
86
|
+
var inflight = null;
|
|
87
|
+
var queued = null;
|
|
88
|
+
var sharedSnapshot = (since) => {
|
|
89
|
+
if (inflight && inflight.startedAt >= since) return inflight.promise;
|
|
90
|
+
if (queued) return queued;
|
|
91
|
+
const after = inflight?.promise.catch(noop) ?? Promise.resolve();
|
|
92
|
+
return queued = after.then(() => {
|
|
93
|
+
queued = null;
|
|
94
|
+
const startedAt = Date.now();
|
|
95
|
+
const promise = lookup().then((list) => ({ startedAt, list }));
|
|
96
|
+
inflight = { startedAt, promise };
|
|
97
|
+
return promise.finally(() => {
|
|
98
|
+
inflight = inflight?.promise === promise ? null : inflight;
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
var pickTree = (list, pid, recursive = false) => {
|
|
103
|
+
const children = list.filter((p) => p.ppid === String(pid));
|
|
104
|
+
return [
|
|
105
|
+
...children,
|
|
106
|
+
...children.flatMap((p) => recursive ? pickTree(list, p.pid, true) : [])
|
|
107
|
+
];
|
|
108
|
+
};
|
|
146
109
|
var kill = (pid, opts, next) => {
|
|
147
|
-
if (typeof opts
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
if (typeof opts == "string" || typeof opts == "number") {
|
|
151
|
-
return kill(pid, { signal: opts }, next);
|
|
152
|
-
}
|
|
110
|
+
if (typeof opts === "function") return kill(pid, void 0, opts);
|
|
111
|
+
if (typeof opts === "string" || typeof opts === "number") return kill(pid, { signal: opts }, next);
|
|
153
112
|
const { promise, resolve, reject } = makeDeferred();
|
|
154
|
-
const {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
113
|
+
const { timeout = 30, signal = "SIGTERM", interval = 200 } = opts || {};
|
|
114
|
+
const sPid = String(pid);
|
|
115
|
+
let done = false;
|
|
116
|
+
const state = {};
|
|
117
|
+
const settle = (err) => {
|
|
118
|
+
if (done) return;
|
|
119
|
+
done = true;
|
|
120
|
+
clearTimeout(state.timer);
|
|
121
|
+
if (err) reject(err);
|
|
122
|
+
else resolve(pid);
|
|
123
|
+
next?.(err ?? null, pid);
|
|
124
|
+
};
|
|
158
125
|
try {
|
|
159
126
|
process.kill(+pid, signal);
|
|
160
127
|
} catch (e) {
|
|
161
|
-
|
|
162
|
-
next?.(e);
|
|
128
|
+
settle(e);
|
|
163
129
|
return promise;
|
|
164
130
|
}
|
|
165
|
-
let
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
reject(err);
|
|
173
|
-
finishCallback?.(err, pid);
|
|
174
|
-
} else if (list.length > 0) {
|
|
175
|
-
checkConfident = checkConfident - 1 || 0;
|
|
176
|
-
checkKilled(finishCallback);
|
|
131
|
+
let since = Date.now();
|
|
132
|
+
state.timer = setTimeout(() => settle(new Error("Kill process timeout")), timeout * 1e3);
|
|
133
|
+
const poll = () => sharedSnapshot(since).then(({ startedAt, list }) => {
|
|
134
|
+
if (done) return;
|
|
135
|
+
since = startedAt + 1;
|
|
136
|
+
if (list.some((p) => p.pid === sPid)) {
|
|
137
|
+
setTimeout(poll, Math.max(0, startedAt + interval - Date.now()));
|
|
177
138
|
} else {
|
|
178
|
-
|
|
179
|
-
if (checkConfident === 5) {
|
|
180
|
-
clearTimeout(checkTimeoutTimer);
|
|
181
|
-
resolve(pid);
|
|
182
|
-
finishCallback?.(null, pid);
|
|
183
|
-
} else {
|
|
184
|
-
checkKilled(finishCallback);
|
|
185
|
-
}
|
|
139
|
+
settle();
|
|
186
140
|
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
checkKilled(next);
|
|
190
|
-
checkTimeoutTimer = setTimeout(() => {
|
|
191
|
-
checkIsTimeout = true;
|
|
192
|
-
next(new Error("Kill process timeout"));
|
|
193
|
-
}, timeout * 1e3);
|
|
194
|
-
} else {
|
|
195
|
-
resolve(pid);
|
|
196
|
-
}
|
|
141
|
+
}, settle);
|
|
142
|
+
poll();
|
|
197
143
|
return promise;
|
|
198
144
|
};
|
|
199
|
-
var normalizeOutput = (data) => data.
|
|
145
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
200
146
|
const pid = (d.PID || d.ProcessId)?.[0];
|
|
201
147
|
const ppid = (d.PPID || d.ParentProcessId)?.[0];
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
if (pid
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
148
|
+
const rawCmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
149
|
+
const parts = rawCmd.length === 1 ? rawCmd[0].split(/\s+/) : rawCmd;
|
|
150
|
+
if (!pid || parts.length === 0) return [];
|
|
151
|
+
const binIdx = parts.findIndex((_v, i) => isBin(parts.slice(0, i).join(" ")));
|
|
152
|
+
const command = (binIdx === -1 ? parts : parts.slice(0, binIdx)).join(" ");
|
|
153
|
+
const args = binIdx === -1 ? [] : parts.slice(binIdx);
|
|
154
|
+
return [{ pid, ppid, command, arguments: args }];
|
|
155
|
+
});
|
|
156
|
+
var filterProcessList = (processList, query = {}) => {
|
|
157
|
+
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map(String);
|
|
158
|
+
const commandRe = query.command ? new RegExp(query.command, "i") : null;
|
|
159
|
+
const argumentsRe = query.arguments ? new RegExp(query.arguments, "i") : null;
|
|
160
|
+
const ppid = query.ppid === void 0 ? null : String(query.ppid);
|
|
161
|
+
return processList.filter(
|
|
162
|
+
(p) => (pidList.length === 0 || pidList.includes(p.pid)) && (!commandRe || commandRe.test(p.command)) && (!argumentsRe || argumentsRe.test(p.arguments.join(" "))) && (!ppid || ppid === p.ppid)
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
var removeWmicPrefix = (stdout) => {
|
|
166
|
+
const s = stdout.indexOf(LOOKUPS.wmic.cmd + os.EOL);
|
|
167
|
+
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(os.EOL) : stdout.length;
|
|
168
|
+
return (s > 0 ? stdout.slice(s + LOOKUPS.wmic.cmd.length, e) : stdout.slice(0, e)).trimStart();
|
|
169
|
+
};
|
|
170
|
+
var isBin = (f) => {
|
|
171
|
+
if (f === "") return false;
|
|
172
|
+
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
173
|
+
if (f.length > 3 && f[0] === '"')
|
|
174
|
+
return f.at(-1) === '"' ? isBin(f.slice(1, -1)) : false;
|
|
175
|
+
try {
|
|
176
|
+
if (!fs.existsSync(f)) return false;
|
|
177
|
+
const stat = fs.lstatSync(f);
|
|
178
|
+
return stat.isFile() || stat.isSymbolicLink();
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
214
181
|
}
|
|
215
|
-
|
|
216
|
-
}, []);
|
|
182
|
+
};
|
|
217
183
|
var makeDeferred = () => {
|
|
218
184
|
let resolve;
|
|
219
185
|
let reject;
|
|
@@ -223,16 +189,16 @@ var makeDeferred = () => {
|
|
|
223
189
|
});
|
|
224
190
|
return { resolve, reject, promise };
|
|
225
191
|
};
|
|
226
|
-
var
|
|
227
|
-
promise:
|
|
228
|
-
resolve:
|
|
192
|
+
var makeSyncDeferred = (result) => ({
|
|
193
|
+
promise: result,
|
|
194
|
+
resolve: () => {
|
|
195
|
+
},
|
|
229
196
|
reject(e) {
|
|
230
197
|
throw e;
|
|
231
198
|
}
|
|
232
199
|
});
|
|
233
200
|
var noop = () => {
|
|
234
201
|
};
|
|
235
|
-
var identity = (v) => v;
|
|
236
202
|
|
|
237
203
|
// src/main/ts/index.ts
|
|
238
204
|
var index_default = { kill, lookup, lookupSync, tree, treeSync };
|