pubo-node 1.0.198 → 1.0.202
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 +320 -0
- package/lib/child-process/index.d.ts +6 -3
- package/lib/child-process/index.js +17 -189
- package/lib/child-process/linux.d.ts +5 -0
- package/lib/child-process/linux.js +53 -0
- package/lib/child-process/p-process-type.d.ts +6 -1
- package/lib/child-process/win32.d.ts +3 -1
- package/lib/child-process/win32.js +46 -1
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# pubo-node
|
|
2
|
+
|
|
3
|
+
Node.js 工具库,提供文件存储、FTP 客户端、gRPC 客户端、进程管理、网络工具、ROS 主题管理和文件系统操作等功能。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install pubo-node
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API 文档
|
|
12
|
+
|
|
13
|
+
### JsonStorage
|
|
14
|
+
|
|
15
|
+
基于 JSON 的文件存储类,支持多进程同步。
|
|
16
|
+
|
|
17
|
+
**构造函数**
|
|
18
|
+
```typescript
|
|
19
|
+
constructor(path: string, options: JsonStorageOptions = {})
|
|
20
|
+
```
|
|
21
|
+
- `path`: 存储文件路径
|
|
22
|
+
- `options`: 可选配置
|
|
23
|
+
- `initialState`: 初始状态,程序运行时会重置为初始值
|
|
24
|
+
- `defaultState`: 默认状态
|
|
25
|
+
|
|
26
|
+
**方法**
|
|
27
|
+
- `async get(key?: string): Promise<any>`
|
|
28
|
+
- 获取指定键的值,不传键则返回整个状态
|
|
29
|
+
- `async set(key: string, values: any): Promise<void>`
|
|
30
|
+
- 设置指定键的值
|
|
31
|
+
- `async merge(values: any): Promise<void>`
|
|
32
|
+
- 合并多个键值到状态
|
|
33
|
+
- `async remove(key: string): Promise<void>`
|
|
34
|
+
- 删除指定键
|
|
35
|
+
|
|
36
|
+
### FtpClient
|
|
37
|
+
|
|
38
|
+
FTP 客户端类,支持连接池和异步操作。
|
|
39
|
+
|
|
40
|
+
**构造函数**
|
|
41
|
+
```typescript
|
|
42
|
+
constructor(options: FtpConnectOptions)
|
|
43
|
+
```
|
|
44
|
+
- `options`: 连接选项
|
|
45
|
+
- `user`: 用户名
|
|
46
|
+
- `host`: 主机地址
|
|
47
|
+
- `password`: 密码
|
|
48
|
+
- `driver`: FTP 驱动
|
|
49
|
+
|
|
50
|
+
**方法**
|
|
51
|
+
- `async get(path: string): Promise<Buffer>`
|
|
52
|
+
- 下载文件
|
|
53
|
+
- `async put(input: string | Buffer | Stream, path: string): Promise<string>`
|
|
54
|
+
- 上传文件
|
|
55
|
+
- `async delete(path: string): Promise<string>`
|
|
56
|
+
- 删除文件
|
|
57
|
+
- `async list(path: string): Promise<FtpFile[]>`
|
|
58
|
+
- 列出目录文件
|
|
59
|
+
- `async rename(path: string, old: string): Promise<string>`
|
|
60
|
+
- 重命名文件
|
|
61
|
+
|
|
62
|
+
### FtpClientPool
|
|
63
|
+
|
|
64
|
+
FTP 客户端连接池,管理多个 FTP 连接。
|
|
65
|
+
|
|
66
|
+
**构造函数**
|
|
67
|
+
```typescript
|
|
68
|
+
constructor({ maxConnection = 5, ...options }: { maxConnection?: number } & FtpConnectOptions)
|
|
69
|
+
```
|
|
70
|
+
- `maxConnection`: 最大连接数,默认 5
|
|
71
|
+
- `options`: 同 FtpClient 连接选项
|
|
72
|
+
|
|
73
|
+
**方法**
|
|
74
|
+
同 FtpClient 接口。
|
|
75
|
+
|
|
76
|
+
### createRpcClient
|
|
77
|
+
|
|
78
|
+
创建 gRPC 客户端。
|
|
79
|
+
|
|
80
|
+
**函数签名**
|
|
81
|
+
```typescript
|
|
82
|
+
function createRpcClient<T>({ url, options = {}, ServiceImp, Grpc, cert }: CreateClientProps): T
|
|
83
|
+
```
|
|
84
|
+
- `url`: gRPC 服务地址
|
|
85
|
+
- `options`: 连接选项
|
|
86
|
+
- `ServiceImp`: 服务实现类
|
|
87
|
+
- `Grpc`: gRPC 模块
|
|
88
|
+
- `cert`: TLS 证书缓冲区
|
|
89
|
+
|
|
90
|
+
### GrpcList
|
|
91
|
+
|
|
92
|
+
全局 gRPC 客户端列表,用于管理所有创建的 gRPC 客户端。
|
|
93
|
+
|
|
94
|
+
### isPortAvailable
|
|
95
|
+
|
|
96
|
+
检查端口是否可用。
|
|
97
|
+
|
|
98
|
+
**函数签名**
|
|
99
|
+
```typescript
|
|
100
|
+
function isPortAvailable(port: number): Promise<boolean>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 进程管理函数
|
|
104
|
+
|
|
105
|
+
以下函数由 `PProcess` 接口提供,具体实现根据操作系统(Linux/Windows)不同。
|
|
106
|
+
|
|
107
|
+
#### getProcessName
|
|
108
|
+
```typescript
|
|
109
|
+
function getProcessName(pid: number): Promise<string>
|
|
110
|
+
```
|
|
111
|
+
获取进程名称。
|
|
112
|
+
|
|
113
|
+
#### getPidByPort
|
|
114
|
+
```typescript
|
|
115
|
+
function getPidByPort(port: number): Promise<number>
|
|
116
|
+
```
|
|
117
|
+
根据端口号获取进程 PID。
|
|
118
|
+
|
|
119
|
+
#### getProcessCpuUseByPid
|
|
120
|
+
```typescript
|
|
121
|
+
function getProcessCpuUseByPid(pid: number): Promise<number>
|
|
122
|
+
```
|
|
123
|
+
获取进程 CPU 使用率(百分比)。
|
|
124
|
+
|
|
125
|
+
#### getProcessCommandByPid
|
|
126
|
+
```typescript
|
|
127
|
+
function getProcessCommandByPid(pid: number): Promise<string>
|
|
128
|
+
```
|
|
129
|
+
获取进程命令行。
|
|
130
|
+
|
|
131
|
+
#### isProcessDied
|
|
132
|
+
```typescript
|
|
133
|
+
function isProcessDied(pid: number): Promise<boolean>
|
|
134
|
+
```
|
|
135
|
+
判断进程是否已死亡。
|
|
136
|
+
|
|
137
|
+
#### getProcessByPpid
|
|
138
|
+
```typescript
|
|
139
|
+
function getProcessByPpid(ppid: number): Promise<number[]>
|
|
140
|
+
```
|
|
141
|
+
获取指定父进程的所有子进程 PID 列表。
|
|
142
|
+
|
|
143
|
+
#### getProcessTree
|
|
144
|
+
```typescript
|
|
145
|
+
function getProcessTree(pid: number, tree?: any): Promise<any>
|
|
146
|
+
```
|
|
147
|
+
获取进程树结构。
|
|
148
|
+
|
|
149
|
+
#### getProcessList
|
|
150
|
+
```typescript
|
|
151
|
+
function getProcessList(pid: number): Promise<number[]>
|
|
152
|
+
```
|
|
153
|
+
获取从叶子到根的所有进程 PID 列表。
|
|
154
|
+
|
|
155
|
+
#### SIGKILL
|
|
156
|
+
```typescript
|
|
157
|
+
function SIGKILL(pid: number, signal = 2, times = 1): Promise<{ success: boolean; error: string }>
|
|
158
|
+
```
|
|
159
|
+
杀死进程及其子进程。
|
|
160
|
+
|
|
161
|
+
#### heartbeat
|
|
162
|
+
```typescript
|
|
163
|
+
function heartbeat(): void
|
|
164
|
+
```
|
|
165
|
+
子进程心跳包,每6秒发送一次。
|
|
166
|
+
|
|
167
|
+
#### getAudioCards
|
|
168
|
+
```typescript
|
|
169
|
+
function getAudioCards(filter?: string): Promise<{ text: string; index: string }[]>
|
|
170
|
+
```
|
|
171
|
+
获取音频设备列表(仅 Linux)。
|
|
172
|
+
|
|
173
|
+
#### getDiskUsage
|
|
174
|
+
```typescript
|
|
175
|
+
function getDiskUsage(): Promise<DiskInfo[]>
|
|
176
|
+
```
|
|
177
|
+
获取磁盘使用情况。
|
|
178
|
+
|
|
179
|
+
### 网络工具
|
|
180
|
+
|
|
181
|
+
#### getNetworks
|
|
182
|
+
```typescript
|
|
183
|
+
function getNetworks(): Promise<any[]>
|
|
184
|
+
```
|
|
185
|
+
获取网络接口信息(通过 lshw 命令)。
|
|
186
|
+
|
|
187
|
+
#### getWifiName
|
|
188
|
+
```typescript
|
|
189
|
+
function getWifiName(): Promise<string>
|
|
190
|
+
```
|
|
191
|
+
获取无线网络接口名称。
|
|
192
|
+
|
|
193
|
+
### RosTopicManager
|
|
194
|
+
|
|
195
|
+
ROS 主题管理器,用于创建和管理 ROS 主题。
|
|
196
|
+
|
|
197
|
+
**方法**
|
|
198
|
+
- `getTopic(topic: string, messageType: string): RosTopic`
|
|
199
|
+
- 获取或创建 ROS 主题实例
|
|
200
|
+
|
|
201
|
+
### RosTopic
|
|
202
|
+
|
|
203
|
+
ROS 主题类,支持订阅、取消订阅和发布消息。
|
|
204
|
+
|
|
205
|
+
**构造函数**
|
|
206
|
+
```typescript
|
|
207
|
+
constructor(topic: string, messageType: string)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**方法**
|
|
211
|
+
- `async subscribe(): Promise<void>`
|
|
212
|
+
- 订阅主题
|
|
213
|
+
- `async unsubscribe(): Promise<void>`
|
|
214
|
+
- 取消订阅
|
|
215
|
+
- `async publish(payload: any): Promise<void>`
|
|
216
|
+
- 发布消息到主题
|
|
217
|
+
|
|
218
|
+
### PuboFileSystem
|
|
219
|
+
|
|
220
|
+
文件系统操作接口,提供 Promise 化的 fs 方法。
|
|
221
|
+
|
|
222
|
+
**方法**
|
|
223
|
+
- `read<TBuffer extends NodeJS.ArrayBufferView>(fd: number, buffer?: TBuffer, offset?: number, length?: number, position?: fs.ReadPosition | null): Promise<[number, TBuffer]>`
|
|
224
|
+
- 从文件描述符读取数据
|
|
225
|
+
- `stat(path: fs.PathLike): Promise<fs.Stats>`
|
|
226
|
+
- 获取文件状态
|
|
227
|
+
- `readFile(path: fs.PathOrFileDescriptor, options?: { encoding?: null; flag?: string } & EventEmitter.Abortable | null): Promise<Buffer>`
|
|
228
|
+
- 读取文件内容
|
|
229
|
+
- `writeFile(file: fs.PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, options?: fs.WriteFileOptions): Promise<void>`
|
|
230
|
+
- 写入文件
|
|
231
|
+
- `readdir(path: fs.PathLike, options?: BufferEncoding | { encoding: BufferEncoding | null; withFileTypes: false } | null): Promise<string[]>`
|
|
232
|
+
- 读取目录内容
|
|
233
|
+
- `open(path: fs.PathLike, flags?: fs.OpenMode, mode?: fs.Mode | null): Promise<number>`
|
|
234
|
+
- 打开文件
|
|
235
|
+
- `close(fd: number): Promise<void>`
|
|
236
|
+
- 关闭文件描述符
|
|
237
|
+
- `mkdir(path: fs.PathLike, options?: fs.MakeDirectoryOptions): Promise<void>`
|
|
238
|
+
- 创建目录
|
|
239
|
+
- `rm(path: fs.PathLike): Promise<void>`
|
|
240
|
+
- 删除文件或目录
|
|
241
|
+
- `write<TBuffer extends NodeJS.ArrayBufferView>(fd: number, buffer: TBuffer, offset?: number | null, length?: number | null, position?: number | null): Promise<void>`
|
|
242
|
+
- 写入数据到文件描述符
|
|
243
|
+
|
|
244
|
+
### PProcess
|
|
245
|
+
|
|
246
|
+
进程管理接口,定义了跨平台的进程操作方法。由 `PProcessLinux` 和 `PProcessWin32` 实现。
|
|
247
|
+
|
|
248
|
+
**方法**
|
|
249
|
+
参见上述进程管理函数。
|
|
250
|
+
|
|
251
|
+
## 类型定义
|
|
252
|
+
|
|
253
|
+
### FtpConnectOptions
|
|
254
|
+
```typescript
|
|
255
|
+
interface FtpConnectOptions {
|
|
256
|
+
user: string;
|
|
257
|
+
host: string;
|
|
258
|
+
password: string;
|
|
259
|
+
driver: any;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### FtpFile
|
|
264
|
+
```typescript
|
|
265
|
+
interface FtpFile {
|
|
266
|
+
name: string;
|
|
267
|
+
owner: string;
|
|
268
|
+
group: string;
|
|
269
|
+
size: number;
|
|
270
|
+
date: Date;
|
|
271
|
+
type: string;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### DiskInfo
|
|
276
|
+
```typescript
|
|
277
|
+
interface DiskInfo {
|
|
278
|
+
fileSystem: string;
|
|
279
|
+
size: number;
|
|
280
|
+
used: number;
|
|
281
|
+
avail: number;
|
|
282
|
+
usedPercent: number;
|
|
283
|
+
mounted: number;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### JsonStorageOptions
|
|
288
|
+
```typescript
|
|
289
|
+
interface JsonStorageOptions {
|
|
290
|
+
initialState?: any;
|
|
291
|
+
defaultState?: any;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## 示例
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
const { JsonStorage, FtpClient, isPortAvailable } = require('pubo-node');
|
|
299
|
+
|
|
300
|
+
// 使用 JsonStorage
|
|
301
|
+
const storage = new JsonStorage('./data.json', { initialState: { count: 0 } });
|
|
302
|
+
await storage.set('count', 1);
|
|
303
|
+
const value = await storage.get('count');
|
|
304
|
+
|
|
305
|
+
// 使用 FTP 客户端
|
|
306
|
+
const ftp = new FtpClient({
|
|
307
|
+
user: 'user',
|
|
308
|
+
host: 'localhost',
|
|
309
|
+
password: 'pass',
|
|
310
|
+
driver: require('ftp')
|
|
311
|
+
});
|
|
312
|
+
const files = await ftp.list('/');
|
|
313
|
+
|
|
314
|
+
// 检查端口
|
|
315
|
+
const available = await isPortAvailable(8080);
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## 许可证
|
|
319
|
+
|
|
320
|
+
MIT
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
export declare function getProcessName(pid: any): Promise<string>;
|
|
2
|
-
export declare function getPidByPort(port: any): Promise<
|
|
2
|
+
export declare function getPidByPort(port: any): Promise<number>;
|
|
3
3
|
export declare function getProcessCpuUseByPid(pid: number): Promise<number>;
|
|
4
4
|
export declare function getProcessCommandByPid(pid: number): Promise<string>;
|
|
5
5
|
export declare function isProcessDied(pid: number): Promise<boolean>;
|
|
6
6
|
export declare function getProcessByPpid(pid: number): Promise<number[]>;
|
|
7
7
|
export declare const getProcessTree: (pid: number, tree?: any) => Promise<any>;
|
|
8
8
|
export declare const getProcessList: (pid: any) => Promise<number[]>;
|
|
9
|
-
export declare function SIGKILL(pid: number, signal?: number, times?: number): Promise<
|
|
9
|
+
export declare function SIGKILL(pid: number, signal?: number, times?: number): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
error: string;
|
|
12
|
+
}>;
|
|
10
13
|
export declare const heartbeat: () => void;
|
|
11
14
|
export declare const getAudioCards: (filter?: string) => Promise<{
|
|
12
15
|
text: string;
|
|
13
16
|
index: string;
|
|
14
17
|
}[]>;
|
|
15
|
-
export declare const getDiskUsage: () => Promise<
|
|
18
|
+
export declare const getDiskUsage: () => Promise<import("./p-process-type").DiskInfo[]>;
|
|
@@ -8,129 +8,33 @@ exports.getProcessCommandByPid = getProcessCommandByPid;
|
|
|
8
8
|
exports.isProcessDied = isProcessDied;
|
|
9
9
|
exports.getProcessByPpid = getProcessByPpid;
|
|
10
10
|
exports.SIGKILL = SIGKILL;
|
|
11
|
-
const child_process_1 = require("child_process");
|
|
12
11
|
const pubo_utils_1 = require("pubo-utils");
|
|
12
|
+
const linux_1 = require("./linux");
|
|
13
|
+
const win32_1 = require("./win32");
|
|
14
|
+
const processManager = process.platform === 'win32' ? new win32_1.PProcessWin32() : new linux_1.PProcessLinux();
|
|
13
15
|
// 获取进程名称
|
|
14
16
|
function getProcessName(pid) {
|
|
15
|
-
|
|
16
|
-
// 使用tasklist命令获取进程信息
|
|
17
|
-
return new Promise((resolve, reject) => {
|
|
18
|
-
(0, child_process_1.exec)(`tasklist /fi "PID eq ${pid}" /fo csv /nh`, (err, stdout) => {
|
|
19
|
-
if (err) {
|
|
20
|
-
reject(err);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
// 解析CSV格式的输出
|
|
24
|
-
const match = stdout.match(/^"(.+?)"/);
|
|
25
|
-
if (match && match[1]) {
|
|
26
|
-
resolve(match[1]);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
reject('process not found');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
(0, child_process_1.exec)(`grep "Name:" /proc/${pid}/status`, (err, data) => {
|
|
37
|
-
if (err) {
|
|
38
|
-
reject(err);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
resolve(data.toString().split(':')[1]?.trim());
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
});
|
|
17
|
+
return processManager.getProcessName(pid);
|
|
45
18
|
}
|
|
46
19
|
// 根据端口号获取进程PID
|
|
47
20
|
async function getPidByPort(port) {
|
|
48
|
-
|
|
49
|
-
return '';
|
|
50
|
-
}
|
|
51
|
-
if (process.platform === 'win32') {
|
|
52
|
-
return new Promise((resolve, reject) => {
|
|
53
|
-
(0, child_process_1.exec)(`netstat -ano | findstr "${port}"`, (error, stdout) => {
|
|
54
|
-
if (error) {
|
|
55
|
-
reject(error);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const arr = stdout.split('\n');
|
|
59
|
-
if (!arr[0]) {
|
|
60
|
-
resolve('');
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const tmp = arr[0].split(' ');
|
|
64
|
-
const res = tmp.pop();
|
|
65
|
-
resolve(res);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
(0, child_process_1.exec)(`lsof -i:${port} | awk '{print $2}'`, (err, stdout) => {
|
|
71
|
-
if (err) {
|
|
72
|
-
reject(err);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
let res = stdout.split('\n')[1];
|
|
76
|
-
if (res) {
|
|
77
|
-
res = res.trim();
|
|
78
|
-
}
|
|
79
|
-
resolve(res);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
21
|
+
return processManager.getPidByPort(port);
|
|
83
22
|
}
|
|
84
23
|
// 获取进程 cpu 使用率
|
|
85
24
|
function getProcessCpuUseByPid(pid) {
|
|
86
|
-
return
|
|
87
|
-
(0, child_process_1.exec)(`ps -p ${pid} -o %cpu=`, (err, stdout) => {
|
|
88
|
-
if (err) {
|
|
89
|
-
resolve(-1);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
resolve(parseFloat(stdout.toString()));
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
});
|
|
25
|
+
return processManager.getProcessCpuUseByPid(pid);
|
|
96
26
|
}
|
|
97
27
|
// 获取进程 command
|
|
98
28
|
function getProcessCommandByPid(pid) {
|
|
99
|
-
return
|
|
100
|
-
(0, child_process_1.exec)(`ps -p ${pid} -o command=`, (err, stdout) => {
|
|
101
|
-
if (err) {
|
|
102
|
-
resolve('');
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
resolve(stdout.toString().split('\n')[0]);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
});
|
|
29
|
+
return processManager.getProcessCommandByPid(pid);
|
|
109
30
|
}
|
|
110
31
|
// 判断进程是否死亡
|
|
111
32
|
async function isProcessDied(pid) {
|
|
112
|
-
|
|
113
|
-
// console.log(`pid: ${pid}, used: ${used}`);
|
|
114
|
-
return used < 0;
|
|
33
|
+
return processManager.isProcessDied(pid);
|
|
115
34
|
}
|
|
116
35
|
// 获取子进程
|
|
117
36
|
function getProcessByPpid(pid) {
|
|
118
|
-
return
|
|
119
|
-
let child = (0, child_process_1.exec)(`ps -o pid --no-headers --ppid ${pid}`, (err, stdout) => {
|
|
120
|
-
if (err) {
|
|
121
|
-
resolve([]);
|
|
122
|
-
child = null;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
resolve(stdout
|
|
126
|
-
.split('\n')
|
|
127
|
-
.filter((item) => !!item)
|
|
128
|
-
.map((item) => parseInt(item.trim()))
|
|
129
|
-
.filter((item) => item !== child.pid && !isNaN(item)));
|
|
130
|
-
child = null;
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
});
|
|
37
|
+
return processManager.getProcessByPpid(pid);
|
|
134
38
|
}
|
|
135
39
|
// 获取进程树
|
|
136
40
|
const getProcessTree = async (pid, tree) => {
|
|
@@ -154,22 +58,6 @@ const getProcessTree = async (pid, tree) => {
|
|
|
154
58
|
}
|
|
155
59
|
};
|
|
156
60
|
exports.getProcessTree = getProcessTree;
|
|
157
|
-
// 杀死进程
|
|
158
|
-
async function _SIGKILL(pid, signal = 2, times = 1) {
|
|
159
|
-
if (times > 5) {
|
|
160
|
-
throw new Error('SIGKILL 失败. times > 5');
|
|
161
|
-
}
|
|
162
|
-
await new Promise((resolve) => (0, child_process_1.exec)(`kill -${signal} ${pid}`, resolve));
|
|
163
|
-
try {
|
|
164
|
-
await (0, pubo_utils_1.waitFor)(async () => isProcessDied(pid), {
|
|
165
|
-
checkTime: 100,
|
|
166
|
-
timeout: 4000,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
await _SIGKILL(pid, 9, times + 1);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
61
|
// 广度优先遍历进程树,将pid放入tmp
|
|
174
62
|
const flatProcessTree = (tree, tmp) => {
|
|
175
63
|
if (tree.children) {
|
|
@@ -195,23 +83,7 @@ const getProcessList = async (pid) => {
|
|
|
195
83
|
exports.getProcessList = getProcessList;
|
|
196
84
|
// 杀死进程以及子进程
|
|
197
85
|
async function SIGKILL(pid, signal = 2, times = 1) {
|
|
198
|
-
|
|
199
|
-
return new Promise((resolve) => {
|
|
200
|
-
(0, child_process_1.exec)(`taskkill /pid ${pid} /T /F`, resolve);
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
const tmp = await (0, exports.getProcessList)(pid);
|
|
204
|
-
const res = { success: true, error: null };
|
|
205
|
-
for (const item of tmp) {
|
|
206
|
-
try {
|
|
207
|
-
await _SIGKILL(item, signal, times);
|
|
208
|
-
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
res.error = err;
|
|
211
|
-
res.success = false;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return res;
|
|
86
|
+
return processManager.SIGKILL(pid, signal, times);
|
|
215
87
|
}
|
|
216
88
|
// 子进程心跳包
|
|
217
89
|
const heartbeat = () => {
|
|
@@ -225,61 +97,17 @@ const heartbeat = () => {
|
|
|
225
97
|
}, 6000);
|
|
226
98
|
};
|
|
227
99
|
exports.heartbeat = heartbeat;
|
|
228
|
-
const parseAudioCard = (v) => {
|
|
229
|
-
let card = /card \d/.exec(v) ?? ['card 1'];
|
|
230
|
-
card = parseInt(card[0].replace('card ', ''), 10);
|
|
231
|
-
let device = /device \d/.exec(v) ?? ['device 0'];
|
|
232
|
-
device = parseInt(device[0].replace('device ', ''), 10);
|
|
233
|
-
return { text: v, index: `hw:${card},${device}` };
|
|
234
|
-
};
|
|
235
100
|
const getAudioCards = (filter = '') => {
|
|
236
|
-
if (
|
|
237
|
-
return
|
|
101
|
+
if (processManager.getAudioCards) {
|
|
102
|
+
return processManager.getAudioCards(filter);
|
|
238
103
|
}
|
|
239
|
-
return
|
|
240
|
-
(0, child_process_1.exec)(`arecord -l`, (err, stdout) => {
|
|
241
|
-
if (err) {
|
|
242
|
-
reject(err);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
const arr = stdout
|
|
246
|
-
.toString()
|
|
247
|
-
.split('\n')
|
|
248
|
-
.filter((item) => item.includes('card') && (filter ? item.includes(filter) : true))
|
|
249
|
-
.map((item) => parseAudioCard(item));
|
|
250
|
-
resolve(arr);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
});
|
|
104
|
+
return Promise.resolve([]);
|
|
254
105
|
};
|
|
255
106
|
exports.getAudioCards = getAudioCards;
|
|
256
|
-
const dic = ['fileSystem', 'size', 'used', 'avail', 'usedPercent', 'mounted'];
|
|
257
|
-
const parser = (str) => {
|
|
258
|
-
return str
|
|
259
|
-
.split('\n')
|
|
260
|
-
.filter((item) => item)
|
|
261
|
-
.map((item) => item.split(' ').filter((s) => !!s))
|
|
262
|
-
.map((item) => {
|
|
263
|
-
const res = {};
|
|
264
|
-
dic.forEach((key, i) => (res[key] = item[i]));
|
|
265
|
-
return res;
|
|
266
|
-
})
|
|
267
|
-
.map((item) => ({
|
|
268
|
-
...item,
|
|
269
|
-
total: parseFloat(item.size),
|
|
270
|
-
percentage: parseFloat(item['use%']),
|
|
271
|
-
}));
|
|
272
|
-
};
|
|
273
107
|
const getDiskUsage = async () => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
resolve(parser(stdout.toString()));
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
});
|
|
108
|
+
if (processManager.getDiskUsage) {
|
|
109
|
+
return processManager.getDiskUsage();
|
|
110
|
+
}
|
|
111
|
+
return Promise.resolve([]);
|
|
284
112
|
};
|
|
285
113
|
exports.getDiskUsage = getDiskUsage;
|
|
@@ -113,5 +113,58 @@ class PProcessLinux {
|
|
|
113
113
|
}
|
|
114
114
|
return res;
|
|
115
115
|
}
|
|
116
|
+
async getDiskUsage() {
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
(0, child_process_1.exec)('df -h | grep G', (err, stdout) => {
|
|
119
|
+
if (err) {
|
|
120
|
+
resolve([]);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
resolve(parser(stdout.toString()));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
getAudioCards(filter = '') {
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
(0, child_process_1.exec)(`arecord -l`, (err, stdout) => {
|
|
131
|
+
if (err) {
|
|
132
|
+
reject(err);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const arr = stdout
|
|
136
|
+
.toString()
|
|
137
|
+
.split('\n')
|
|
138
|
+
.filter((item) => item.includes('card') && (filter ? item.includes(filter) : true))
|
|
139
|
+
.map((item) => parseAudioCard(item));
|
|
140
|
+
resolve(arr);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
116
145
|
}
|
|
117
146
|
exports.PProcessLinux = PProcessLinux;
|
|
147
|
+
const parseAudioCard = (v) => {
|
|
148
|
+
let card = /card \d/.exec(v) ?? ['card 1'];
|
|
149
|
+
card = parseInt(card[0].replace('card ', ''), 10);
|
|
150
|
+
let device = /device \d/.exec(v) ?? ['device 0'];
|
|
151
|
+
device = parseInt(device[0].replace('device ', ''), 10);
|
|
152
|
+
return { text: v, index: `hw:${card},${device}` };
|
|
153
|
+
};
|
|
154
|
+
const dic = ['fileSystem', 'size', 'used', 'avail', 'usedPercent', 'mounted'];
|
|
155
|
+
const parser = (str) => {
|
|
156
|
+
return str
|
|
157
|
+
.split('\n')
|
|
158
|
+
.filter((item) => item)
|
|
159
|
+
.map((item) => item.split(' ').filter((s) => !!s))
|
|
160
|
+
.map((item) => {
|
|
161
|
+
const res = {};
|
|
162
|
+
dic.forEach((key, i) => (res[key] = item[i]));
|
|
163
|
+
return res;
|
|
164
|
+
})
|
|
165
|
+
.map((item) => ({
|
|
166
|
+
...item,
|
|
167
|
+
total: parseFloat(item.size),
|
|
168
|
+
percentage: parseFloat(item['use%']),
|
|
169
|
+
}));
|
|
170
|
+
};
|
|
@@ -17,8 +17,13 @@ export interface PProcess {
|
|
|
17
17
|
getProcessCommandByPid(pid: number): Promise<string>;
|
|
18
18
|
isProcessDied(pid: number): Promise<boolean>;
|
|
19
19
|
getProcessByPpid(ppid: number): Promise<number[]>;
|
|
20
|
-
SIGKILL(pid: number): Promise<{
|
|
20
|
+
SIGKILL(pid: number, signal?: number, times?: number): Promise<{
|
|
21
21
|
success: boolean;
|
|
22
22
|
error: string;
|
|
23
23
|
}>;
|
|
24
|
+
getDiskUsage?(): Promise<DiskInfo[]>;
|
|
25
|
+
getAudioCards?(filter?: string): Promise<{
|
|
26
|
+
text: string;
|
|
27
|
+
index: string;
|
|
28
|
+
}[]>;
|
|
24
29
|
}
|
|
@@ -6,8 +6,10 @@ export declare class PProcessWin32 implements PProcess {
|
|
|
6
6
|
getProcessCommandByPid(pid: number): Promise<string>;
|
|
7
7
|
isProcessDied(pid: number): Promise<boolean>;
|
|
8
8
|
getProcessByPpid(ppid: number): Promise<number[]>;
|
|
9
|
-
SIGKILL(pid: number): Promise<{
|
|
9
|
+
SIGKILL(pid: number, signal?: number, times?: number): Promise<{
|
|
10
10
|
success: boolean;
|
|
11
11
|
error: string;
|
|
12
12
|
}>;
|
|
13
|
+
getDiskUsage(): Promise<any>;
|
|
14
|
+
getAudioCards(): Promise<never[]>;
|
|
13
15
|
}
|
|
@@ -103,12 +103,57 @@ class PProcessWin32 {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
-
async SIGKILL(pid) {
|
|
106
|
+
async SIGKILL(pid, signal, times) {
|
|
107
107
|
return new Promise((resolve) => {
|
|
108
108
|
(0, child_process_1.exec)(`taskkill /pid ${pid} /T /F`, (err) => {
|
|
109
109
|
resolve({ success: true, error: err.toString() });
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
|
+
async getDiskUsage() {
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
(0, child_process_1.exec)('wmic logicaldisk get Caption,FreeSpace,Size /format:csv', (err, stdout) => {
|
|
116
|
+
if (err) {
|
|
117
|
+
resolve([]);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const lines = stdout.split('\r\n').filter((line) => line.trim() !== '');
|
|
121
|
+
// format:csv output has a header line "Node,Caption,FreeSpace,Size" usually.
|
|
122
|
+
// It might also have empty lines.
|
|
123
|
+
const disks = lines
|
|
124
|
+
.map((line) => {
|
|
125
|
+
// CSV parsing: simple split by comma
|
|
126
|
+
const parts = line.split(',').map((s) => s.trim());
|
|
127
|
+
if (parts.length < 4)
|
|
128
|
+
return null; // Node, Caption, FreeSpace, Size
|
|
129
|
+
// Header check
|
|
130
|
+
if (parts[1] === 'Caption')
|
|
131
|
+
return null;
|
|
132
|
+
const caption = parts[1];
|
|
133
|
+
const freeSpace = parseInt(parts[2]);
|
|
134
|
+
const size = parseInt(parts[3]);
|
|
135
|
+
if (isNaN(size) || isNaN(freeSpace))
|
|
136
|
+
return null;
|
|
137
|
+
const used = size - freeSpace;
|
|
138
|
+
const usedPercent = size > 0 ? (used / size) * 100 : 0;
|
|
139
|
+
return {
|
|
140
|
+
fileSystem: caption,
|
|
141
|
+
size: size, // bytes
|
|
142
|
+
used: used,
|
|
143
|
+
avail: freeSpace,
|
|
144
|
+
usedPercent: `${usedPercent.toFixed(0)}%`,
|
|
145
|
+
mounted: caption,
|
|
146
|
+
total: size,
|
|
147
|
+
percentage: parseFloat(usedPercent.toFixed(0)),
|
|
148
|
+
};
|
|
149
|
+
})
|
|
150
|
+
.filter((item) => item);
|
|
151
|
+
resolve(disks);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
getAudioCards() {
|
|
156
|
+
return Promise.resolve([]);
|
|
157
|
+
}
|
|
113
158
|
}
|
|
114
159
|
exports.PProcessWin32 = PProcessWin32;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pubo-node",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.202",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"types": "./lib/index.d.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@types/node": "^17.0.25",
|
|
21
|
-
"pubo-utils": "^1.0.
|
|
21
|
+
"pubo-utils": "^1.0.202",
|
|
22
22
|
"yaml": "^2.5.1"
|
|
23
23
|
},
|
|
24
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "6cab20969ce73935cf78b48ddecfab0adca48174",
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"del": "^5.1.0",
|
|
27
27
|
"eslint": "^8.42.0",
|