@wanghaopeng1148/deskpet 2.0.3 → 2.0.4
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 +22 -14
- package/bin/deskpet.mjs +35 -3
- package/dist/node/server/utils/auto-start.js +44 -29
- package/package.json +1 -1
- package/server/utils/auto-start.ts +43 -28
package/README.md
CHANGED
|
@@ -432,17 +432,25 @@ resources/ 皮肤、图标等随包资源
|
|
|
432
432
|
- 通知通道:系统原生通知(osascript / PowerShell Toast / notify-send)+ 管理台页面内提示 + 微信。
|
|
433
433
|
- **开机自启**:管理台「设置」页有开关(对应配置项 `system.autoStart`)。开启后各平台的落地方式:
|
|
434
434
|
|
|
435
|
-
| 平台 | 写到哪里 |
|
|
436
|
-
| --- | --- |
|
|
437
|
-
| Windows | 启动文件夹 `DeskPet.
|
|
438
|
-
| macOS | `~/Library/LaunchAgents/DeskPet.plist` |
|
|
439
|
-
| Linux | `~/.config/autostart/DeskPet.desktop`(XDG 标准) |
|
|
440
|
-
|
|
441
|
-
注册的命令统一是 `bin/deskpet.mjs --no-open
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
435
|
+
| 平台 | 写到哪里 | 是否弹窗口 |
|
|
436
|
+
| --- | --- | --- |
|
|
437
|
+
| Windows | 启动文件夹 `DeskPet.vbs`(不依赖 reg / schtasks,受管控的机器也能用) | **不弹**(WScript 以隐藏窗口启动) |
|
|
438
|
+
| macOS | `~/Library/LaunchAgents/DeskPet.plist` | 不弹 |
|
|
439
|
+
| Linux | `~/.config/autostart/DeskPet.desktop`(XDG 标准) | 不弹 |
|
|
440
|
+
|
|
441
|
+
注册的命令统一是 `bin/deskpet.mjs --no-open --log`:
|
|
442
|
+
|
|
443
|
+
- `--no-open` 不在开机时弹浏览器
|
|
444
|
+
- `--log` 把服务输出转存到 **`~/.deskpet/logs/service.log`**(超过 2MB 自动重开)。
|
|
445
|
+
Windows 用隐藏窗口启动后看不到控制台,日志必须有地方落 —— 排查启动失败就看它
|
|
446
|
+
- 入口内部用 `process.execPath` 拉起服务,不依赖 PATH 里有没有 node
|
|
447
|
+
|
|
448
|
+
**想临时停用**:删掉对应的自启文件即可,或在设置页把开关关掉。
|
|
449
|
+
|
|
450
|
+
> 历史坑 1:本项目早期是 Electron 应用,自启是启动文件夹里的 `DeskPet.lnk`
|
|
451
|
+
> (指向 `pet\dist\DeskPet.exe`)。改成 Node 服务、目录改名后,那个快捷方式不会自动消失,
|
|
452
|
+
> 于是「以为自启开着,实际每次开机拉起的是旧程序」。开启自启时会**主动清理**同名的旧 `.lnk`。
|
|
453
|
+
>
|
|
454
|
+
> 历史坑 2:中间一版用 `.cmd` + `start /min`,任务栏里**一直挂着一个最小化的 node.exe 窗口**。
|
|
455
|
+
> 现在是 `.vbs`(`WScript.Shell.Run(..., 0, False)`,窗口样式 0 = 完全隐藏),
|
|
456
|
+
> 写入时同样会清掉旧的 `.cmd`。文件用 UTF-16LE + BOM 编码,路径含中文也不会被按 ANSI 解析坏。
|
package/bin/deskpet.mjs
CHANGED
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
* npx deskpet 启动,并自动打开浏览器
|
|
13
13
|
* npx deskpet --no-open 只启动、不开浏览器(SSH 远程 / 服务器场景)
|
|
14
14
|
* npx deskpet --port 3211 指定端口(等价于设置 DESKPET_PORT)
|
|
15
|
+
* npx deskpet --log 输出转存到 <数据目录>/logs/service.log(供无窗口自启使用)
|
|
15
16
|
* npx deskpet --check 只做环境自检,不启动服务
|
|
16
17
|
*/
|
|
17
18
|
import { spawn, spawnSync } from 'node:child_process'
|
|
18
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
|
|
19
|
+
import { createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from 'node:fs'
|
|
19
20
|
import net from 'node:net'
|
|
20
21
|
import { homedir } from 'node:os'
|
|
21
22
|
import { dirname, join } from 'node:path'
|
|
@@ -99,11 +100,19 @@ const flagValue = (name) => {
|
|
|
99
100
|
const cliPort = flagValue('--port') ?? process.env.DESKPET_PORT ?? null
|
|
100
101
|
const checkOnly = hasFlag('--check')
|
|
101
102
|
const shouldOpen = !hasFlag('--no-open')
|
|
103
|
+
/** 把服务输出转存到 <数据目录>/logs/service.log(自启时窗口是隐藏的,日志总得有地方看) */
|
|
104
|
+
const logToFile = hasFlag('--log')
|
|
102
105
|
|
|
103
106
|
function log(msg) {
|
|
104
107
|
process.stdout.write(`[deskpet] ${msg}\n`)
|
|
105
108
|
}
|
|
106
109
|
|
|
110
|
+
/** 数据根目录(与 server/utils/paths.ts 的规则保持一致) */
|
|
111
|
+
function dataRoot() {
|
|
112
|
+
const env = process.env.DESKPET_HOME?.trim()
|
|
113
|
+
return env || join(homedir(), '.deskpet')
|
|
114
|
+
}
|
|
115
|
+
|
|
107
116
|
/** 实际监听端口:命令行 > 环境变量 > 配置文件 > 默认 3210 */
|
|
108
117
|
function resolvePort() {
|
|
109
118
|
if (cliPort) {
|
|
@@ -112,7 +121,7 @@ function resolvePort() {
|
|
|
112
121
|
console.error(`[deskpet] 端口无效:${cliPort}`)
|
|
113
122
|
process.exit(1)
|
|
114
123
|
}
|
|
115
|
-
const home =
|
|
124
|
+
const home = dataRoot()
|
|
116
125
|
try {
|
|
117
126
|
const s = JSON.parse(readFileSync(join(home, 'config', 'settings.json'), 'utf-8'))
|
|
118
127
|
if (Number.isInteger(s?.server?.port) && s.server.port > 0) return s.server.port
|
|
@@ -187,12 +196,35 @@ if (checkOnly) {
|
|
|
187
196
|
|
|
188
197
|
// ── 2. 启动后端 ───────────────────────────────────────────
|
|
189
198
|
log(`启动服务(端口 ${port})…`)
|
|
199
|
+
|
|
200
|
+
/** 日志文件:超过 2MB 就重开一个,避免长期运行无限增长 */
|
|
201
|
+
function openLogStream() {
|
|
202
|
+
const file = join(dataRoot(), 'logs', 'service.log')
|
|
203
|
+
try {
|
|
204
|
+
mkdirSync(join(dataRoot(), 'logs'), { recursive: true })
|
|
205
|
+
const fresh = existsSync(file) && statSync(file).size < 2 * 1024 * 1024
|
|
206
|
+
const stream = createWriteStream(file, { flags: fresh ? 'a' : 'w' })
|
|
207
|
+
stream.write(`\n===== ${new Date().toISOString()} 启动(端口 ${port})=====\n`)
|
|
208
|
+
log(`日志文件:${file}`)
|
|
209
|
+
return stream
|
|
210
|
+
} catch (e) {
|
|
211
|
+
log(`日志文件不可写,输出将丢弃:${e?.message ?? e}`)
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const logStream = logToFile ? openLogStream() : null
|
|
190
217
|
const child = spawn(process.execPath, [...server.args, server.entry], {
|
|
191
218
|
cwd: pkgRoot,
|
|
192
|
-
stdio: 'inherit',
|
|
219
|
+
stdio: logStream ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
|
193
220
|
env: { ...process.env, DESKPET_PORT: String(port) }
|
|
194
221
|
})
|
|
195
222
|
|
|
223
|
+
if (logStream) {
|
|
224
|
+
child.stdout.pipe(logStream)
|
|
225
|
+
child.stderr.pipe(logStream)
|
|
226
|
+
}
|
|
227
|
+
|
|
196
228
|
process.on('SIGINT', () => child.kill('SIGINT'))
|
|
197
229
|
process.on('SIGTERM', () => child.kill('SIGTERM'))
|
|
198
230
|
child.on('exit', (code) => process.exit(code ?? 0))
|
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
* 开机自启
|
|
3
3
|
*
|
|
4
4
|
* 各平台实现:
|
|
5
|
-
* Windows 启动文件夹放一个 .
|
|
5
|
+
* Windows 启动文件夹放一个 .vbs —— 不依赖 reg / schtasks 命令,受管控的机器一样能用;
|
|
6
|
+
* 用 WScript 以隐藏窗口(样式 0)启动,**任务栏不会出现 node.exe 窗口**
|
|
6
7
|
* macOS ~/Library/LaunchAgents/<APP_ID>.plist
|
|
7
8
|
* Linux ~/.config/autostart/<APP_ID>.desktop(XDG 标准,GNOME/KDE 都认)
|
|
8
9
|
*
|
|
9
|
-
* 启动命令统一走 `bin/deskpet.mjs --no-open`,理由:
|
|
10
|
+
* 启动命令统一走 `bin/deskpet.mjs --no-open --log`,理由:
|
|
10
11
|
* · bin/ 在「源码运行」和「npm 包运行」两种形态下都存在(scripts/ 不进发布包)
|
|
11
|
-
* · 它内部用 process.execPath
|
|
12
|
+
* · 它内部用 process.execPath 拉起后端,不依赖 PATH 里有没有 node / npm
|
|
12
13
|
* · 脚本自己会按 package.json 推导根目录,所以不依赖工作目录
|
|
13
14
|
* · --no-open 避免每次开机都弹一个浏览器
|
|
15
|
+
* · --log 把输出转存到文件:窗口隐藏后,出问题时总得有地方看日志
|
|
14
16
|
*
|
|
15
17
|
* 历史坑(说明为什么要主动清理旧文件):
|
|
16
18
|
* 本项目早期是 Electron 应用,自启是安装包写在启动文件夹里的快捷方式
|
|
17
19
|
* (DeskPet.lnk → pet\dist\DeskPet.exe)。后来改成纯 Node 服务、目录也从 pet 换成 pet2.0,
|
|
18
20
|
* 那个旧快捷方式却没人清理 —— 结果用户以为自启开着,实际每次开机拉起的是旧程序,
|
|
19
|
-
*
|
|
21
|
+
* 新服务仍然要手动启动。再往后一版用 .cmd(start /min),会弹出一个最小化窗口。
|
|
22
|
+
* 因此下面写自启时会**顺手删掉同名的旧 .lnk 与 .cmd**,避免重复启动或误导排查。
|
|
20
23
|
*
|
|
21
24
|
* 仅在用户在设置中开启时写入;任何异常都只记录日志,不影响服务本身。
|
|
22
25
|
*/
|
|
@@ -45,28 +48,35 @@ function winStartupDir() {
|
|
|
45
48
|
const appData = process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
|
|
46
49
|
return join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
|
|
47
50
|
}
|
|
48
|
-
function
|
|
49
|
-
return join(winStartupDir(), `${APP_ID}.
|
|
51
|
+
function winVbsPath() {
|
|
52
|
+
return join(winStartupDir(), `${APP_ID}.vbs`);
|
|
50
53
|
}
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
/** 历史版本写过的自启文件:Electron 时代的快捷方式、上一版的批处理 */
|
|
55
|
+
function winLegacyPaths() {
|
|
56
|
+
return [join(winStartupDir(), `${APP_ID}.lnk`), join(winStartupDir(), `${APP_ID}.cmd`)];
|
|
53
57
|
}
|
|
54
58
|
/**
|
|
55
|
-
*
|
|
59
|
+
* 生成自启脚本(VBScript)。
|
|
56
60
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
+
* 为什么不是 .cmd:批处理启动的任何控制台程序都会**弹出一个窗口**,
|
|
62
|
+
* `start /min` 只能把它最小化 —— 任务栏里始终挂着一个 node.exe,很碍眼。
|
|
63
|
+
* 改用 WScript.Shell.Run 的第二参数(窗口样式)传 0,窗口**完全隐藏**:
|
|
64
|
+
* 0 = 隐藏窗口,False = 不等待其结束(否则 wscript 会一直挂着)
|
|
65
|
+
*
|
|
66
|
+
* 编码用 UTF-16LE 带 BOM:wscript 默认按系统 ANSI 代码页读 .vbs,
|
|
67
|
+
* 若安装路径含中文(例如用户名是中文)会乱码成乱路径;带 BOM 则按 Unicode 解析。
|
|
68
|
+
*
|
|
69
|
+
* 隐藏窗口后看不到服务日志,所以启动命令带 `--log`:输出转存到
|
|
70
|
+
* <数据目录>/logs/service.log(见 bin/deskpet.mjs)。
|
|
61
71
|
*/
|
|
62
|
-
function
|
|
72
|
+
function winVbsContent() {
|
|
63
73
|
const { node, entry } = launch();
|
|
64
74
|
return [
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
`
|
|
69
|
-
`
|
|
75
|
+
"' DeskPet autostart - generated by the DeskPet dashboard.",
|
|
76
|
+
"' Delete this file to disable autostart.",
|
|
77
|
+
'Set sh = CreateObject("WScript.Shell")',
|
|
78
|
+
`sh.CurrentDirectory = "${appRoot()}"`,
|
|
79
|
+
`sh.Run """${node}"" ""${entry}"" --no-open --log", 0, False`,
|
|
70
80
|
''
|
|
71
81
|
].join('\r\n');
|
|
72
82
|
}
|
|
@@ -120,21 +130,24 @@ export async function setAutoStart(on, opts = {}) {
|
|
|
120
130
|
if (process.platform === 'win32') {
|
|
121
131
|
const dir = winStartupDir();
|
|
122
132
|
mkdirSync(dir, { recursive: true });
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
// 清掉历史遗留:Electron 时代的 .lnk(指向已不存在的 pet\dist\DeskPet.exe)
|
|
134
|
+
// 与上一版的 .cmd(会弹窗口)。留着只会重复启动或误导排查。
|
|
135
|
+
for (const legacy of winLegacyPaths()) {
|
|
136
|
+
if (!existsSync(legacy))
|
|
137
|
+
continue;
|
|
126
138
|
try {
|
|
127
139
|
unlinkSync(legacy);
|
|
128
|
-
console.log(`[autostart]
|
|
140
|
+
console.log(`[autostart] 已清理旧版自启文件: ${legacy}`);
|
|
129
141
|
}
|
|
130
142
|
catch (err) {
|
|
131
|
-
console.warn(`[autostart]
|
|
143
|
+
console.warn(`[autostart] 旧自启文件清理失败(可手动删除): ${String(err)}`);
|
|
132
144
|
}
|
|
133
145
|
}
|
|
134
|
-
const file =
|
|
146
|
+
const file = winVbsPath();
|
|
135
147
|
if (on) {
|
|
136
|
-
|
|
137
|
-
|
|
148
|
+
// utf16le + BOM:见 winVbsContent() 的说明(路径含中文时不会被 ANSI 解析搞坏)
|
|
149
|
+
writeFileSync(file, Buffer.from(`\ufeff${winVbsContent()}`, 'utf16le'));
|
|
150
|
+
console.log(`[autostart] 开机自启已开启(静默,无窗口): ${file}`);
|
|
138
151
|
}
|
|
139
152
|
else if (removeWhenOff && existsSync(file)) {
|
|
140
153
|
unlinkSync(file);
|
|
@@ -179,8 +192,10 @@ export async function setAutoStart(on, opts = {}) {
|
|
|
179
192
|
/** 查询当前是否已设置自启 */
|
|
180
193
|
export function autoStartEnabled() {
|
|
181
194
|
try {
|
|
182
|
-
if (process.platform === 'win32')
|
|
183
|
-
|
|
195
|
+
if (process.platform === 'win32') {
|
|
196
|
+
// .cmd 是上一版留下的形式,也算「已开启」,直到下次写入时被替换
|
|
197
|
+
return winLegacyPaths().some((p) => p.endsWith('.cmd') && existsSync(p)) || existsSync(winVbsPath());
|
|
198
|
+
}
|
|
184
199
|
if (process.platform === 'darwin')
|
|
185
200
|
return existsSync(macPlistPath());
|
|
186
201
|
if (process.platform === 'linux')
|
package/package.json
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
* 开机自启
|
|
3
3
|
*
|
|
4
4
|
* 各平台实现:
|
|
5
|
-
* Windows 启动文件夹放一个 .
|
|
5
|
+
* Windows 启动文件夹放一个 .vbs —— 不依赖 reg / schtasks 命令,受管控的机器一样能用;
|
|
6
|
+
* 用 WScript 以隐藏窗口(样式 0)启动,**任务栏不会出现 node.exe 窗口**
|
|
6
7
|
* macOS ~/Library/LaunchAgents/<APP_ID>.plist
|
|
7
8
|
* Linux ~/.config/autostart/<APP_ID>.desktop(XDG 标准,GNOME/KDE 都认)
|
|
8
9
|
*
|
|
9
|
-
* 启动命令统一走 `bin/deskpet.mjs --no-open`,理由:
|
|
10
|
+
* 启动命令统一走 `bin/deskpet.mjs --no-open --log`,理由:
|
|
10
11
|
* · bin/ 在「源码运行」和「npm 包运行」两种形态下都存在(scripts/ 不进发布包)
|
|
11
|
-
* · 它内部用 process.execPath
|
|
12
|
+
* · 它内部用 process.execPath 拉起后端,不依赖 PATH 里有没有 node / npm
|
|
12
13
|
* · 脚本自己会按 package.json 推导根目录,所以不依赖工作目录
|
|
13
14
|
* · --no-open 避免每次开机都弹一个浏览器
|
|
15
|
+
* · --log 把输出转存到文件:窗口隐藏后,出问题时总得有地方看日志
|
|
14
16
|
*
|
|
15
17
|
* 历史坑(说明为什么要主动清理旧文件):
|
|
16
18
|
* 本项目早期是 Electron 应用,自启是安装包写在启动文件夹里的快捷方式
|
|
17
19
|
* (DeskPet.lnk → pet\dist\DeskPet.exe)。后来改成纯 Node 服务、目录也从 pet 换成 pet2.0,
|
|
18
20
|
* 那个旧快捷方式却没人清理 —— 结果用户以为自启开着,实际每次开机拉起的是旧程序,
|
|
19
|
-
*
|
|
21
|
+
* 新服务仍然要手动启动。再往后一版用 .cmd(start /min),会弹出一个最小化窗口。
|
|
22
|
+
* 因此下面写自启时会**顺手删掉同名的旧 .lnk 与 .cmd**,避免重复启动或误导排查。
|
|
20
23
|
*
|
|
21
24
|
* 仅在用户在设置中开启时写入;任何异常都只记录日志,不影响服务本身。
|
|
22
25
|
*/
|
|
@@ -51,30 +54,37 @@ function winStartupDir(): string {
|
|
|
51
54
|
return join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
function
|
|
55
|
-
return join(winStartupDir(), `${APP_ID}.
|
|
57
|
+
function winVbsPath(): string {
|
|
58
|
+
return join(winStartupDir(), `${APP_ID}.vbs`)
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
/** 历史版本写过的自启文件:Electron 时代的快捷方式、上一版的批处理 */
|
|
62
|
+
function winLegacyPaths(): string[] {
|
|
63
|
+
return [join(winStartupDir(), `${APP_ID}.lnk`), join(winStartupDir(), `${APP_ID}.cmd`)]
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
/**
|
|
63
|
-
*
|
|
67
|
+
* 生成自启脚本(VBScript)。
|
|
64
68
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
+
* 为什么不是 .cmd:批处理启动的任何控制台程序都会**弹出一个窗口**,
|
|
70
|
+
* `start /min` 只能把它最小化 —— 任务栏里始终挂着一个 node.exe,很碍眼。
|
|
71
|
+
* 改用 WScript.Shell.Run 的第二参数(窗口样式)传 0,窗口**完全隐藏**:
|
|
72
|
+
* 0 = 隐藏窗口,False = 不等待其结束(否则 wscript 会一直挂着)
|
|
73
|
+
*
|
|
74
|
+
* 编码用 UTF-16LE 带 BOM:wscript 默认按系统 ANSI 代码页读 .vbs,
|
|
75
|
+
* 若安装路径含中文(例如用户名是中文)会乱码成乱路径;带 BOM 则按 Unicode 解析。
|
|
76
|
+
*
|
|
77
|
+
* 隐藏窗口后看不到服务日志,所以启动命令带 `--log`:输出转存到
|
|
78
|
+
* <数据目录>/logs/service.log(见 bin/deskpet.mjs)。
|
|
69
79
|
*/
|
|
70
|
-
function
|
|
80
|
+
function winVbsContent(): string {
|
|
71
81
|
const { node, entry } = launch()
|
|
72
82
|
return [
|
|
73
|
-
'
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
`
|
|
77
|
-
`
|
|
83
|
+
"' DeskPet autostart - generated by the DeskPet dashboard.",
|
|
84
|
+
"' Delete this file to disable autostart.",
|
|
85
|
+
'Set sh = CreateObject("WScript.Shell")',
|
|
86
|
+
`sh.CurrentDirectory = "${appRoot()}"`,
|
|
87
|
+
`sh.Run """${node}"" ""${entry}"" --no-open --log", 0, False`,
|
|
78
88
|
''
|
|
79
89
|
].join('\r\n')
|
|
80
90
|
}
|
|
@@ -151,21 +161,23 @@ export async function setAutoStart(on: boolean, opts: AutoStartOptions = {}): Pr
|
|
|
151
161
|
const dir = winStartupDir()
|
|
152
162
|
mkdirSync(dir, { recursive: true })
|
|
153
163
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
// 清掉历史遗留:Electron 时代的 .lnk(指向已不存在的 pet\dist\DeskPet.exe)
|
|
165
|
+
// 与上一版的 .cmd(会弹窗口)。留着只会重复启动或误导排查。
|
|
166
|
+
for (const legacy of winLegacyPaths()) {
|
|
167
|
+
if (!existsSync(legacy)) continue
|
|
157
168
|
try {
|
|
158
169
|
unlinkSync(legacy)
|
|
159
|
-
console.log(`[autostart]
|
|
170
|
+
console.log(`[autostart] 已清理旧版自启文件: ${legacy}`)
|
|
160
171
|
} catch (err) {
|
|
161
|
-
console.warn(`[autostart]
|
|
172
|
+
console.warn(`[autostart] 旧自启文件清理失败(可手动删除): ${String(err)}`)
|
|
162
173
|
}
|
|
163
174
|
}
|
|
164
175
|
|
|
165
|
-
const file =
|
|
176
|
+
const file = winVbsPath()
|
|
166
177
|
if (on) {
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
// utf16le + BOM:见 winVbsContent() 的说明(路径含中文时不会被 ANSI 解析搞坏)
|
|
179
|
+
writeFileSync(file, Buffer.from(`\ufeff${winVbsContent()}`, 'utf16le'))
|
|
180
|
+
console.log(`[autostart] 开机自启已开启(静默,无窗口): ${file}`)
|
|
169
181
|
} else if (removeWhenOff && existsSync(file)) {
|
|
170
182
|
unlinkSync(file)
|
|
171
183
|
console.log('[autostart] 开机自启已关闭')
|
|
@@ -210,7 +222,10 @@ export async function setAutoStart(on: boolean, opts: AutoStartOptions = {}): Pr
|
|
|
210
222
|
/** 查询当前是否已设置自启 */
|
|
211
223
|
export function autoStartEnabled(): boolean {
|
|
212
224
|
try {
|
|
213
|
-
if (process.platform === 'win32')
|
|
225
|
+
if (process.platform === 'win32') {
|
|
226
|
+
// .cmd 是上一版留下的形式,也算「已开启」,直到下次写入时被替换
|
|
227
|
+
return winLegacyPaths().some((p) => p.endsWith('.cmd') && existsSync(p)) || existsSync(winVbsPath())
|
|
228
|
+
}
|
|
214
229
|
if (process.platform === 'darwin') return existsSync(macPlistPath())
|
|
215
230
|
if (process.platform === 'linux') return existsSync(linuxDesktopPath())
|
|
216
231
|
} catch {
|