@webpod/ps 0.1.4 → 1.1.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 +7 -4
- package/package.json +13 -14
- package/target/cjs/index.cjs +50 -29
- package/target/dts/ps.d.ts +3 -5
- package/target/esm/index.mjs +49 -28
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* [x] `table-parser` replaced with `@webpod/ingrid` to handle some issues: [neekey/ps#76](https://github.com/neekey/ps/issues/76), [neekey/ps#62](https://github.com/neekey/ps/issues/62), [neekey/table-parser#11](https://github.com/neekey/table-parser/issues/11), [neekey/table-parser#18](https://github.com/neekey/table-parser/issues/18)
|
|
9
9
|
* [x] Provides promisified responses
|
|
10
10
|
* [x] Brings sync API
|
|
11
|
-
* [x] Builds a process
|
|
11
|
+
* [x] Builds a process subtree by parent
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
```bash
|
|
@@ -16,10 +16,13 @@ $ npm install @webpod/ps
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Internals
|
|
19
|
-
This module
|
|
19
|
+
This module uses different approaches for getting process list:
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
| Platform | Method |
|
|
22
|
+
|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
23
|
+
| Unix/Mac | `ps -lx` |
|
|
24
|
+
| Windows (kernel >= 26000)| `pwsh -NoProfile -Command "Get-CimInstance Win32_Process \| Select-Object ProcessId,ParentProcessId,CommandLine \| ConvertTo-Json -Compress"` |
|
|
25
|
+
| Windows (kernel < 26000) | [`wmic`](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmic) `process get ProcessId,CommandLine` |
|
|
23
26
|
|
|
24
27
|
## Usage
|
|
25
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpod/ps",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A process lookup utility",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,8 +23,8 @@
|
|
|
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 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
30
|
"publish:draft": "npm run build && npm publish --no-git-tag-version"
|
|
@@ -42,24 +42,23 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@webpod/ingrid": "^1.1.1",
|
|
45
|
-
"zurk": "^0.11.
|
|
45
|
+
"zurk": "^0.11.10"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^
|
|
49
|
-
"c8": "^
|
|
50
|
-
"concurrently": "^9.2.
|
|
51
|
-
"esbuild": "^0.
|
|
52
|
-
"esbuild-node-externals": "^1.
|
|
53
|
-
"esbuild-plugin-entry-chunks": "^0.1.
|
|
54
|
-
"
|
|
55
|
-
"eslint-config-qiwi": "^2.1.3",
|
|
48
|
+
"@types/node": "^25.5.2",
|
|
49
|
+
"c8": "^11.0.0",
|
|
50
|
+
"concurrently": "^9.2.1",
|
|
51
|
+
"esbuild": "^0.28.0",
|
|
52
|
+
"esbuild-node-externals": "^1.21.0",
|
|
53
|
+
"esbuild-plugin-entry-chunks": "^0.1.18",
|
|
54
|
+
"oxlint": "^1.58.0",
|
|
56
55
|
"fast-glob": "^3.3.3",
|
|
57
56
|
"minimist": "^1.2.8",
|
|
58
57
|
"mocha": "^10.8.2",
|
|
59
58
|
"sinon": "^18.0.1",
|
|
60
59
|
"ts-node": "^10.9.2",
|
|
61
|
-
"typedoc": "^0.28.
|
|
62
|
-
"typescript": "^5.
|
|
60
|
+
"typedoc": "^0.28.18",
|
|
61
|
+
"typescript": "^5.9.2"
|
|
63
62
|
},
|
|
64
63
|
"repository": {
|
|
65
64
|
"type": "git",
|
package/target/cjs/index.cjs
CHANGED
|
@@ -62,11 +62,44 @@ module.exports = __toCommonJS(index_exports);
|
|
|
62
62
|
// src/main/ts/ps.ts
|
|
63
63
|
var import_node_process = __toESM(require("node:process"), 1);
|
|
64
64
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
65
|
-
var import_node_os = require("node:os");
|
|
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
68
|
var IS_WIN = import_node_process.default.platform === "win32";
|
|
69
|
-
var
|
|
69
|
+
var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(import_node_os.default.release().split(".")[2], 10) >= 26e3;
|
|
70
|
+
var LOOKUPS = {
|
|
71
|
+
wmic: {
|
|
72
|
+
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
73
|
+
args: [],
|
|
74
|
+
parse(stdout) {
|
|
75
|
+
return (0, import_ingrid.parse)(removeWmicPrefix(stdout), { format: "win" });
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
ps: {
|
|
79
|
+
cmd: "ps",
|
|
80
|
+
args: ["-eo", "pid,ppid,args"],
|
|
81
|
+
parse(stdout) {
|
|
82
|
+
return (0, import_ingrid.parse)(stdout, { format: "unix" });
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
pwsh: {
|
|
86
|
+
cmd: "pwsh",
|
|
87
|
+
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
88
|
+
parse(stdout) {
|
|
89
|
+
let arr = [];
|
|
90
|
+
try {
|
|
91
|
+
arr = JSON.parse(stdout);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return arr.map((p) => ({
|
|
96
|
+
ProcessId: [p.ProcessId + ""],
|
|
97
|
+
ParentProcessId: [p.ParentProcessId + ""],
|
|
98
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
70
103
|
var isBin = (f) => {
|
|
71
104
|
if (f === "") return false;
|
|
72
105
|
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
@@ -90,42 +123,31 @@ var _lookup = ({
|
|
|
90
123
|
}) => {
|
|
91
124
|
const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
|
|
92
125
|
const { promise, resolve, reject } = pFactory();
|
|
93
|
-
const { psargs = ["-lx"] } = query;
|
|
94
|
-
const args = Array.isArray(psargs) ? psargs : psargs.split(/\s+/);
|
|
95
126
|
const result = [];
|
|
96
|
-
const
|
|
127
|
+
const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
128
|
+
const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
|
|
97
129
|
const callback = (err, { stdout }) => {
|
|
98
130
|
if (err) {
|
|
99
131
|
reject(err);
|
|
100
132
|
cb(err);
|
|
101
133
|
return;
|
|
102
134
|
}
|
|
103
|
-
result.push(...
|
|
135
|
+
result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
|
|
104
136
|
resolve(result);
|
|
105
137
|
cb(null, result);
|
|
106
138
|
};
|
|
107
|
-
|
|
108
|
-
cmd
|
|
109
|
-
args: [],
|
|
110
|
-
callback,
|
|
111
|
-
sync,
|
|
112
|
-
run(cb2) {
|
|
113
|
-
cb2();
|
|
114
|
-
}
|
|
115
|
-
} : {
|
|
116
|
-
cmd: "ps",
|
|
139
|
+
(0, import_spawn.exec)({
|
|
140
|
+
cmd,
|
|
117
141
|
args,
|
|
118
142
|
callback,
|
|
119
143
|
sync,
|
|
120
144
|
run(cb2) {
|
|
121
145
|
cb2();
|
|
122
146
|
}
|
|
123
|
-
};
|
|
124
|
-
(0, import_spawn.exec)(ctx);
|
|
147
|
+
});
|
|
125
148
|
return Object.assign(promise, result);
|
|
126
149
|
};
|
|
127
|
-
var
|
|
128
|
-
const processList = parseGrid(output);
|
|
150
|
+
var filterProcessList = (processList, query = {}) => {
|
|
129
151
|
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
130
152
|
const filters = [
|
|
131
153
|
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
@@ -137,9 +159,9 @@ var parseProcessList = (output, query = {}) => {
|
|
|
137
159
|
);
|
|
138
160
|
};
|
|
139
161
|
var removeWmicPrefix = (stdout) => {
|
|
140
|
-
const s = stdout.indexOf(
|
|
141
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(import_node_os.EOL) : stdout.length;
|
|
142
|
-
return (s > 0 ? stdout.slice(s +
|
|
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();
|
|
143
165
|
};
|
|
144
166
|
var pickTree = (list, pid, recursive = false) => {
|
|
145
167
|
const children = list.filter((p) => p.ppid === pid + "");
|
|
@@ -233,12 +255,11 @@ var kill = (pid, opts, next) => {
|
|
|
233
255
|
}
|
|
234
256
|
return promise;
|
|
235
257
|
};
|
|
236
|
-
var
|
|
237
|
-
var
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
const
|
|
241
|
-
const _cmd = d.CMD || d.CommandLine || d.COMMAND || [];
|
|
258
|
+
var normalizeOutput = (data) => data.reduce((m, d) => {
|
|
259
|
+
var _a, _b;
|
|
260
|
+
const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
|
|
261
|
+
const ppid = (_b = d.PPID || d.ParentProcessId) == null ? void 0 : _b[0];
|
|
262
|
+
const _cmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
242
263
|
const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
|
|
243
264
|
if (pid && cmd.length > 0) {
|
|
244
265
|
const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
|
package/target/dts/ps.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TIngridResponse } from '@webpod/ingrid';
|
|
1
|
+
import { type TIngridResponse } from '@webpod/ingrid';
|
|
2
2
|
export type TPsLookupCallback = (err: any, processList?: TPsLookupEntry[]) => void;
|
|
3
3
|
export type TPsLookupEntry = {
|
|
4
4
|
pid: string;
|
|
@@ -11,7 +11,6 @@ export type TPsLookupQuery = {
|
|
|
11
11
|
command?: string;
|
|
12
12
|
arguments?: string;
|
|
13
13
|
ppid?: number | string;
|
|
14
|
-
psargs?: string | string[];
|
|
15
14
|
};
|
|
16
15
|
export type TPsKillOptions = {
|
|
17
16
|
timeout?: number;
|
|
@@ -43,7 +42,7 @@ export declare const lookup: {
|
|
|
43
42
|
* @return {TPsLookupEntry[]}
|
|
44
43
|
*/
|
|
45
44
|
export declare const lookupSync: (query?: TPsLookupQuery, cb?: TPsLookupCallback) => TPsLookupEntry[];
|
|
46
|
-
export declare const
|
|
45
|
+
export declare const filterProcessList: (processList: TPsLookupEntry[], query?: TPsLookupQuery) => TPsLookupEntry[];
|
|
47
46
|
export declare const removeWmicPrefix: (stdout: string) => string;
|
|
48
47
|
export type TPsTreeOpts = {
|
|
49
48
|
pid: string | number;
|
|
@@ -64,6 +63,5 @@ export declare const treeSync: (opts?: string | number | TPsTreeOpts | undefined
|
|
|
64
63
|
* @param next
|
|
65
64
|
*/
|
|
66
65
|
export declare const kill: (pid: string | number, opts?: TPsNext | TPsKillOptions | TPsKillOptions["signal"], next?: TPsNext) => Promise<void>;
|
|
67
|
-
export declare const
|
|
68
|
-
export declare const formatOutput: (data: TIngridResponse) => TPsLookupEntry[];
|
|
66
|
+
export declare const normalizeOutput: (data: TIngridResponse) => TPsLookupEntry[];
|
|
69
67
|
export type PromiseResolve<T = any> = (value?: T | PromiseLike<T>) => void;
|
package/target/esm/index.mjs
CHANGED
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
// src/main/ts/ps.ts
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
-
import
|
|
4
|
+
import os from "node:os";
|
|
5
5
|
import { parse } from "@webpod/ingrid";
|
|
6
6
|
import { exec } from "zurk/spawn";
|
|
7
7
|
var IS_WIN = process.platform === "win32";
|
|
8
|
-
var
|
|
8
|
+
var IS_WIN2025_PLUS = IS_WIN && Number.parseInt(os.release().split(".")[2], 10) >= 26e3;
|
|
9
|
+
var LOOKUPS = {
|
|
10
|
+
wmic: {
|
|
11
|
+
cmd: "wmic process get ProcessId,ParentProcessId,CommandLine",
|
|
12
|
+
args: [],
|
|
13
|
+
parse(stdout) {
|
|
14
|
+
return parse(removeWmicPrefix(stdout), { format: "win" });
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
ps: {
|
|
18
|
+
cmd: "ps",
|
|
19
|
+
args: ["-eo", "pid,ppid,args"],
|
|
20
|
+
parse(stdout) {
|
|
21
|
+
return parse(stdout, { format: "unix" });
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
pwsh: {
|
|
25
|
+
cmd: "pwsh",
|
|
26
|
+
args: ["-NoProfile", "-Command", '"Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine | ConvertTo-Json -Compress"'],
|
|
27
|
+
parse(stdout) {
|
|
28
|
+
let arr = [];
|
|
29
|
+
try {
|
|
30
|
+
arr = JSON.parse(stdout);
|
|
31
|
+
} catch {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return arr.map((p) => ({
|
|
35
|
+
ProcessId: [p.ProcessId + ""],
|
|
36
|
+
ParentProcessId: [p.ParentProcessId + ""],
|
|
37
|
+
CommandLine: p.CommandLine ? [p.CommandLine] : []
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
9
42
|
var isBin = (f) => {
|
|
10
43
|
if (f === "") return false;
|
|
11
44
|
if (!f.includes("/") && !f.includes("\\")) return true;
|
|
@@ -29,42 +62,31 @@ var _lookup = ({
|
|
|
29
62
|
}) => {
|
|
30
63
|
const pFactory = sync ? makePseudoDeferred.bind(null, []) : makeDeferred;
|
|
31
64
|
const { promise, resolve, reject } = pFactory();
|
|
32
|
-
const { psargs = ["-lx"] } = query;
|
|
33
|
-
const args = Array.isArray(psargs) ? psargs : psargs.split(/\s+/);
|
|
34
65
|
const result = [];
|
|
35
|
-
const
|
|
66
|
+
const lookupFlow = IS_WIN ? IS_WIN2025_PLUS ? "pwsh" : "wmic" : "ps";
|
|
67
|
+
const { parse: parse2, cmd, args } = LOOKUPS[lookupFlow];
|
|
36
68
|
const callback = (err, { stdout }) => {
|
|
37
69
|
if (err) {
|
|
38
70
|
reject(err);
|
|
39
71
|
cb(err);
|
|
40
72
|
return;
|
|
41
73
|
}
|
|
42
|
-
result.push(...
|
|
74
|
+
result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
|
|
43
75
|
resolve(result);
|
|
44
76
|
cb(null, result);
|
|
45
77
|
};
|
|
46
|
-
|
|
47
|
-
cmd
|
|
48
|
-
args: [],
|
|
49
|
-
callback,
|
|
50
|
-
sync,
|
|
51
|
-
run(cb2) {
|
|
52
|
-
cb2();
|
|
53
|
-
}
|
|
54
|
-
} : {
|
|
55
|
-
cmd: "ps",
|
|
78
|
+
exec({
|
|
79
|
+
cmd,
|
|
56
80
|
args,
|
|
57
81
|
callback,
|
|
58
82
|
sync,
|
|
59
83
|
run(cb2) {
|
|
60
84
|
cb2();
|
|
61
85
|
}
|
|
62
|
-
};
|
|
63
|
-
exec(ctx);
|
|
86
|
+
});
|
|
64
87
|
return Object.assign(promise, result);
|
|
65
88
|
};
|
|
66
|
-
var
|
|
67
|
-
const processList = parseGrid(output);
|
|
89
|
+
var filterProcessList = (processList, query = {}) => {
|
|
68
90
|
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
69
91
|
const filters = [
|
|
70
92
|
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
@@ -76,9 +98,9 @@ var parseProcessList = (output, query = {}) => {
|
|
|
76
98
|
);
|
|
77
99
|
};
|
|
78
100
|
var removeWmicPrefix = (stdout) => {
|
|
79
|
-
const s = stdout.indexOf(
|
|
80
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(
|
|
81
|
-
return (s > 0 ? stdout.slice(s +
|
|
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();
|
|
82
104
|
};
|
|
83
105
|
var pickTree = (list, pid, recursive = false) => {
|
|
84
106
|
const children = list.filter((p) => p.ppid === pid + "");
|
|
@@ -170,11 +192,10 @@ var kill = (pid, opts, next) => {
|
|
|
170
192
|
}
|
|
171
193
|
return promise;
|
|
172
194
|
};
|
|
173
|
-
var
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
const _cmd = d.CMD || d.CommandLine || d.COMMAND || [];
|
|
195
|
+
var normalizeOutput = (data) => data.reduce((m, d) => {
|
|
196
|
+
const pid = (d.PID || d.ProcessId)?.[0];
|
|
197
|
+
const ppid = (d.PPID || d.ParentProcessId)?.[0];
|
|
198
|
+
const _cmd = d.CMD || d.CommandLine || d.COMMAND || d.ARGS || [];
|
|
178
199
|
const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
|
|
179
200
|
if (pid && cmd.length > 0) {
|
|
180
201
|
const c = cmd.findIndex((_v, i) => isBin(cmd.slice(0, i).join(" ")));
|