@flun/windows 3.0.10 → 3.0.11

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/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
  # 变更日志
2
+
3
+ ## [3.0.11] - 2026-10-25 15:57
4
+ ### 优化
5
+ - 项目标点已改为以英文标点为主;
6
+ - 行宽改为以 123 为主,部分超宽表格改为了列表;
7
+
2
8
  ## [3.0.10] - 2026-09-29 19:29
3
9
  ### 更改
4
10
  - 加固了拷贝文件的一些逻辑;
@@ -6,9 +12,4 @@
6
12
 
7
13
  ## [3.0.9] - 2026-07-24 19:35
8
14
  ### 优化
9
- - 内部一些优化;
10
-
11
- ## [3.0.8] - 2026-07-22 22:04
12
- ### 优化:
13
- - 对 'README.md' 中一些描述做了一些优化;
14
- - 对 'package.json' 的细节做了一些优化;
15
+ - 内部一些优化;
package/LICENSE CHANGED
@@ -1,19 +1,35 @@
1
- ISC 开源许可声明
2
- 版权信息
3
- 衍生开发 Copyright (c) 2026, flun <open@flun.top>
4
-
5
- 本项目(软件包)基于 Schley Andrew Kutz 等作者的原代码进行开发、修改和扩展。
6
-
7
- 使用条款
8
- 本软件以“原样”提供,任何人都可以自由地:
9
-
10
- 使用:用于任何目的,包括商业用途。
11
-
12
- 修改:根据需要改编代码。
13
-
14
- 分发:免费或收费地分享原版或修改后的软件。
15
-
16
- 唯一的条件是:不能从事非法及其它不正当行为,否则由您自行承担全部责任。
17
-
18
- 免责声明
19
- 作者对软件的质量不作任何保证。因使用本软件而产生的任何问题(如数据丢失、系统故障等),作者不承担任何责任。
1
+ ISC 许可证 (ISC License)
2
+
3
+ Copyright (c) 2026, flun <open@flun.top>
4
+ 衍生开发 本项目基于 Schley Andrew Kutz 等作者的原代码进行开发、修改和扩展;
5
+
6
+ 特此免费授予任何获得本软件及相关文档文件("软件")副本的人,不受限制地处理本软件的权利,
7
+ 包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,
8
+ 并允许向其提供本软件的人这样做,但须满足以下条件:
9
+
10
+ 上述版权声明和本许可声明应包含在本软件的所有副本或重要部分中;
11
+
12
+ 本软件按"原样"提供,不作任何形式的明示或暗示的保证,
13
+ 包括但不限于适销性、特定用途的适用性和不侵权的保证;
14
+ 在任何情况下,作者或版权持有人均不对因本软件或本软件的使用或其它交易而产生的
15
+ 任何索赔、损害或其它责任负责,无论是合同之诉、侵权之诉还是其它行为之诉;
16
+
17
+ ---
18
+
19
+ 英文原文 (English Version)
20
+
21
+ ISC License
22
+
23
+ Copyright (c) 2026, flun <open@flun.top>
24
+
25
+ Permission to use, copy, modify, and/or distribute this software for any
26
+ purpose with or without fee is hereby granted, provided that the above
27
+ copyright notice and this permission notice appear in all copies.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
30
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
31
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
32
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
33
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
34
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
35
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,32 +1,35 @@
1
1
  # @flun/windows
2
2
 
3
- 本工具库能将您的 Node.js 应用程序作为 Windows 后台服务来运行和管理,支持服务的安装、启动、停止及卸载全流程;还提供事件日志等功能,它主要用于面向生产环境应用程序的部署与运维;如需联系,邮箱: [open@flun.top](mailto:open@flun.top)
3
+ 本工具库能将您的 Node.js 应用程序作为 Windows 后台服务来运行和管理,支持服务的安装、启动、停止及卸载全流程;
4
+ 还提供事件日志等功能,它主要用于面向生产环境应用程序的部署与运维;如需联系,邮箱: [open@flun.top](mailto:open@flun.top)
4
5
 
5
6
  ### 本包基于 ESM 模块系统编写->拥抱未来趋势
6
7
 
7
- - **推荐方式**:使用 `import` / `export` 语法,静态分析更友好,工具链兼容性最佳;
8
- - **兼容方式**:Node.js ≥ 23.5.0 原生支持 `require(esm)`;22.12+ 需开启 `--experimental-require-module` 标志;
9
- - **重要**:本文档所有示例均采用 **ESM 标准**,请确保你的项目 `package.json` 中已设置 `"type": "module"`,或将脚本后缀改为 `.mjs`;
8
+ - **推荐方式**:使用 `import` / `export` 语法,静态分析更友好,工具链兼容性最佳;
9
+ - **兼容方式**:Node.js ≥ 23.5.0 原生支持 `require(esm)`;22.12+ 需开启 `--experimental-require-module` 标志;
10
+ - **重要**:本文档所有示例均采用 **ESM 标准**,请确保你的项目 `package.json` 中已设置 `"type": "module"`,或将脚本后缀改为
11
+ `.mjs`;
10
12
 
11
13
  ## 功能概述
12
14
 
13
- @flun/windows 提供以下功能:
15
+ @flun/windows 提供以下功能:
14
16
 
15
- - **服务管理**:将 Node.js 脚本作为原生 Windows 服务运行(含监控功能)
16
- - **事件日志**:写入 Windows 事件日志
17
- - **系统命令**:
18
- - _权限提升_:以管理员权限运行命令(会触发 UAC 确认)
19
- - _Sudo 执行_:以管理员身份执行命令
20
- - _权限检测_:检测当前用户是否具有管理员权限
21
- - _进程列表_:列出运行中的 Windows 进程/服务
22
- - _终止进程_:通过 PID 终止特定进程
17
+ - **服务管理**:将 Node.js 脚本作为原生 Windows 服务运行(含监控功能)
18
+ - **事件日志**:写入 Windows 事件日志
19
+ - **系统命令**:
20
+ - _权限提升_:以管理员权限运行命令(会触发 UAC 确认)
21
+ - _Sudo 执行_:以管理员身份执行命令
22
+ - _权限检测_:检测当前用户是否具有管理员权限
23
+ - _进程列表_:列出运行中的 Windows 进程/服务
24
+ - _终止进程_:通过 PID 终止特定进程
23
25
  ---
24
26
 
25
27
  ## 配置
26
28
 
27
29
  ### 允许安装脚本执行
28
30
 
29
- 本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了 `ignore-scripts=true`),可能会导致安装不完整或运行时异常;
31
+ 本包在安装时可能触发某些依赖包的自动脚本(如 `postinstall` 等);如果你的 npm 全局配置或项目配置禁止了脚本执行(例如设置了
32
+ `ignore-scripts=true`),可能会导致安装不完整或运行时异常;
30
33
 
31
34
  推荐在项目根目录的 `package.json` 中添加 `allowScripts` 字段,显式放行本包及其依赖的脚本:
32
35
 
