@wanghaopeng1148/deskpet 2.0.0 → 2.0.2
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 +70 -16
- package/bin/deskpet.mjs +70 -2
- package/dist/node/server/db/database.js +113 -0
- package/dist/node/server/db/migrate-legacy.js +88 -0
- package/dist/node/server/db/task-repository.js +374 -0
- package/dist/node/server/http/http-server.js +493 -0
- package/dist/node/server/http/ws-hub.js +59 -0
- package/dist/node/server/main.js +291 -0
- package/dist/node/server/plugins/actions/builtin.js +67 -0
- package/dist/node/server/plugins/actions/clipboard-watch.js +27 -0
- package/dist/node/server/plugins/actions/http-request.js +41 -0
- package/dist/node/server/plugins/actions/jenkins-build.js +183 -0
- package/dist/node/server/plugins/actions/open-app.js +41 -0
- package/dist/node/server/plugins/actions/python-script.js +180 -0
- package/dist/node/server/plugins/actions/screenshot.js +38 -0
- package/dist/node/server/plugins/actions/send-keystroke.js +100 -0
- package/dist/node/server/plugins/actions/show-reminder.js +7 -0
- package/dist/node/server/plugins/actions/ssh-command.js +123 -0
- package/dist/node/server/plugins/actions/task-chain.js +24 -0
- package/dist/node/server/plugins/actions/volume-control.js +31 -0
- package/dist/node/server/plugins/index.js +35 -0
- package/dist/node/server/plugins/registry.js +23 -0
- package/dist/node/server/services/clipboard-watcher.js +112 -0
- package/dist/node/server/services/config-store.js +141 -0
- package/dist/node/server/services/idle-monitor.js +131 -0
- package/dist/node/server/services/notifier.js +36 -0
- package/dist/node/server/services/quick-actions-store.js +52 -0
- package/dist/node/server/services/remote-connector.js +67 -0
- package/dist/node/server/services/scanner-reader.js +217 -0
- package/dist/node/server/services/script-runner.js +228 -0
- package/dist/node/server/services/snapshot-service.js +135 -0
- package/dist/node/server/services/task-scheduler.js +813 -0
- package/dist/node/server/services/wechat-bot.js +635 -0
- package/dist/node/server/services/wechat-command-types.js +1 -0
- package/dist/node/server/services/wechat-commands.js +330 -0
- package/dist/node/server/suppress-warnings.js +12 -0
- package/dist/node/server/utils/asset-url.js +26 -0
- package/dist/node/server/utils/auto-start.js +186 -0
- package/dist/node/server/utils/clipboard.js +50 -0
- package/dist/node/server/utils/dashboard-url.js +8 -0
- package/dist/node/server/utils/instance-guard.js +165 -0
- package/dist/node/server/utils/native-notify.js +53 -0
- package/dist/node/server/utils/open.js +37 -0
- package/dist/node/server/utils/paths.js +95 -0
- package/dist/node/server/utils/python-interpreter.js +129 -0
- package/dist/node/shared/animation-engine.js +349 -0
- package/dist/node/shared/chain-condition.js +39 -0
- package/dist/node/shared/cron-weekly.js +124 -0
- package/dist/node/shared/py-task-params.js +335 -0
- package/dist/node/shared/types.js +69 -0
- package/package.json +6 -2
- package/server/http/http-server.ts +4 -1
- package/server/utils/auto-start.ts +158 -49
- package/server/utils/paths.ts +28 -1
|
@@ -1,55 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 开机自启
|
|
3
|
-
* Windows: HKCU\...\Run 注册表项
|
|
4
|
-
* macOS : ~/Library/LaunchAgents plist
|
|
5
|
-
* Linux : 暂不支持
|
|
2
|
+
* 开机自启
|
|
6
3
|
*
|
|
7
|
-
*
|
|
4
|
+
* 各平台实现:
|
|
5
|
+
* Windows 启动文件夹放一个 .cmd —— 不依赖 reg / schtasks 命令,受管控的机器一样能用
|
|
6
|
+
* macOS ~/Library/LaunchAgents/<APP_ID>.plist
|
|
7
|
+
* Linux ~/.config/autostart/<APP_ID>.desktop(XDG 标准,GNOME/KDE 都认)
|
|
8
|
+
*
|
|
9
|
+
* 启动命令统一走 `bin/deskpet.mjs --no-open`,理由:
|
|
10
|
+
* · bin/ 在「源码运行」和「npm 包运行」两种形态下都存在(scripts/ 不进发布包)
|
|
11
|
+
* · 它内部用 process.execPath 拉起 server/main.ts,不依赖 PATH 里有没有 node / npm
|
|
12
|
+
* · 脚本自己会按 package.json 推导根目录,所以不依赖工作目录
|
|
13
|
+
* · --no-open 避免每次开机都弹一个浏览器
|
|
14
|
+
*
|
|
15
|
+
* 历史坑(说明为什么要主动清理旧文件):
|
|
16
|
+
* 本项目早期是 Electron 应用,自启是安装包写在启动文件夹里的快捷方式
|
|
17
|
+
* (DeskPet.lnk → pet\dist\DeskPet.exe)。后来改成纯 Node 服务、目录也从 pet 换成 pet2.0,
|
|
18
|
+
* 那个旧快捷方式却没人清理 —— 结果用户以为自启开着,实际每次开机拉起的是旧程序,
|
|
19
|
+
* 新服务仍然要手动启动。因此下面在写入自启时会**顺手删掉同名的旧 .lnk**。
|
|
20
|
+
*
|
|
21
|
+
* 仅在用户在设置中开启时写入;任何异常都只记录日志,不影响服务本身。
|
|
8
22
|
*/
|
|
9
|
-
import { execFile } from 'node:child_process'
|
|
10
23
|
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
11
24
|
import { homedir } from 'node:os'
|
|
12
|
-
import { join } from 'node:path'
|
|
25
|
+
import { dirname, join, resolve } from 'node:path'
|
|
13
26
|
import { fileURLToPath } from 'node:url'
|
|
14
27
|
|
|
15
28
|
const APP_ID = 'DeskPet'
|
|
16
29
|
|
|
17
|
-
/**
|
|
18
|
-
function
|
|
19
|
-
return fileURLToPath(import.meta.url)
|
|
30
|
+
/** 应用根目录(源码运行为项目根,npm 包运行为包根) */
|
|
31
|
+
function appRoot(): string {
|
|
32
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
|
|
20
33
|
}
|
|
21
34
|
|
|
22
|
-
/**
|
|
23
|
-
function
|
|
24
|
-
return
|
|
35
|
+
/** 开机要执行的 node 可执行文件与入口脚本 */
|
|
36
|
+
function launch(): { node: string; entry: string } {
|
|
37
|
+
return { node: process.execPath, entry: join(appRoot(), 'bin', 'deskpet.mjs') }
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
if (process.platform === 'win32') {
|
|
30
|
-
const args = on
|
|
31
|
-
? ['add', 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', '/v', APP_ID, '/t', 'REG_SZ', '/d', startCommand(), '/f']
|
|
32
|
-
: ['delete', 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', '/v', APP_ID, '/f']
|
|
33
|
-
await new Promise<void>((resolve) => {
|
|
34
|
-
execFile('reg', args, { windowsHide: true }, () => resolve())
|
|
35
|
-
})
|
|
36
|
-
return true
|
|
37
|
-
}
|
|
40
|
+
// ── Windows ─────────────────────────────────────────────────
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
function winStartupDir(): string {
|
|
43
|
+
const appData = process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming')
|
|
44
|
+
return join(appData, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function winCmdPath(): string {
|
|
48
|
+
return join(winStartupDir(), `${APP_ID}.cmd`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function winLegacyLnkPath(): string {
|
|
52
|
+
return join(winStartupDir(), `${APP_ID}.lnk`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 生成自启批处理。
|
|
57
|
+
*
|
|
58
|
+
* 注意两点:
|
|
59
|
+
* · 换行必须是 CRLF,注释用英文 —— cmd 按系统 ANSI 代码页解析批处理,
|
|
60
|
+
* 写中文注释在中文 Windows 上容易乱码(虽然注释不影响执行,但很难看)
|
|
61
|
+
* · 用 `start /min` 让服务窗口最小化,不打扰用户,同时任务栏里能看到它在跑
|
|
62
|
+
*/
|
|
63
|
+
function winCmdContent(): string {
|
|
64
|
+
const { node, entry } = launch()
|
|
65
|
+
return [
|
|
66
|
+
'@echo off',
|
|
67
|
+
'rem DeskPet autostart - generated by the DeskPet dashboard.',
|
|
68
|
+
'rem Delete this file to disable autostart.',
|
|
69
|
+
`cd /d "${appRoot()}"`,
|
|
70
|
+
`start "${APP_ID}" /min "${node}" "${entry}" --no-open`,
|
|
71
|
+
''
|
|
72
|
+
].join('\r\n')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── macOS ───────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
function macPlistPath(): string {
|
|
78
|
+
return join(homedir(), 'Library', 'LaunchAgents', `${APP_ID}.plist`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function macPlistContent(): string {
|
|
82
|
+
const { node, entry } = launch()
|
|
83
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
53
84
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
54
85
|
<plist version="1.0">
|
|
55
86
|
<dict>
|
|
@@ -57,32 +88,110 @@ export async function setAutoStart(on: boolean): Promise<boolean> {
|
|
|
57
88
|
<string>${APP_ID}</string>
|
|
58
89
|
<key>ProgramArguments</key>
|
|
59
90
|
<array>
|
|
60
|
-
<string>${
|
|
61
|
-
|
|
91
|
+
<string>${node}</string>
|
|
92
|
+
<string>${entry}</string>
|
|
93
|
+
<string>--no-open</string>
|
|
62
94
|
</array>
|
|
95
|
+
<key>WorkingDirectory</key>
|
|
96
|
+
<string>${appRoot()}</string>
|
|
63
97
|
<key>RunAtLoad</key>
|
|
64
98
|
<true/>
|
|
65
99
|
</dict>
|
|
66
100
|
</plist>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
101
|
+
`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Linux (XDG autostart) ───────────────────────────────────
|
|
105
|
+
|
|
106
|
+
function linuxDesktopPath(): string {
|
|
107
|
+
return join(homedir(), '.config', 'autostart', `${APP_ID}.desktop`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function linuxDesktopContent(): string {
|
|
111
|
+
const { node, entry } = launch()
|
|
112
|
+
return [
|
|
113
|
+
'[Desktop Entry]',
|
|
114
|
+
'Type=Application',
|
|
115
|
+
`Name=${APP_ID}`,
|
|
116
|
+
'Comment=DeskPet 本机任务自动化服务',
|
|
117
|
+
`Exec="${node}" "${entry}" --no-open`,
|
|
118
|
+
`Path=${appRoot()}`,
|
|
119
|
+
'X-GNOME-Autostart-enabled=true',
|
|
120
|
+
''
|
|
121
|
+
].join('\n')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── 对外接口 ────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
/** 开启 / 关闭开机自启,返回是否成功(失败原因会打印到日志) */
|
|
127
|
+
export async function setAutoStart(on: boolean): Promise<boolean> {
|
|
128
|
+
try {
|
|
129
|
+
if (process.platform === 'win32') {
|
|
130
|
+
const dir = winStartupDir()
|
|
131
|
+
mkdirSync(dir, { recursive: true })
|
|
132
|
+
|
|
133
|
+
// 清掉 Electron 时代遗留的失效快捷方式(它指向 pet\dist\DeskPet.exe)
|
|
134
|
+
const legacy = winLegacyLnkPath()
|
|
135
|
+
if (existsSync(legacy)) {
|
|
136
|
+
try {
|
|
137
|
+
unlinkSync(legacy)
|
|
138
|
+
console.log(`[autostart] 已清理旧版自启快捷方式: ${legacy}`)
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.warn(`[autostart] 旧快捷方式清理失败(可手动删除): ${String(err)}`)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const file = winCmdPath()
|
|
145
|
+
if (on) {
|
|
146
|
+
writeFileSync(file, winCmdContent(), 'utf-8')
|
|
147
|
+
console.log(`[autostart] 开机自启已开启: ${file}`)
|
|
148
|
+
} else if (existsSync(file)) {
|
|
149
|
+
unlinkSync(file)
|
|
150
|
+
console.log('[autostart] 开机自启已关闭')
|
|
151
|
+
}
|
|
70
152
|
return true
|
|
71
153
|
}
|
|
154
|
+
|
|
155
|
+
if (process.platform === 'darwin') {
|
|
156
|
+
const file = macPlistPath()
|
|
157
|
+
if (on) {
|
|
158
|
+
mkdirSync(dirname(file), { recursive: true })
|
|
159
|
+
writeFileSync(file, macPlistContent(), 'utf-8')
|
|
160
|
+
console.log(`[autostart] 开机自启已开启: ${file}`)
|
|
161
|
+
} else if (existsSync(file)) {
|
|
162
|
+
unlinkSync(file)
|
|
163
|
+
console.log('[autostart] 开机自启已关闭')
|
|
164
|
+
}
|
|
165
|
+
return true
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (process.platform === 'linux') {
|
|
169
|
+
const file = linuxDesktopPath()
|
|
170
|
+
if (on) {
|
|
171
|
+
mkdirSync(dirname(file), { recursive: true })
|
|
172
|
+
writeFileSync(file, linuxDesktopContent(), 'utf-8')
|
|
173
|
+
console.log(`[autostart] 开机自启已开启: ${file}`)
|
|
174
|
+
} else if (existsSync(file)) {
|
|
175
|
+
unlinkSync(file)
|
|
176
|
+
console.log('[autostart] 开机自启已关闭')
|
|
177
|
+
}
|
|
178
|
+
return true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.warn(`[autostart] 暂不支持的平台: ${process.platform}`)
|
|
182
|
+
return false
|
|
72
183
|
} catch (err) {
|
|
73
184
|
console.error('[autostart] 设置失败:', err)
|
|
74
185
|
return false
|
|
75
186
|
}
|
|
76
|
-
console.warn(`[autostart] 暂不支持的平台: ${process.platform}`)
|
|
77
|
-
return false
|
|
78
187
|
}
|
|
79
188
|
|
|
80
|
-
/**
|
|
189
|
+
/** 查询当前是否已设置自启 */
|
|
81
190
|
export function autoStartEnabled(): boolean {
|
|
82
191
|
try {
|
|
83
|
-
if (process.platform === '
|
|
84
|
-
|
|
85
|
-
|
|
192
|
+
if (process.platform === 'win32') return existsSync(winCmdPath())
|
|
193
|
+
if (process.platform === 'darwin') return existsSync(macPlistPath())
|
|
194
|
+
if (process.platform === 'linux') return existsSync(linuxDesktopPath())
|
|
86
195
|
} catch {
|
|
87
196
|
/* ignore */
|
|
88
197
|
}
|
package/server/utils/paths.ts
CHANGED
|
@@ -23,6 +23,33 @@ export function moduleDir(): string {
|
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = moduleDir()
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* 包根目录(含 package.json 的那一层)。
|
|
28
|
+
*
|
|
29
|
+
* 为什么不写死相对层级:源码运行时本模块在 `server/utils/`,
|
|
30
|
+
* 而发布产物在 `dist/node/server/utils/` —— 两者距包根的层数不同,
|
|
31
|
+
* 任何 `../../xxx` 形式的假设都会在编译后失效(曾导致 resources 与 dist/web 定位不到)。
|
|
32
|
+
* 这里改为从模块目录向上查找 package.json,源码形态与产物形态都能正确定位。
|
|
33
|
+
*/
|
|
34
|
+
function findPackageRoot(): string {
|
|
35
|
+
let dir = MODULE_DIR
|
|
36
|
+
for (let i = 0; i < 8; i += 1) {
|
|
37
|
+
if (existsSync(join(dir, 'package.json'))) return dir
|
|
38
|
+
const parent = dirname(dir)
|
|
39
|
+
if (parent === dir) break
|
|
40
|
+
dir = parent
|
|
41
|
+
}
|
|
42
|
+
// 兜底:沿用源码形态的假设(server/utils → 上两级即包根)
|
|
43
|
+
return resolve(MODULE_DIR, '../..')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const PACKAGE_ROOT = findPackageRoot()
|
|
47
|
+
|
|
48
|
+
/** 包根目录 */
|
|
49
|
+
export function getPackageRoot(): string {
|
|
50
|
+
return PACKAGE_ROOT
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
/** 数据根目录(配置/数据库/皮肤/快照) */
|
|
27
54
|
export function getDataRoot(): string {
|
|
28
55
|
const env = process.env.DESKPET_HOME
|
|
@@ -34,7 +61,7 @@ export function getDataRoot(): string {
|
|
|
34
61
|
export function getResourcesRoot(): string {
|
|
35
62
|
const env = process.env.DESKPET_RESOURCES
|
|
36
63
|
if (env && env.trim()) return resolve(env.trim())
|
|
37
|
-
return
|
|
64
|
+
return join(PACKAGE_ROOT, 'resources')
|
|
38
65
|
}
|
|
39
66
|
|
|
40
67
|
/** 用户配置目录 */
|