@webpod/ps 0.1.4 → 1.0.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 +10 -10
- package/target/cjs/index.cjs +53 -28
- package/target/dts/ps.d.ts +3 -5
- package/target/esm/index.mjs +52 -27
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": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A process lookup utility",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,10 +23,10 @@
|
|
|
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:*'",
|
|
26
|
+
"test": "concurrently 'npm:test:*' && npm run x:test:legacy",
|
|
27
27
|
"test:lint": "eslint -c src/test/lint/.eslintrc.json src",
|
|
28
28
|
"test:unit": "c8 -r lcov -r text -o target/coverage -x src/scripts -x src/test -x target node --loader ts-node/esm --experimental-specifier-resolution=node src/scripts/test.mjs",
|
|
29
|
-
"test:legacy": "node ./node_modules/mocha/bin/mocha -t 0 -R spec src/test/legacy/test.cjs",
|
|
29
|
+
"x: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"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@webpod/ingrid": "^1.1.1",
|
|
45
|
-
"zurk": "^0.11.
|
|
45
|
+
"zurk": "^0.11.5"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^24.
|
|
48
|
+
"@types/node": "^24.5.2",
|
|
49
49
|
"c8": "^10.1.3",
|
|
50
|
-
"concurrently": "^9.2.
|
|
51
|
-
"esbuild": "^0.25.
|
|
50
|
+
"concurrently": "^9.2.1",
|
|
51
|
+
"esbuild": "^0.25.10",
|
|
52
52
|
"esbuild-node-externals": "^1.18.0",
|
|
53
|
-
"esbuild-plugin-entry-chunks": "^0.1.
|
|
53
|
+
"esbuild-plugin-entry-chunks": "^0.1.17",
|
|
54
54
|
"eslint": "^8.57.0",
|
|
55
55
|
"eslint-config-qiwi": "^2.1.3",
|
|
56
56
|
"fast-glob": "^3.3.3",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"mocha": "^10.8.2",
|
|
59
59
|
"sinon": "^18.0.1",
|
|
60
60
|
"ts-node": "^10.9.2",
|
|
61
|
-
"typedoc": "^0.28.
|
|
62
|
-
"typescript": "^5.
|
|
61
|
+
"typedoc": "^0.28.13",
|
|
62
|
+
"typescript": "^5.9.2"
|
|
63
63
|
},
|
|
64
64
|
"repository": {
|
|
65
65
|
"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: ["-lx"],
|
|
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,35 @@ 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 {
|
|
129
|
+
parse: parse2,
|
|
130
|
+
cmd,
|
|
131
|
+
args
|
|
132
|
+
} = LOOKUPS[lookupFlow];
|
|
97
133
|
const callback = (err, { stdout }) => {
|
|
98
134
|
if (err) {
|
|
99
135
|
reject(err);
|
|
100
136
|
cb(err);
|
|
101
137
|
return;
|
|
102
138
|
}
|
|
103
|
-
result.push(...
|
|
139
|
+
result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
|
|
104
140
|
resolve(result);
|
|
105
141
|
cb(null, result);
|
|
106
142
|
};
|
|
107
|
-
|
|
108
|
-
cmd
|
|
109
|
-
args: [],
|
|
110
|
-
callback,
|
|
111
|
-
sync,
|
|
112
|
-
run(cb2) {
|
|
113
|
-
cb2();
|
|
114
|
-
}
|
|
115
|
-
} : {
|
|
116
|
-
cmd: "ps",
|
|
143
|
+
(0, import_spawn.exec)({
|
|
144
|
+
cmd,
|
|
117
145
|
args,
|
|
118
146
|
callback,
|
|
119
147
|
sync,
|
|
120
148
|
run(cb2) {
|
|
121
149
|
cb2();
|
|
122
150
|
}
|
|
123
|
-
};
|
|
124
|
-
(0, import_spawn.exec)(ctx);
|
|
151
|
+
});
|
|
125
152
|
return Object.assign(promise, result);
|
|
126
153
|
};
|
|
127
|
-
var
|
|
128
|
-
const processList = parseGrid(output);
|
|
154
|
+
var filterProcessList = (processList, query = {}) => {
|
|
129
155
|
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
130
156
|
const filters = [
|
|
131
157
|
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
@@ -137,9 +163,9 @@ var parseProcessList = (output, query = {}) => {
|
|
|
137
163
|
);
|
|
138
164
|
};
|
|
139
165
|
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 +
|
|
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();
|
|
143
169
|
};
|
|
144
170
|
var pickTree = (list, pid, recursive = false) => {
|
|
145
171
|
const children = list.filter((p) => p.ppid === pid + "");
|
|
@@ -233,11 +259,10 @@ var kill = (pid, opts, next) => {
|
|
|
233
259
|
}
|
|
234
260
|
return promise;
|
|
235
261
|
};
|
|
236
|
-
var
|
|
237
|
-
var
|
|
238
|
-
|
|
239
|
-
const
|
|
240
|
-
const ppid = ((_c = d.PPID) == null ? void 0 : _c[0]) || ((_d = d.ParentProcessId) == null ? void 0 : _d[0]);
|
|
262
|
+
var normalizeOutput = (data) => data.reduce((m, d) => {
|
|
263
|
+
var _a, _b;
|
|
264
|
+
const pid = (_a = d.PID || d.ProcessId) == null ? void 0 : _a[0];
|
|
265
|
+
const ppid = (_b = d.PPID || d.ParentProcessId) == null ? void 0 : _b[0];
|
|
241
266
|
const _cmd = d.CMD || d.CommandLine || d.COMMAND || [];
|
|
242
267
|
const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
|
|
243
268
|
if (pid && cmd.length > 0) {
|
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: ["-lx"],
|
|
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,35 @@ 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 {
|
|
68
|
+
parse: parse2,
|
|
69
|
+
cmd,
|
|
70
|
+
args
|
|
71
|
+
} = LOOKUPS[lookupFlow];
|
|
36
72
|
const callback = (err, { stdout }) => {
|
|
37
73
|
if (err) {
|
|
38
74
|
reject(err);
|
|
39
75
|
cb(err);
|
|
40
76
|
return;
|
|
41
77
|
}
|
|
42
|
-
result.push(...
|
|
78
|
+
result.push(...filterProcessList(normalizeOutput(parse2(stdout)), query));
|
|
43
79
|
resolve(result);
|
|
44
80
|
cb(null, result);
|
|
45
81
|
};
|
|
46
|
-
|
|
47
|
-
cmd
|
|
48
|
-
args: [],
|
|
49
|
-
callback,
|
|
50
|
-
sync,
|
|
51
|
-
run(cb2) {
|
|
52
|
-
cb2();
|
|
53
|
-
}
|
|
54
|
-
} : {
|
|
55
|
-
cmd: "ps",
|
|
82
|
+
exec({
|
|
83
|
+
cmd,
|
|
56
84
|
args,
|
|
57
85
|
callback,
|
|
58
86
|
sync,
|
|
59
87
|
run(cb2) {
|
|
60
88
|
cb2();
|
|
61
89
|
}
|
|
62
|
-
};
|
|
63
|
-
exec(ctx);
|
|
90
|
+
});
|
|
64
91
|
return Object.assign(promise, result);
|
|
65
92
|
};
|
|
66
|
-
var
|
|
67
|
-
const processList = parseGrid(output);
|
|
93
|
+
var filterProcessList = (processList, query = {}) => {
|
|
68
94
|
const pidList = (query.pid === void 0 ? [] : [query.pid].flat(1)).map((v) => v + "");
|
|
69
95
|
const filters = [
|
|
70
96
|
(p) => query.command ? new RegExp(query.command, "i").test(p.command) : true,
|
|
@@ -76,9 +102,9 @@ var parseProcessList = (output, query = {}) => {
|
|
|
76
102
|
);
|
|
77
103
|
};
|
|
78
104
|
var removeWmicPrefix = (stdout) => {
|
|
79
|
-
const s = stdout.indexOf(
|
|
80
|
-
const e = stdout.includes(">") ? stdout.trimEnd().lastIndexOf(
|
|
81
|
-
return (s > 0 ? stdout.slice(s +
|
|
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();
|
|
82
108
|
};
|
|
83
109
|
var pickTree = (list, pid, recursive = false) => {
|
|
84
110
|
const children = list.filter((p) => p.ppid === pid + "");
|
|
@@ -170,10 +196,9 @@ var kill = (pid, opts, next) => {
|
|
|
170
196
|
}
|
|
171
197
|
return promise;
|
|
172
198
|
};
|
|
173
|
-
var
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
const ppid = d.PPID?.[0] || d.ParentProcessId?.[0];
|
|
199
|
+
var normalizeOutput = (data) => data.reduce((m, d) => {
|
|
200
|
+
const pid = (d.PID || d.ProcessId)?.[0];
|
|
201
|
+
const ppid = (d.PPID || d.ParentProcessId)?.[0];
|
|
177
202
|
const _cmd = d.CMD || d.CommandLine || d.COMMAND || [];
|
|
178
203
|
const cmd = _cmd.length === 1 ? _cmd[0].split(/\s+/) : _cmd;
|
|
179
204
|
if (pid && cmd.length > 0) {
|