@@ -34,32 +37,34 @@
34
37
  {
35
38
  "allowScripts": {
36
39
  "@flun/windows": true
37
- // 如果依赖的其他包(如 bcrypt、electron-winstaller 等)也有脚本,请按需添加,格式相同
40
+ // 如果依赖的其他包(如 bcrypt、electron-winstaller 等)也有脚本,请按需添加,格式相同
38
41
  }
39
42
  }
40
43
  ```
41
44
 
42
- > 如果你信任所有安装包,也可以直接在项目 `.npmrc` 中设置 `allow-scripts = false`(表示关闭脚本拦截,所有脚本均允许执行),或删除 `ignore-scripts` 字段;
45
+ > 如果你信任所有安装包,也可以直接在项目 `.npmrc` 中设置 `allow-scripts = false`(表示关闭脚本拦截,所有脚本均允许执行),
46
+ 或删除 `ignore-scripts` 字段;
43
47
 
44
48
  ---
45
49
 
46
50
  ## 安装
47
51
 
48
- 推荐通过 npm 全局安装:`npm i -g @flun/windows`
52
+ 推荐通过 npm 全局安装:`npm i -g @flun/windows`
49
53
 
50
- 然后在项目根目录执行:`npm link @flun/windows`
54
+ 然后在项目根目录执行:`npm link @flun/windows`
51
55
 
52
56
  局部安装:`npm i @flun/windows`
53
57
 
54
58
  ## 无原生模块依赖
55
59
 
56
- Windows 上的原生 Node 模块安装复杂,通常需要 Visual Studio 和 node-gyp 进行编译;@flun/windows **不依赖任何原生模块**,所有二进制工具均已预打包,无需安装 Visual Studio 等编译环境;
60
+ Windows 上的原生 Node 模块安装复杂,通常需要 Visual Studio 和 node-gyp 进行编译;@flun/windows **不依赖任何原生模块**,
61
+ 所有二进制工具均已预打包,无需安装 Visual Studio 等编译环境;
57
62
 
58
63
  # Windows 服务管理
59
64
 
60
- @flun/windows 可将 Node.js 脚本转换为 Windows 服务;注意:创建服务需要管理员权限!!
65
+ @flun/windows 可将 Node.js 脚本转换为 Windows 服务;注意:创建服务需要管理员权限!!
61
66
 
62
- 基础示例:
67
+ 基础示例:
63
68
 
64
69
  ```js
65
70
  import { Service } from '@flun/windows';
@@ -74,22 +79,23 @@ const svc = new Service({
74
79
  '--max-old-space-size=4096'
75
80
  ]
76
81
  // ===== 其它可选配置 =====
77
- //, maxRetries: null // 最大重试次数(默认null,表示忽略)
78
- //, maxRestarts: 3 // 60秒内最大重启次数(默认3)
79
- //, stoptimeout: 30 // 停止服务超时时间(秒,默认30)
80
- //, wait: 1 // 重启等待时间(秒,默认1)
81
- //, scriptOptions: '' // 传递给脚本的参数(默认空字符串)
82
- //, stopparentfirst: false // 是否先停止父进程(默认false)
83
- //, abortOnError: false // 遇到错误是否退出进程(默认false)
84
- //, grow: 0.25 // 重启等待时间增长比例(默认0.25即25%)
85
- //, logpath: null // 日志文件路径(默认null,使用默认路径)
86
- //, logmode: 'rotate' // 日志模式('reset', 'roll', 'append')
87
- //, execPath: process.execPath // Node.js可执行文件路径(默认当前Node)
88
- //, logOnAs: {} // 服务运行的用户凭据('domain'、'account'、'password'和'mungeCredentialsAfterInstall')
89
- //, env: undefined // 环境变量(默认:undefined)
90
- //, logging: null // 日志配置(默认:mode = 'append', pattern = 'yyyMMdd', sizeThreshold = 10240, keepFiles = 8)
91
- //, allowServiceLogon: undefined // 允许服务登录(默认undefined)
92
- //, workingDirectory: process.cwd() // 工作目录(默认当前目录)
82
+ //, maxRetries: null // 最大重试次数(默认null,表示忽略)
83
+ //, maxRestarts: 3 // 60秒内最大重启次数(默认3)
84
+ //, stoptimeout: 30 // 停止服务超时时间(秒,默认30)
85
+ //, wait: 1 // 重启等待时间(秒,默认1)
86
+ //, scriptOptions: '' // 传递给脚本的参数(默认空字符串)
87
+ //, stopparentfirst: false // 是否先停止父进程(默认false)
88
+ //, abortOnError: false // 遇到错误是否退出进程(默认false)
89
+ //, grow: 0.25 // 重启等待时间增长比例(默认0.25即25%)
90
+ //, logpath: null // 日志文件路径(默认null,使用默认路径)
91
+ //, logmode: 'rotate' // 日志模式('reset', 'roll', 'append')
92
+ //, execPath: process.execPath // Node.js可执行文件路径(默认当前Node)
93
+ //, logOnAs: {} // 服务运行的用户凭据('domain'、'account'、'password'和'mungeCredentialsAfterInstall')
94
+ //, env: undefined // 环境变量(默认:undefined)
95
+ //, logging: null // 日志配置(默认:mode = 'append', pattern = 'yyyMMdd', sizeThreshold = 10240, keepFiles
96
+ // = 8)
97
+ //, allowServiceLogon: undefined // 允许服务登录(默认undefined)
98
+ //, workingDirectory: process.cwd() // 工作目录(默认当前目录)
93
99
  });
94
100
 
95
101
  // 监听安装完成事件
@@ -115,7 +121,7 @@ svc.install();
115
121
 
116
122
  ### 脚本参数传递
117
123
 
118
- 通过 `scriptOptions` 配置:
124
+ 通过 `scriptOptions` 配置:
119
125
 
120
126
  ```js
121
127
  import { Service } from '@flun/windows';
@@ -127,10 +133,11 @@ const svc1 = new Service({
127
133
 
128
134
  ### 环境变量设置
129
135
 
130
- `@flun/windows` 会自动向服务进程注入 `USERPROFILE` 环境变量,其值为**安装服务时当前登录用户的主目录**(例如 `C:\Users\你的用户名`;
136
+ `@flun/windows` 会自动向服务进程注入 `USERPROFILE` 环境变量,其值为**安装服务时当前登录用户的主目录**(例如
137
+ `C:\Users\你的用户名`;
131
138
  这确保了服务中调用 `os.homedir()` 能返回正确的用户目录,即使服务以 `LocalSystem` 等系统账户运;
132
139
 
133
- **你通常不需要手动设置 `USERPROFILE`**,如需添加其他自定义环境变量,可按以下方式:
140
+ **你通常不需要手动设置 `USERPROFILE`**,如需添加其他自定义环境变量,可按以下方式:
134
141
 
135
142
  ```js
136
143
  import { Service } from '@flun/windows';
