@webpod/ps 1.1.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 +17 -5
- package/target/cjs/index.cjs +119 -147
- package/target/dts/index.d.ts +3 -3
- package/target/dts/ps.d.ts +23 -36
- package/target/esm/index.mjs +115 -145
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: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
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",
|
|
@@ -45,17 +56,18 @@
|
|
|
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": "^11.7.5",
|
|
69
|
+
"oxlint": "^1.58.0",
|
|
70
|
+
"size-limit": "^12.0.1",
|
|
59
71
|
"ts-node": "^10.9.2",
|
|
60
72
|
"typedoc": "^0.28.18",
|
|
61
73
|
"typescript": "^5.9.2"
|
package/target/cjs/index.cjs
CHANGED
|
@@ -71,209 +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
78
|
args: ["-eo", "pid,ppid,args"],
|
|
81
|
-
parse(stdout) {
|
|
82
|
-
return (0, import_ingrid.parse)(stdout, { format: "unix" });
|
|
83
|
-
}
|
|
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
|
|
105
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
|
|
106
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
129
107
|
const callback = (err, { stdout }) => {
|
|
130
108
|
if (err) {
|
|
131
109
|
reject(err);
|
|
132
110
|
cb(err);
|
|
133
111
|
return;
|
|
134
112
|
}
|
|
135
|
-
result.push(...filterProcessList(normalizeOutput(
|
|
113
|
+
result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
|
|
136
114
|
resolve(result);
|
|
137
115
|
cb(null, result);
|
|
138
116
|
};
|
|
139
|
-
(0, import_spawn.exec)({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
callback,
|
|
143
|
-
sync,
|
|
144
|
-
run(cb2) {
|
|
145
|
-
cb2();
|
|
146
|
-
}
|
|
147
|
-
});
|
|
117
|
+
(0, import_spawn.exec)({ cmd, args, callback, sync, run(cb2) {
|
|
118
|
+
cb2();
|
|
119
|
+
} });
|
|
148
120
|
return Object.assign(promise, result);
|
|
149
121
|
};
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
}) => {
|
|
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 }) => {
|
|
178
128
|
if (typeof opts === "string" || typeof opts === "number") {
|
|
179
129
|
return _tree({ opts: { pid: opts }, cb, sync });
|
|
180
130
|
}
|
|
181
|
-
const onError = (err) => cb(err);
|
|
182
131
|
const onData = (all) => {
|
|
132
|
+
var _a;
|
|
183
133
|
if (opts === void 0) return all;
|
|
184
|
-
const
|
|
185
|
-
const list = pickTree(all, pid, recursive);
|
|
134
|
+
const list = pickTree(all, opts.pid, (_a = opts.recursive) != null ? _a : false);
|
|
186
135
|
cb(null, list);
|
|
187
136
|
return list;
|
|
188
137
|
};
|
|
138
|
+
const onError = (err) => {
|
|
139
|
+
cb(err);
|
|
140
|
+
throw err;
|
|
141
|
+
};
|
|
189
142
|
try {
|
|
190
143
|
const all = _lookup({ sync });
|
|
191
|
-
return sync ? onData(all) : all.then(onData,
|
|
192
|
-
onError(err);
|
|
193
|
-
throw err;
|
|
194
|
-
});
|
|
144
|
+
return sync ? onData(all) : all.then(onData, onError);
|
|
195
145
|
} catch (err) {
|
|
196
|
-
|
|
146
|
+
cb(err);
|
|
197
147
|
return Promise.reject(err);
|
|
198
148
|
}
|
|
199
149
|
};
|
|
200
|
-
var
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
var
|
|
204
|
-
|
|
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
|
+
};
|
|
205
174
|
var kill = (pid, opts, next) => {
|
|
206
|
-
if (typeof opts
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
if (typeof opts == "string" || typeof opts == "number") {
|
|
210
|
-
return kill(pid, { signal: opts }, next);
|
|
211
|
-
}
|
|
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);
|
|
212
177
|
const { promise, resolve, reject } = makeDeferred();
|
|
213
|
-
const {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
+
};
|
|
217
190
|
try {
|
|
218
191
|
import_node_process.default.kill(+pid, signal);
|
|
219
192
|
} catch (e) {
|
|
220
|
-
|
|
221
|
-
next == null ? void 0 : next(e);
|
|
193
|
+
settle(e);
|
|
222
194
|
return promise;
|
|
223
195
|
}
|
|
224
|
-
let
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
|
|
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);
|
|
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()));
|
|
236
203
|
} else {
|
|
237
|
-
|
|
238
|
-
if (checkConfident === 5) {
|
|
239
|
-
clearTimeout(checkTimeoutTimer);
|
|
240
|
-
resolve(pid);
|
|
241
|
-
finishCallback == null ? void 0 : finishCallback(null, pid);
|
|
242
|
-
} else {
|
|
243
|
-
checkKilled(finishCallback);
|
|
244
|
-
}
|
|
204
|
+
settle();
|
|
245
205
|
}
|
|
246
|
-
});
|
|
247
|
-
|
|
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
|
-
}
|
|
206
|
+
}, settle);
|
|
207
|
+
poll();
|
|
256
208
|
return promise;
|
|
257
209
|
};
|
|
258
|
-
var normalizeOutput = (data) => data.
|
|
210
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
259
211
|
var _a, _b;
|
|
260
212
|
const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
|
|
261
213
|
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
|
-
|
|
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;
|
|
274
247
|
}
|
|
275
|
-
|
|
276
|
-
}, []);
|
|
248
|
+
};
|
|
277
249
|
var makeDeferred = () => {
|
|
278
250
|
let resolve;
|
|
279
251
|
let reject;
|
|
@@ -283,16 +255,16 @@ var makeDeferred = () => {
|
|
|
283
255
|
});
|
|
284
256
|
return { resolve, reject, promise };
|
|
285
257
|
};
|
|
286
|
-
var
|
|
287
|
-
promise:
|
|
288
|
-
resolve:
|
|
258
|
+
var makeSyncDeferred = (result) => ({
|
|
259
|
+
promise: result,
|
|
260
|
+
resolve: () => {
|
|
261
|
+
},
|
|
289
262
|
reject(e) {
|
|
290
263
|
throw e;
|
|
291
264
|
}
|
|
292
265
|
});
|
|
293
266
|
var noop = () => {
|
|
294
267
|
};
|
|
295
|
-
var identity = (v) => v;
|
|
296
268
|
|
|
297
269
|
// src/main/ts/index.ts
|
|
298
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,206 +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
17
|
args: ["-eo", "pid,ppid,args"],
|
|
20
|
-
parse(stdout) {
|
|
21
|
-
return parse(stdout, { format: "unix" });
|
|
22
|
-
}
|
|
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
|
|
44
|
+
const { parse: parseOutput, cmd, args: defaultArgs } = LOOKUPS[lookupFlow];
|
|
45
|
+
const args = !IS_WIN && query.psargs ? query.psargs.split(/\s+/) : defaultArgs;
|
|
68
46
|
const callback = (err, { stdout }) => {
|
|
69
47
|
if (err) {
|
|
70
48
|
reject(err);
|
|
71
49
|
cb(err);
|
|
72
50
|
return;
|
|
73
51
|
}
|
|
74
|
-
result.push(...filterProcessList(normalizeOutput(
|
|
52
|
+
result.push(...filterProcessList(normalizeOutput(parseOutput(stdout)), query));
|
|
75
53
|
resolve(result);
|
|
76
54
|
cb(null, result);
|
|
77
55
|
};
|
|
78
|
-
exec({
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
callback,
|
|
82
|
-
sync,
|
|
83
|
-
run(cb2) {
|
|
84
|
-
cb2();
|
|
85
|
-
}
|
|
86
|
-
});
|
|
56
|
+
exec({ cmd, args, callback, sync, run(cb2) {
|
|
57
|
+
cb2();
|
|
58
|
+
} });
|
|
87
59
|
return Object.assign(promise, result);
|
|
88
60
|
};
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
}) => {
|
|
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 }) => {
|
|
117
65
|
if (typeof opts === "string" || typeof opts === "number") {
|
|
118
66
|
return _tree({ opts: { pid: opts }, cb, sync });
|
|
119
67
|
}
|
|
120
|
-
const onError = (err) => cb(err);
|
|
121
68
|
const onData = (all) => {
|
|
122
69
|
if (opts === void 0) return all;
|
|
123
|
-
const
|
|
124
|
-
const list = pickTree(all, pid, recursive);
|
|
70
|
+
const list = pickTree(all, opts.pid, opts.recursive ?? false);
|
|
125
71
|
cb(null, list);
|
|
126
72
|
return list;
|
|
127
73
|
};
|
|
74
|
+
const onError = (err) => {
|
|
75
|
+
cb(err);
|
|
76
|
+
throw err;
|
|
77
|
+
};
|
|
128
78
|
try {
|
|
129
79
|
const all = _lookup({ sync });
|
|
130
|
-
return sync ? onData(all) : all.then(onData,
|
|
131
|
-
onError(err);
|
|
132
|
-
throw err;
|
|
133
|
-
});
|
|
80
|
+
return sync ? onData(all) : all.then(onData, onError);
|
|
134
81
|
} catch (err) {
|
|
135
|
-
|
|
82
|
+
cb(err);
|
|
136
83
|
return Promise.reject(err);
|
|
137
84
|
}
|
|
138
85
|
};
|
|
139
|
-
var
|
|
140
|
-
var
|
|
141
|
-
|
|
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
|
+
};
|
|
142
109
|
var kill = (pid, opts, next) => {
|
|
143
|
-
if (typeof opts
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
if (typeof opts == "string" || typeof opts == "number") {
|
|
147
|
-
return kill(pid, { signal: opts }, next);
|
|
148
|
-
}
|
|
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);
|
|
149
112
|
const { promise, resolve, reject } = makeDeferred();
|
|
150
|
-
const {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
+
};
|
|
154
125
|
try {
|
|
155
126
|
process.kill(+pid, signal);
|
|
156
127
|
} catch (e) {
|
|
157
|
-
|
|
158
|
-
next?.(e);
|
|
128
|
+
settle(e);
|
|
159
129
|
return promise;
|
|
160
130
|
}
|
|
161
|
-
let
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
reject(err);
|
|
169
|
-
finishCallback?.(err, pid);
|
|
170
|
-
} else if (list.length > 0) {
|
|
171
|
-
checkConfident = checkConfident - 1 || 0;
|
|
172
|
-
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()));
|
|
173
138
|
} else {
|
|
174
|
-
|
|
175
|
-
if (checkConfident === 5) {
|
|
176
|
-
clearTimeout(checkTimeoutTimer);
|
|
177
|
-
resolve(pid);
|
|
178
|
-
finishCallback?.(null, pid);
|
|
179
|
-
} else {
|
|
180
|
-
checkKilled(finishCallback);
|
|
181
|
-
}
|
|
139
|
+
settle();
|
|
182
140
|
}
|
|
183
|
-
});
|
|
184
|
-
|
|
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
|
-
}
|
|
141
|
+
}, settle);
|
|
142
|
+
poll();
|
|
193
143
|
return promise;
|
|
194
144
|
};
|
|
195
|
-
var normalizeOutput = (data) => data.
|
|
145
|
+
var normalizeOutput = (data) => data.flatMap((d) => {
|
|
196
146
|
const pid = (d.PID || d.ProcessId)?.[0];
|
|
197
147
|
const ppid = (d.PPID || d.ParentProcessId)?.[0];
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
if (pid
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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;
|
|
210
181
|
}
|
|
211
|
-
|
|
212
|
-
}, []);
|
|
182
|
+
};
|
|
213
183
|
var makeDeferred = () => {
|
|
214
184
|
let resolve;
|
|
215
185
|
let reject;
|
|
@@ -219,16 +189,16 @@ var makeDeferred = () => {
|
|
|
219
189
|
});
|
|
220
190
|
return { resolve, reject, promise };
|
|
221
191
|
};
|
|
222
|
-
var
|
|
223
|
-
promise:
|
|
224
|
-
resolve:
|
|
192
|
+
var makeSyncDeferred = (result) => ({
|
|
193
|
+
promise: result,
|
|
194
|
+
resolve: () => {
|
|
195
|
+
},
|
|
225
196
|
reject(e) {
|
|
226
197
|
throw e;
|
|
227
198
|
}
|
|
228
199
|
});
|
|
229
200
|
var noop = () => {
|
|
230
201
|
};
|
|
231
|
-
var identity = (v) => v;
|
|
232
202
|
|
|
233
203
|
// src/main/ts/index.ts
|
|
234
204
|
var index_default = { kill, lookup, lookupSync, tree, treeSync };
|