@@ -176,7 +183,9 @@ svc.logOnAs.account = 'username';
176
183
  svc.logOnAs.password = 'password';
177
184
  svc.logOnAs.mungeCredentialsAfterInstall: true // 默认
178
185
  ```
179
- > 💡 **提示**:即使你使用默认的 `LocalSystem` 账户运行服务,`@flun/windows` 也会通过自动注入 `USERPROFILE` 环境变量,使你的应用能够正确获取当前登录用户的目录(例如读取用户目录下的配置文件);因此,你不需要为了修正 `os.homedir()` 而特意配置 `logOnAs` 账户和密码;
186
+ > 💡 **提示**:即使你使用默认的 `LocalSystem` 账户运行服务,`@flun/windows` 也会通过自动注入 `USERPROFILE` 环境变量,
187
+ 使你的应用能够正确获取当前登录用户的目录(例如读取用户目录下的配置文件);因此,你不需要为了修正 `os.homedir()` 而特意配置
188
+ `logOnAs` 账户和密码;
180
189
 
181
190
  ### 服务卸载
182
191
 
@@ -187,29 +196,29 @@ const svc = new Service({
187
196
  });
188
197
  svc.on('uninstall', ()=>{
189
198
  console.log('卸载完成');
190
- console.log('服务是否存在:', svc.exists);
191
- // svc.exists 返回值说明:
199
+ console.log('服务是否存在:', svc.exists);
200
+ // svc.exists 返回值说明:
192
201
  // 0: 服务和相关文件没有
193
- // 1: 服务已注册但相关文件不存在(异常情况)
194
- // 2: 服务未注册但相关文件存在(文件残留)
195
- // 3: 服务已注册且相关文件存在(正常状态)
202
+ // 1: 服务已注册但相关文件不存在(异常情况)
203
+ // 2: 服务未注册但相关文件存在(文件残留)
204
+ // 3: 服务已注册且相关文件存在(正常状态)
196
205
  });
197
206
 
198
207
  svc.uninstall();
199
208
  ```
200
209
 
201
- 注:卸载仅移除服务相关文件,**不会删除您的 Node.js 脚本**;
210
+ 注:卸载仅移除服务相关文件,**不会删除您的 Node.js 脚本**;
202
211
 
203
212
  ### 智能重启机制
204
213
 
205
- @flun/windows 提供可配置的重启策略:
214
+ @flun/windows 提供可配置的重启策略:
206
215
 
207
216
  ```js
208
217
  import { Service } from '@flun/windows';
209
218
  const svc = new Service({
210
219
  ..., // 基本配置略...
211
- wait: 2, // 初始等待时间(秒)
212
- grow: 0.5, // 等待时间增长比例(50%)
220
+ wait: 2, // 初始等待时间(秒)
221
+ grow: 0.5, // 等待时间增长比例(50%)
213
222
  maxRetries: 5, // 最大重试次数
214
223
  maxRestarts: 3 // 60秒内最大重启次数
215
224
  });
@@ -217,13 +226,14 @@ const svc = new Service({
217
226
 
218
227
  ### 服务实现原理
219
228
 
220
- 使用 [winsw]为每个服务生成独立的 `.exe` 和 `.xml` 配置文件,存储在脚本所在目录的 `daemon` 子文件夹中;服务日志可通过 Windows 事件查看器查看;
229
+ 使用 [winsw]为每个服务生成独立的 `.exe` 和 `.xml` 配置文件,存储在脚本所在目录的 `daemon` 子文件夹中;服务日志可通过 Windows
230
+ 事件查看器查看;
221
231
  此外,服务启动时会自动设置 `USERPROFILE` 环境变量为安装服务时当前用户的用户目录,保证路径相关 API 的行为与预期一致;
222
232
  ---
223
233
 
224
234
  # 事件日志系统
225
235
 
226
- @flun/windows 提供非 C++ 依赖的事件日志功能:
236
+ @flun/windows 提供非 C++ 依赖的事件日志功能:
227
237
 
228
238
  ```js
229
239
  import { EventLogger } from '@flun/windows';
@@ -250,15 +260,16 @@ const eLog2 = new EventLogger({
250
260
 
251
261
  # 系统命令工具
252
262
 
253
- ## elevate(权限提升)
263
+ ## elevate(权限提升)
254
264
 
255
- `elevate` 类似于 Linux/Mac 上的 `sudo`,它会尝试将当前用户的权限提升为本地管理员;但要求当前用户具有管理员权限,若无管理员权限,命令将失败并返回 `access denied` 错误;
265
+ `elevate` 类似于 Linux/Mac 上的 `sudo`,它会尝试将当前用户的权限提升为本地管理员;但要求当前用户具有管理员权限,
266
+ 若无管理员权限,命令将失败并返回 `access denied` 错误;
256
267
 
257
268
  系统会弹出 UAC 提示,确认用户是否允许继续;
258
269
 
259
- **语法**:`elevate(cmd[, options][, callback])`
270
+ **语法**:`elevate(cmd[, options][, callback])`
260
271
 
261
- **示例**:
272
+ **示例**:
262
273
 
263
274
  ```js
264
275
  import { elevate } from '@flun/windows';
@@ -280,18 +291,19 @@ elevate('echo "Hello World" && whoami', (err, stdout)=> { }); // options视为
280
291
  elevate('echo "Hello World" && whoami'); // 无回调
281
292
  ```
282
293
 
283
- ## sudo(管理员执行)
294
+ ## sudo(管理员执行)
284
295
 
285
- `sudo` 与 Linux/Mac 上的 `sudo` 类似;与 `elevate` 雷同,但稳定性更高,这仍要求用户具有管理员权限,并且需在Windows上启用sudo命令,否则命令将失败;
296
+ `sudo` 与 Linux/Mac 上的 `sudo` 类似;与 `elevate` 雷同,但稳定性更高,这仍要求用户具有管理员权限,
297
+ 并且需在Windows上启用sudo命令,否则命令将失败;
286
298
 
287
- **语法**:`sudo(cmd[, options][, callback])`
299
+ **语法**:`sudo(cmd[, options][, callback])`
288
300
 
289
- **参数说明**:
290
- - `cmd`:要执行的命令
291
- - `options`(可选):执行选项
292
- - `callback`(可选):执行完成后的回调函数
301
+ **参数说明**:
302
+ - `cmd`:要执行的命令
303
+ - `options`(可选):执行选项
304
+ - `callback`(可选):执行完成后的回调函数
293
305
 
294
- **示例**:
306
+ **示例**:
295
307
 
296
308
  ```js
297
309
  import { sudo } from '@flun/windows';
@@ -315,9 +327,9 @@ sudo('echo "Hello World" && whoami' ); // 无回调
315
327
 
316
328
  此异步命令用于判断当前用户是否拥有管理员权限;它会向回调函数传递一个布尔值,如果用户是管理员则返回 `true`,否则返回 `false`;
317
329
 
318
- **语法**:`isAdminUser(callback)`
330
+ **语法**:`isAdminUser(callback)`
319
331
 
320
- **示例**:
332
+ **示例**:
321
333
 
322
334
  ```js
323
335
  import { isAdminUser } from '@flun/windows';
@@ -355,11 +367,11 @@ kill(进程PID, () => {
355
367
 
356
368
  ## elevate 和 sudo 函数常见问题
357
369
 
358
- 1. **sudo 函数执行失败**:这可能是因为 `sudo.exe` 外部工具不兼容当前系统,建议使用 `elevate` 函数作为替代
370
+ 1. **sudo 函数执行失败**:这可能是因为 `sudo.exe` 外部工具不兼容当前系统,建议使用 `elevate` 函数作为替代
359
371
 
360
- 2. **权限被拒绝**:确保当前用户具有管理员权限,可以运行 `isAdminUser` 函数检查
372
+ 2. **权限被拒绝**:确保当前用户具有管理员权限,可以运行 `isAdminUser` 函数检查
361
373
 
362
- 3. **回调函数未执行**:确保正确传递回调函数,如果不需要回调,可以不传但无法获取执行结果
374
+ 3. **回调函数未执行**:确保正确传递回调函数,如果不需要回调,可以不传但无法获取执行结果
363
375
 
364
376
  4. **sudo 函数需要在系统中设置并启用sudo**
365
377
 
@@ -370,10 +382,10 @@ kill(进程PID, () => {
370
382
 
371
383
  ---
372
384
 
373
- # 致谢!!!
385
+ # 致谢!!!
374
386
 
375
387
  感谢所有贡献者,特别感谢 @arthurblake、@hockeytim11 等开发者的贡献;
376
388
 
377
389
  # 许可证
378
390
 
379
- winsw 和 sudowin 遵循各自所有者的许可证;@flun/windows 核心代码采用 ISC 许可证(具体内容请见许可证文档)
391
+ winsw 和 sudowin 遵循各自所有者的许可证;@flun/windows 核心代码采用 ISC 许可证(具体内容请见许可证文档)
@@ -3,4 +3,4 @@
3
3
  <supportedRuntime version="v2.0.50727" />
4
4
  <supportedRuntime version="v4.0" />
5
5
  </startup>
6
- </configuration>
6
+ </configuration>
package/copy-files.js CHANGED
@@ -15,11 +15,11 @@ const __filename = fileURLToPath(import.meta.url), __dirname = path.dirname(__fi
15
15
  const copyFile = () => {
16
16
  console.log(`🔍 检查 ${fileName} 文件...`), console.log(`📁 项目根目录:${targetDir}`);
17
17
  try {
18
- // 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
18
+ // 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
19
19
  if (!fs.existsSync(path.join(targetDir, 'package.json'))) return true;
20
20
  if (targetDir === path.resolve(__dirname, '..')) return true;
21
21
  if (fs.existsSync(targetFile)) return true; // 目标文件已存在 → 直接返回
22
- console.log(`⚠️ 在项目根目录未找到 ${fileName} 文件,正在创建...`);
22
+ console.log(`⚠️ 在项目根目录未找到 ${fileName} 文件,正在创建...`);
23
23
 
24
24
  fs.copyFileSync(sourceFile, targetFile); // 复制源文件到项目根目录
25
25
  console.log(`✓ 已创建 ${fileName} 示例文件:${targetFile}`);
package/index.d.ts CHANGED
@@ -2,15 +2,17 @@ import { elevate, sudo, isAdminUser, platform } from './lib/binaries.js';
2
2
  import { kill, list } from './lib/cmd.js';
3
3
  import { Service } from './lib/daemon.js';
4
4
  import { EventLogger } from './lib/eventlog.js';
5
- import { exec, execSync, fork, promisify, execPromise, path, fs, EventEmitter, isPermissionError, getDirname } from './lib/shared.js';
5
+ import {
6
+ exec, execSync, fork, promisify, execPromise, path, fs, EventEmitter, isPermissionError, getDirname
7
+ } from './lib/shared.js';
6
8
  import { generateXml, createExe } from './lib/winsw.js';
7
9
 
8
10
  // =================================== lib/binaries.js ===================================
9
11
  /**
10
12
  * ```js
11
13
  * // 文件导出内容
12
- * elevate(); // 提升当前进程权限(Windows UAC)
13
- * sudo(); // 使用sudo.exe提升权限(适用于Windows 10及以上版本)
14
+ * elevate(); // 提升当前进程权限(Windows UAC)
15
+ * sudo(); // 使用sudo.exe提升权限(适用于Windows 10及以上版本)
14
16
  * isAdminUser(); // 检查当前用户是否拥有管理员权限
15
17
  * platform(); // 获取操作系统平台字符串,用于判断当前运行环境是否为Windows
16
18
  * ```
@@ -74,7 +76,8 @@ declare module './lib/eventlog.js' {
74
76
  * isPermissionError(); // 检查错误对象是否表示权限错误的函数,用于判断操作失败是否由于权限不足引起
75
77
  * getDirname(); // 获取当前模块目录路径的函数,用于在ES模块环境中替代__dirname变量的功能
76
78
  * ```
77
- * >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、{@link fs}、{@link EventEmitter}
79
+ * >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、
80
+ * {@link fs}、{@link EventEmitter}
78
81
  * - 自定义函数:{@link execPromise}、{@link isPermissionError}、{@link getDirname}
79
82
  */
80
83
  declare module './lib/shared.js' {
@@ -96,7 +99,7 @@ declare module './lib/winsw.js' {
96
99
 
97
100
  // =================================== 模块导出入口 ===================================
98
101
  /**
99
- * Windows功能模块 主要功能:
102
+ * Windows功能模块 主要功能:
100
103
  * ```js
101
104
  * Service{}; // 服务管理类
102
105
  * EventLogger{}; // 事件日志记录器类
@@ -107,7 +110,8 @@ declare module './lib/winsw.js' {
107
110
  * list(); // 列出服务器上正在运行的进程
108
111
  * ```
109
112
  * ---
110
- * >查看定义:@see {@link Service}、{@link EventLogger}、{@link elevate}、{@link sudo}、{@link isAdminUser}、{@link kill}、{@link list}
113
+ * >查看定义:@see {@link Service}、{@link EventLogger}、{@link elevate}、{@link sudo}、{@link isAdminUser}、
114
+ * {@link kill}、{@link list}
111
115
  * @example
112
116
  * // 基础示例
113
117
  * import { Service } from '@flun/windows';
package/lib/binaries.js CHANGED
@@ -23,12 +23,12 @@ const __dirname = getDirname(import.meta.url), vbsPath = path.resolve(__dirname,
23
23
  };
24
24
 
25
25
  /**
26
- * 提升当前进程权限(Windows UAC)
26
+ * 提升当前进程权限(Windows UAC)
27
27
  * >查看定义:@see {@link elevate}
28
28
  * @param {string} cmd - 要使用提升权限执行的命令
29
29
  * @param {Object} [options] - 传递给 child_process.exec 的选项
30
30
  * @param {string} [options.cwd] - 工作目录
31
- * @param {number} [options.timeout] - 超时时间(毫秒)
31
+ * @param {number} [options.timeout] - 超时时间(毫秒)
32
32
  * @param {(error: Error|null, stdout: string, stderr: string) => void} [callback] - 执行完成后的回调函数
33
33
  * @returns {void}
34
34
  * @example
@@ -55,7 +55,8 @@ const elevate = (cmd, options, callback) => {
55
55
  const p = _params(options, callback), tmpDir = tmpdir(),
56
56
  [vbsFile, outFile, batFile, cmdFile] = ['flun_el.vbs', 'flun_el_output.txt', 'flun_el.bat', 'flun_el.cmd']
57
57
  .map(name => path.join(tmpDir, name)),
58
- escapedCmd = cmd.replace(/"/g, '""').replace(/%/g, "%%"), vbs = readFileSafe(vbsPath).replace(/{command}/g, escapedCmd);
58
+ escapedCmd = cmd.replace(/"/g, '""').replace(/%/g, "%%"),
59
+ vbs = readFileSafe(vbsPath).replace(/{command}/g, escapedCmd);
59
60
 
60
61
  // 写入VBS文件
61
62
  try {
@@ -80,12 +81,12 @@ const elevate = (cmd, options, callback) => {
80
81
  };
81
82
 
82
83
  /**
83
- * 使用 sudo 方式提升权限(与 elevate 相似但体验更好)
84
+ * 使用 sudo 方式提升权限(与 elevate 相似但体验更好)
84
85
  * >查看定义:@see {@link sudo}
85
86
  * @param {string} cmd - 要使用提升权限执行的命令
86
87
  * @param {Object} [options] - 传递给 child_process.exec 的选项
87
88
  * @param {string} [options.cwd] - 工作目录
88
- * @param {number} [options.timeout] - 超时时间(毫秒)
89
+ * @param {number} [options.timeout] - 超时时间(毫秒)
89
90
  * @param {(error: Error|null, stdout: string, stderr: string) => void} [callback] - 执行完成后的回调函数
90
91
  * @returns {void}
91
92
  * @example
package/lib/cmd.js CHANGED
@@ -5,7 +5,7 @@ import { exec } from './shared.js';
5
5
  * >查看定义:@see {@link kill}
6
6
  * @param {number} pid - 进程PID
7
7
  * @param {(error: Error|null, stdout: string, stderr: string) => void} [callback] - 执行完成后的回调函数
8
- * @param {boolean} [force=false] - 是否强制结束进程(默认: false)
8
+ * @param {boolean} [force=false] - 是否强制结束进程(默认: false)
9
9
  * @returns {void}
10
10
  * @example
11
11
  * import { kill } from '@flun/windows';
@@ -14,8 +14,8 @@ import { exec } from './shared.js';
14
14
  * });
15
15
  */
16
16
  const kill = (pid, callback, force = false) => {
17
- if (!pid) throw new Error('PID是kill操作必需的参数。');
18
- if (isNaN(pid)) throw new Error('PID必须为数字。');
17
+ if (!pid) throw new Error('PID是kill操作必需的参数;');
18
+ if (isNaN(pid)) throw new Error('PID必须为数字;');
19
19
  if (typeof callback !== 'function') callback = function () { };
20
20
  exec(`taskkill /PID ${pid}${force == true ? ' /f' : ''}`, callback);
21
21
  };
@@ -24,7 +24,7 @@ const kill = (pid, callback, force = false) => {
24
24
  * 列出服务器上正在运行的进程
25
25
  * >查看定义:@see {@link list}
26
26
  * @param {(processes: Array<Record<string, string>>) => void} callback - 回调函数,接收进程对象数组
27
- * @param {boolean} [verbose=false] - 是否显示详细信息(默认: false)
27
+ * @param {boolean} [verbose=false] - 是否显示详细信息(默认: false)
28
28
  * @returns {void}
29
29
  * @example
30
30
  * import { list } from '@flun/windows';
package/lib/daemon.js CHANGED
@@ -9,7 +9,8 @@ const __dirname = getDirname(import.meta.url), writeFileAsync = promisify(fs.wri
9
9
  /**
10
10
  * Windows服务管理类,提供创建、安装、卸载、启动、停止、重启和查询服务状态等功能
11
11
  * - 用于将Node.js脚本作为Windows服务运行,支持自动重启、日志记录和权限提升等功能
12
- * >查看定义:@see {@link Service}、{@link install}、{@link uninstall}、{@link start}、{@link stop}、{@link restart}、{@link exists}
12
+ * >查看定义:@see {@link Service}、{@link install}、{@link uninstall}、{@link start}、{@link stop}、
13
+ * {@link restart}、{@link exists}
13
14
  * @example
14
15
  * // 配置示例
15
16
  * import { Service } from '@flun/windows';
@@ -102,7 +103,8 @@ class Service extends EventEmitter {
102
103
  this.scriptOptions = scriptOptions; // 传递给脚本的选项;
103
104
 
104
105
  this.stopparentfirst = stopparentfirst; // 是否先停止父进程(默认:false);
105
- this.abortOnError = Boolean(abortOnError); // 当遇到导致node.js脚本无法运行错误时是否退出进程(默认:false);
106
+ this.abortOnError = Boolean(abortOnError);
107
+ // 当遇到导致node.js脚本无法运行错误时是否退出进程(默认:false);
106
108
  this.grow = Number(grow); // 重启等待时间的增长百分比(默认:0.25);
107
109
  this.logpath = logpath; // 日志文件路径(默认:与可执行文件相同的目录);
108
110
  this.logmode = logmode; // 日志模式(默认:rotate);可选值: rotate, truncate, append;
@@ -139,28 +141,30 @@ class Service extends EventEmitter {
139
141
  return `${this.id}.exe`;
140
142
  }
141
143
 
142
- // 服务名称(用于Windows服务管理)
144
+ // 服务名称(用于Windows服务管理)
143
145
  get #serviceName() {
144
146
  return this.id;
145
147
  }
146
148
 
147
149
  // 生成服务的XML配置
148
150
  get #xml() {
149
- const { script, scriptOptions, name, grow, wait, maxRestarts, abortOnError, stopparentfirst, maxRetries, id, nodeOptions,
150
- description, logpath, execPath, logOnAs, workingdirectory, stoptimeout, logmode, env, logging, allowServiceLogon } = this;
151
+ const { script, scriptOptions, name, grow, wait, maxRestarts, abortOnError, stopparentfirst,
152
+ maxRetries, id, nodeOptions, description, logpath, execPath, logOnAs, workingdirectory,
153
+ stoptimeout, logmode, env, logging, allowServiceLogon } = this;
151
154
 
152
- // 构建环境变量数组,自动添加 USERPROFILE(如果未显式设置)
155
+ // 构建环境变量数组,自动添加 USERPROFILE(如果未显式设置)
153
156
  let envList = [];
154
157
  if (env) envList.push(...(Array.isArray(env) ? env : [env]));
155
158
  if (process.env.USERPROFILE && !envList.some(e => e.name === 'USERPROFILE'))
156
159
  envList.push({ name: 'USERPROFILE', value: process.env.USERPROFILE });
157
160
 
158
- const wrapperArgs = ['--file', script, `--scriptoptions=${scriptOptions}`, '--log', `${name} 包装器`, '--grow', grow, '--wait', wait,
159
- '--maxrestarts', maxRestarts, '--abortonerror', abortOnError ? 'y' : 'n', '--stopparentfirst', stopparentfirst,
160
- ...(maxRetries !== null ? ['--maxretries', maxRetries] : [])];
161
+ const wrapperArgs = ['--file', script, `--scriptoptions=${scriptOptions}`, '--log', `${name} 包装器`,
162
+ '--grow', grow, '--wait', wait, '--maxrestarts', maxRestarts, '--abortonerror', abortOnError ? 'y' : 'n',
163
+ '--stopparentfirst', stopparentfirst, ...(maxRetries !== null ? ['--maxretries', maxRetries] : [])];
161
164
 
162
165
  return generateXml({
163
- name, id, nodeOptions, script: wrapper, scriptOptions, wrapperArgs, description, logpath, execPath, logOnAs, workingdirectory,
166
+ name, id, nodeOptions, script: wrapper, scriptOptions, wrapperArgs, description, logpath, execPath,
167
+ logOnAs, workingdirectory,
164
168
  stopparentfirst, stoptimeout, logmode, env: envList, logging, allowServiceLogon
165
169
  });
166
170
  }
@@ -219,7 +223,7 @@ class Service extends EventEmitter {
219
223
  /**
220
224
  * 将脚本安装为Windows服务
221
225
  * >查看定义:@see {@link install}
222
- * @param {string} [dir] - 服务文件将保存的目录(默认为脚本所在目录)
226
+ * @param {string} [dir] - 服务文件将保存的目录(默认为脚本所在目录)
223
227
  * @returns {Promise<void>}
224
228
  * @event install - 当安装过程完成时触发
225
229
  * @event alreadyinstalled - 如果服务已安装时触发
@@ -283,12 +287,12 @@ class Service extends EventEmitter {
283
287
  * });
284
288
  * svc.on('uninstall', ()=>{
285
289
  * console.log('卸载完成');
286
- * console.log('服务是否存在:', svc.exists);
287
- * // svc.exists 返回值说明:
290
+ * console.log('服务是否存在:', svc.exists);
291
+ * // svc.exists 返回值说明:
288
292
  * // 0: 服务和相关文件没有
289
- * // 1: 服务已注册但相关文件不存在(异常情况)
290
- * // 2: 服务未注册但相关文件存在(文件残留)
291
- * // 3: 服务已注册且相关文件存在(正常状态)
293
+ * // 1: 服务已注册但相关文件不存在(异常情况)
294
+ * // 2: 服务未注册但相关文件存在(文件残留)
295
+ * // 3: 服务已注册且相关文件存在(正常状态)
292
296
  * });
293
297
  *
294
298
  * svc.uninstall();
package/lib/eventlog.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { execPromise, execSync, isPermissionError } from './shared.js';
2
2
  import { elevate } from './binaries.js';
3
3
 
4
- const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'], validTypes = ['ERROR', 'WARNING', 'INFORMATION', 'SUCCESSAUDIT', 'FAILUREAUDIT'];
4
+ const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'],
5
+ validTypes = ['ERROR', 'WARNING', 'INFORMATION', 'SUCCESSAUDIT', 'FAILUREAUDIT'];
5
6
 
6
7
  /**
7
8
  * 事件日志记录器类,用于向Windows事件查看器写入日志
8
9
  * - 提供记录信息、警告、错误和审计成功、审计失败事件的方法
9
- * >查看定义:@see {@link EventLogger}、{@link info}、{@link warn}、{@link error}、{@link auditSuccess}、{@link auditFailure}
10
+ * >查看定义:@see {@link EventLogger}、{@link info}、{@link warn}、{@link error}、{@link auditSuccess}、
11
+ * {@link auditFailure}
10
12
  * @class EventLogger
11
13
  * @example
12
14
  * // 配置示例
@@ -28,9 +30,9 @@ const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'], validTypes = ['ERROR',
28
30
  class EventLogger {
29
31
  /**
30
32
  * @constructor
31
- * @param {string|Object} [config] - 配置字符串(作为 source)或配置对象
33
+ * @param {string|Object} [config] - 配置字符串(作为 source)或配置对象
32
34
  * @param {string} [config.source='Node.js'] - 事件源名称
33
- * @param {string} [config.eventLog='APPLICATION'] - 事件日志名称(APPLICATION, SYSTEM, SECURITY)
35
+ * @param {string} [config.eventLog='APPLICATION'] - 事件日志名称(APPLICATION, SYSTEM, SECURITY)
34
36
  */
35
37
  constructor(config = {}) {
36
38
  if (typeof config === 'string') config = { source: config };
@@ -57,7 +59,7 @@ class EventLogger {
57
59
  }
58
60
 
59
61
  /**
60
- * 获取当前事件日志名称(大写)
62
+ * 获取当前事件日志名称(大写)
61
63
  * @returns {string}
62
64
  */
63
65
  get eventLog() {
@@ -124,10 +126,14 @@ class EventLogger {
124
126
  // 判断是否需要使用PowerShell
125
127
  if (this.#shouldUsePowerShell(vType, vId)) {
126
128
  // 使用PowerShell的Write-EventLog命令
127
- const entryTypeMap = { 'ERROR': 'Error', 'WARNING': 'Warning', 'SUCCESSAUDIT': 'SuccessAudit', 'FAILUREAUDIT': 'FailureAudit' },
129
+ const entryTypeMap = {
130
+ 'ERROR': 'Error', 'WARNING': 'Warning',
131
+ 'SUCCESSAUDIT': 'SuccessAudit', 'FAILUREAUDIT': 'FailureAudit'
132
+ },
128
133
  entryType = entryTypeMap[vType] || 'Information', escapedMsg = pMsg.replace(/"/g, '""'),
129
134
  powHad = 'powershell -Command "Write-EventLog -LogName';
130
- command = `${powHad} '${vLog}' -Source '${vSrc}' -EventId ${vId} -EntryType ${entryType} -Message \\\"${escapedMsg}\\\""`;
135
+ command = `${powHad} '${vLog}' -Source '${vSrc}' -EventId ${vId}` +
136
+ ` -EntryType ${entryType} -Message \\\"${escapedMsg}\\\""`;
131
137
  } else {
132
138
  const eventCreateId = Math.min(Math.max(1, vId), 1000); // 限制在1-1000范围内
133
139
  command = `eventcreate /L ${vLog} /T ${vType} /SO "${vSrc}" /D "${pMsg}" /ID ${eventCreateId}`;
package/lib/shared.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 共享模块,导出在多个文件中使用的常用函数和模块
3
- * >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、{@link fs}、{@link EventEmitter}
3
+ * >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、
4
+ * {@link fs}、{@link EventEmitter}
4
5
  */
5
6
  import { exec, execSync, fork } from 'child_process';
6
7
  import { promisify } from 'util';
@@ -24,14 +25,15 @@ const execPromise = promisify(exec);
24
25
  */
25
26
  const isPermissionError = error => {
26
27
  const errorMessage = error?.message || error?.toString() || String(error),
27
- permissionErrors = ['拒绝访问', 'Access is denied', '权限不足', 'Insufficient privileges', 'AccessDenied', '需要提升权限',
28
+ permissionErrors = ['拒绝访问', 'Access is denied', '权限不足', 'Insufficient privileges',
29
+ 'AccessDenied', '需要提升权限',
28
30
  'requires elevation', 'Administrator', 'admin', '权限被拒绝'],
29
31
  lowerMessage = errorMessage.toLowerCase();
30
32
  return permissionErrors.some(keyword => lowerMessage.includes(keyword.toLowerCase()));
31
33
  }
32
34
 
33
35
  /**
34
- * 获取当前模块所在目录的路径(类似 CommonJS 的 __dirname)
36
+ * 获取当前模块所在目录的路径(类似 CommonJS 的 __dirname)
35
37
  * >查看定义:@see {@link getDirname}
36
38
  * @param {string} metaUrl - 传入 `import.meta.url`
37
39
  * @returns {string} 当前模块的目录路径
package/lib/winsw.js CHANGED
@@ -6,31 +6,31 @@ const __dirname = getDirname(import.meta.url);
6
6
  /**
7
7
  * @method generateXml 生成 winsw 配置文件的 XML;
8
8
  * >查看定义:@see {@link generateXml}
9
- * @param {Object} config - 配置对象,必须包含以下必需属性:
9
+ * @param {Object} config - 配置对象,必须包含以下必需属性:
10
10
  * - *id* 服务的标识符,只能包含字母和数字,不能有空格
11
11
  * - *name* 服务的描述性名称
12
- * - *execPath* Node.js 可执行文件的绝对路径(如:C:\Program Files\nodejs\node.exe)
13
- * - *script* Node.js 服务器脚本的绝对路径(包装脚本)
12
+ * - *execPath* Node.js 可执行文件的绝对路径(如:C:\Program Files\nodejs\node.exe)
13
+ * - *script* Node.js 服务器脚本的绝对路径(包装脚本)
14
14
  * - *workingdirectory* 服务运行的工作目录
15
15
  * @param {string} [config.description] - 在服务管理器中显示的描述信息
16
- * @param {string|string[]} [config.nodeOptions] - Node.js 选项(数组或空格分隔的字符串)
16
+ * @param {string|string[]} [config.nodeOptions] - Node.js 选项(数组或空格分隔的字符串)
17
17
  * @param {string} [config.wrapperArgs] - 传递给包装脚本的额外参数
18
18
  * @param {string} [config.logmode] - 日志模式(老式):'rotate'(默认), 'reset', 'roll', 'append'
19
19
  * @param {Object} [config.logging] - 日志配置对象,可替代 logmode
20
20
  * @param {string} [config.logging.mode] - 日志模式(现代),支持 'append', 'reset', 'none', 'roll-by-time', 'roll-by-size'
21
- * @param {string} [config.logging.pattern] - roll-by-time 模式的时间格式模式(默认:'yyyyMMdd')
22
- * @param {number} [config.logging.sizeThreshold] - roll-by-size 模式的大小阈值(默认:10240)
23
- * @param {number} [config.logging.keepFiles] - roll-by-size 模式保留的文件数(默认:8)
24
- * @param {string} [config.logpath] - 日志存储目录的绝对路径(默认:当前目录)
21
+ * @param {string} [config.logging.pattern] - roll-by-time 模式的时间格式模式(默认:'yyyyMMdd')
22
+ * @param {number} [config.logging.sizeThreshold] - roll-by-size 模式的大小阈值(默认:10240)
23
+ * @param {number} [config.logging.keepFiles] - roll-by-size 模式保留的文件数(默认:8)
24
+ * @param {string} [config.logpath] - 日志存储目录的绝对路径(默认:当前目录)
25
25
  * @param {string|string[]} [config.dependencies] - 进程依赖项的逗号分隔列表或数组
26
- * @param {{ name: string, value: string }[]|Object<string, string>} [config.env] - 环境变量配置(键/值对象或对象数组)
26
+ * @param {{ name: string, value: string }[]|Object<string, string>} [config.env] - 环境变量配置(键/值对象或对象数组)
27
27
  * @param {Object} [config.logOnAs] - 服务登录凭据配置
28
28
  * @param {string} [config.logOnAs.account] - 登录账户名
29
29
  * @param {string} [config.logOnAs.password] - 登录密码
30
30
  * @param {string} [config.logOnAs.domain] - 账户所在域
31
31
  * @param {boolean} [config.allowServiceLogon] - 是否允许服务登录
32
32
  * @param {boolean} [config.stopparentfirst] - 是否先停止父进程
33
- * @param {number} [config.stoptimeout] - 停止超时时间(秒)
33
+ * @param {number} [config.stoptimeout] - 停止超时时间(秒)
34
34
  * @returns {string} 生成的 XML 字符串
35
35
  * @throws {string} 当缺少必需配置时抛出错误
36
36
  */
@@ -41,7 +41,8 @@ const generateXml = config => {
41
41
  } = config;
42
42
 
43
43
  // 验证必需配置项是否存在
44
- if (!id || !name || !script || !execPath || !workingdirectory) throw "id,name,script,execPath,workingdirectory为必需配置项";
44
+ if (!id || !name || !script || !execPath || !workingdirectory)
45
+ throw "id,name,script,execPath,workingdirectory为必需配置项";
45
46
 
46
47
  let xml = [{ id }, { name }, { description }, { executable: execPath }]; // 基础 XML 结构
47
48
 
@@ -58,7 +59,8 @@ const generateXml = config => {
58
59
 
59
60
  // 日志配置处理:优先使用 logging 对象,其次使用 logmode
60
61
  if (logging) {
61
- const { mode = 'append', pattern = 'yyyyMMdd', sizeThreshold = 10240, keepFiles = 8 } = logging, logContent = [{ _attr: { mode } }];
62
+ const { mode = 'append', pattern = 'yyyyMMdd', sizeThreshold = 10240, keepFiles = 8 } = logging,
63
+ logContent = [{ _attr: { mode } }];
62
64
  if (mode === 'roll-by-time') logContent.push({ pattern }); // 按时间滚动日志
63
65
  else if (mode === 'roll-by-size') logContent.push({ sizeThreshold }), logContent.push({ keepFiles }); // 按大小滚动日志
64
66
  xml.push({ log: logContent });
@@ -88,7 +90,7 @@ const generateXml = config => {
88
90
  * @method createExe 创建 winsw 可执行文件及其配置文件
89
91
  * >查看定义:@see {@link createExe}
90
92
  * @param {string} name - 可执行文件名称(不含扩展名);
91
- * @param {string} dir - 输出目录(默认:当前工作目录)
93
+ * @param {string} dir - 输出目录(默认:当前工作目录)
92
94
  * @param {() => void} [callback] - 完成后的回调函数
93
95
  * @description 将 winsw.exe 的安装版本复制为根据服务id重命名的特定版本,
94
96
  * 并复制 .exe.config 文件以支持 .NET 4+ 运行时环境;
package/lib/wrapper.js CHANGED
@@ -8,14 +8,14 @@
8
8
  * 4. 重启次数限制和频率控制
9
9
  * 5. 支持通过事件日志或控制台输出日志
10
10
  *
11
- * 命令行参数:
12
- * -f, --file: 要运行的脚本绝对路径(必需)
11
+ * 命令行参数:
12
+ * -f, --file: 要运行的脚本绝对路径(必需)
13
13
  * -d, --cwd: 子进程工作目录
14
- * -l, --log: 日志描述名称(必需)
15
- * -e, --eventlog: 事件日志容器(APPLICATION或SYSTEM)
16
- * -m, --maxretries: 最大重启次数(-1表示无限制)
14
+ * -l, --log: 日志描述名称(必需)
15
+ * -e, --eventlog: 事件日志容器(APPLICATION或SYSTEM)
16
+ * -m, --maxretries: 最大重启次数(-1表示无限制)
17
17
  * -r, --maxrestarts: 时间窗口内最大重启次数
18
- * -w, --wait: 重启等待时间(秒)
18
+ * -w, --wait: 重启等待时间(秒)
19
19
  * -g, --grow: 等待时间增长系数
20
20
  * -a, --abortonerror: 错误时是否终止
21
21
  * -s, --stopparentfirst: 是否允许优雅退出
@@ -27,7 +27,7 @@ import yargs from 'yargs/yargs';
27
27
  import { hideBin } from 'yargs/helpers';
28
28
  import { EventLogger } from './eventlog.js';
29
29
 
30
- // 时间窗口(秒),默认时间窗口内最大重启次数,默认重启等待时间(秒),默认等待时间增长系数
30
+ // 时间窗口(秒),默认时间窗口内最大重启次数,默认重启等待时间(秒),默认等待时间增长系数
31
31
  const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowth = 0.25,
32
32
  booleanChoices = ['y', 'n', 'yes', 'no', 'true', 'false'],
33
33
  /**
@@ -104,7 +104,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
104
104
  alias: 'm',
105
105
  type: 'number',
106
106
  default: -1,
107
- description: '进程自动重启的最大次数(-1表示无限制)'
107
+ description: '进程自动重启的最大次数(-1表示无限制)'
108
108
  })
109
109
  .option('maxrestarts', {
110
110
  alias: 'r',
@@ -126,7 +126,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
126
126
  alias: 'g',
127
127
  type: 'number',
128
128
  default: defaultGrowth,
129
- description: '等待时间增长的增长百分比(0-1之间)',
129
+ description: '等待时间增长的增长百分比(0-1之间)',
130
130
  check: growth => {
131
131
  if (growth < 0 || growth > 1) throw new Error('增长率必须在0到1之间');
132
132
  return true;
@@ -150,7 +150,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
150
150
  })
151
151
  .option('scriptoptions', {
152
152
  type: 'string',
153
- description: '要传递给脚本的选项(空格分隔)',
153
+ description: '要传递给脚本的选项(空格分隔)',
154
154
  default: '',
155
155
  coerce: parseQuotedOption
156
156
  }).help().parse();
@@ -223,7 +223,7 @@ const prepareScriptArgs = () => {
223
223
 
224
224
  // 检查总重启次数限制
225
225
  if (argv.maxretries >= 0 && restartAttempts > argv.maxretries)
226
- logger.error(`重启次数过多。${scriptPath} 将不会被重启,已超过最大重启次数 ${argv.maxretries}`), process.exit(1);
226
+ logger.error(`重启次数过多;${scriptPath} 将不会被重启,已超过最大重启次数 ${argv.maxretries}`), process.exit(1);
227
227
 
228
228
  launchProcess('warn', `在意外退出后 ${waitTime} 毫秒重启: 尝试次数=${restartAttempts}`);
229
229
  }, waitTime);
@@ -233,7 +233,7 @@ const prepareScriptArgs = () => {
233
233
 
234
234
  /**
235
235
  * 启动子进程
236
- * @param {string} logLevel - 日志级别(info, warn, error等)
236
+ * @param {string} logLevel - 日志级别(info, warn, error等)
237
237
  * @param {string} message - 日志消息
238
238
  * @returns {void}
239
239
  */
@@ -273,7 +273,7 @@ const prepareScriptArgs = () => {
273
273
  logger.warn(exitMessage);
274
274
  // 检查是否需要因错误而终止
275
275
  if (code !== 0 && (argv.abortonerror === 'yes' || argv.abortonerror === 'y'))
276
- logger.error(`${scriptPath} 以错误代码 ${code} 退出,终止包装器进程`), process.exit(code || 1);
276
+ logger.error(`${scriptPath} 以错误代码 ${code} 退出,终止包装器进程`), process.exit(code || 1);
277
277
  else if (isForceKill) process.exit(0);
278
278
 
279
279
  childProcess = null, monitorChildProcess(); // 重新监控并可能重启
@@ -306,10 +306,10 @@ const prepareScriptArgs = () => {
306
306
  // 注册进程终止事件处理程序
307
307
  process.on('exit', terminateChildProcess);
308
308
  process.on('SIGINT', () => {
309
- logger.info('收到 SIGINT 信号,正在终止...'), terminateChildProcess();
309
+ logger.info('收到 SIGINT 信号,正在终止...'), terminateChildProcess();
310
310
  });
311
311
  process.on('SIGTERM', () => {
312
- logger.info('收到 SIGTERM 信号,正在终止...'), terminateChildProcess();
312
+ logger.info('收到 SIGTERM 信号,正在终止...'), terminateChildProcess();
313
313
  });
314
314
 
315
315
  // 处理未捕获异常
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flun/windows",
3
- "version": "3.0.10",
3
+ "version": "3.0.11",
4
4
  "description": "支持 Windows 服务、事件日志、UAC 以及多种与操作系统交互的辅助方法",
5
5
  "keywords": [
6
6
  "windows",
@@ -47,7 +47,7 @@
47
47
  "type": "git",
48
48
  "url": "git+https://github.com/OpenFlun/windows.git"
49
49
  },
50
- "homepage": "https://www.npmjs.com/package/@flun/windows#readme",
50
+ "homepage": "https://github.com/OpenFlun/windows#readme",
51
51
  "bugs": {
52
52
  "url": "https://github.com/OpenFlun/windows/issues"
53
53
  },