@myassis/gateway 1.0.22 → 1.0.24
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/dist/main.js +1 -0
- package/dist/services/ServiceManager.js +77 -31
- package/package.json +4 -3
package/dist/main.js
CHANGED
|
@@ -9,11 +9,19 @@ const util_1 = require("util");
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const shared_1 = require("@myassis/shared");
|
|
13
|
+
const os_1 = __importDefault(require("os"));
|
|
14
|
+
const logger = (0, shared_1.getLogger)('ServiceManager');
|
|
12
15
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
13
16
|
exports.SERVICE_NAME = 'myassis-gateway';
|
|
14
17
|
exports.SERVICE_DISPLAY_NAME = '我的助手 Desktop Gateway Service';
|
|
15
18
|
// ─── 平台无关工具 ────────────────────────────────────────────
|
|
19
|
+
function isPackagedExe() {
|
|
20
|
+
return !!process.pkg;
|
|
21
|
+
}
|
|
16
22
|
function getServiceScript() {
|
|
23
|
+
if (isPackagedExe())
|
|
24
|
+
return '';
|
|
17
25
|
// npm install -g 后,用当前模块位置定位包目录
|
|
18
26
|
const pkgRoot = path_1.default.resolve(__dirname, '..', '..');
|
|
19
27
|
return path_1.default.join(pkgRoot, 'dist', 'index.js');
|
|
@@ -21,10 +29,34 @@ function getServiceScript() {
|
|
|
21
29
|
function getNodeExec() {
|
|
22
30
|
return process.execPath;
|
|
23
31
|
}
|
|
32
|
+
function getNssmPath() {
|
|
33
|
+
// 1. 优先:exe 同目录下有 nssm.exe
|
|
34
|
+
const alongside = path_1.default.join(path_1.default.dirname(process.execPath), 'nssm.exe');
|
|
35
|
+
if (fs_1.default.existsSync(alongside))
|
|
36
|
+
return alongside;
|
|
37
|
+
// 2. pkg 运行时从包内虚拟文件系统提取 nssm.exe 到临时目录
|
|
38
|
+
if (isPackagedExe()) {
|
|
39
|
+
const tmpPath = path_1.default.join(os_1.default.tmpdir(), 'myassis-nssm.exe');
|
|
40
|
+
if (!fs_1.default.existsSync(tmpPath)) {
|
|
41
|
+
try {
|
|
42
|
+
const bundledPath = path_1.default.join(__dirname, '..', '..', 'nssm.exe');
|
|
43
|
+
const data = fs_1.default.readFileSync(bundledPath);
|
|
44
|
+
fs_1.default.writeFileSync(tmpPath, data);
|
|
45
|
+
logger.info(`Extracted nssm.exe to ${tmpPath}`);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
logger.warn(`Failed to extract nssm.exe: ${e.message}`);
|
|
49
|
+
return '';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return tmpPath;
|
|
53
|
+
}
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
24
56
|
// ─── Windows 实现 ───────────────────────────────────────────
|
|
25
57
|
async function queryServiceWindows() {
|
|
26
58
|
try {
|
|
27
|
-
const { stdout } = await execAsync(`sc query ${exports.SERVICE_NAME}`, { timeout: 5000 });
|
|
59
|
+
const { stdout } = await execAsync(`sc.exe query ${exports.SERVICE_NAME}`, { timeout: 5000 });
|
|
28
60
|
return { installed: true, running: stdout.includes('RUNNING') };
|
|
29
61
|
}
|
|
30
62
|
catch {
|
|
@@ -32,23 +64,35 @@ async function queryServiceWindows() {
|
|
|
32
64
|
}
|
|
33
65
|
}
|
|
34
66
|
async function installWindows() {
|
|
67
|
+
const isPkg = isPackagedExe();
|
|
35
68
|
const script = getServiceScript();
|
|
36
|
-
const
|
|
37
|
-
const
|
|
69
|
+
const exe = getNodeExec();
|
|
70
|
+
const workDir = isPkg ? path_1.default.dirname(exe) : path_1.default.dirname(script);
|
|
71
|
+
logger.info(`isPkg=${isPkg}, exe=${exe}, script=${script}, workDir=${workDir}`);
|
|
38
72
|
const { installed } = await queryServiceWindows();
|
|
39
73
|
if (installed)
|
|
40
|
-
await
|
|
41
|
-
const nssmPath =
|
|
42
|
-
const useNssm = fs_1.default.existsSync(nssmPath);
|
|
74
|
+
await uninstallWindows();
|
|
75
|
+
const nssmPath = getNssmPath();
|
|
76
|
+
const useNssm = !!nssmPath && fs_1.default.existsSync(nssmPath);
|
|
43
77
|
try {
|
|
44
78
|
if (useNssm) {
|
|
45
|
-
|
|
79
|
+
if (isPkg) {
|
|
80
|
+
await execAsync(`"${nssmPath}" install ${exports.SERVICE_NAME} "${exe}"`, { timeout: 15000, cwd: workDir });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await execAsync(`"${nssmPath}" install ${exports.SERVICE_NAME} "${exe}" "${script}"`, { timeout: 15000, cwd: workDir });
|
|
84
|
+
}
|
|
46
85
|
await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} AppDirectory "${workDir}"`, { timeout: 10000 });
|
|
47
86
|
await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} DisplayName "${exports.SERVICE_DISPLAY_NAME}"`, { timeout: 10000 });
|
|
48
87
|
await execAsync(`"${nssmPath}" set ${exports.SERVICE_NAME} Start SERVICE_AUTO_START`, { timeout: 10000 });
|
|
49
88
|
}
|
|
50
89
|
else {
|
|
51
|
-
|
|
90
|
+
if (isPkg) {
|
|
91
|
+
await execAsync(`sc.exe create ${exports.SERVICE_NAME} binPath= "\\"${exe}\\"" DisplayName= "${exports.SERVICE_DISPLAY_NAME}" start= auto`, { timeout: 15000 });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
await execAsync(`sc.exe create ${exports.SERVICE_NAME} binPath= "\\"${exe}\\" \\"${script}\\"" DisplayName= "${exports.SERVICE_DISPLAY_NAME}" start= auto`, { timeout: 15000 });
|
|
95
|
+
}
|
|
52
96
|
}
|
|
53
97
|
await startService();
|
|
54
98
|
return { success: true, message: '服务安装并启动成功' };
|
|
@@ -60,12 +104,12 @@ async function installWindows() {
|
|
|
60
104
|
async function uninstallWindows() {
|
|
61
105
|
try {
|
|
62
106
|
await stopService();
|
|
63
|
-
const nssmPath =
|
|
64
|
-
if (fs_1.default.existsSync(nssmPath)) {
|
|
107
|
+
const nssmPath = getNssmPath();
|
|
108
|
+
if (nssmPath && fs_1.default.existsSync(nssmPath)) {
|
|
65
109
|
await execAsync(`"${nssmPath}" remove ${exports.SERVICE_NAME} confirm`, { timeout: 10000 });
|
|
66
110
|
}
|
|
67
111
|
else {
|
|
68
|
-
await execAsync(`sc delete ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
112
|
+
await execAsync(`sc.exe delete ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
69
113
|
}
|
|
70
114
|
return { success: true, message: '服务卸载成功' };
|
|
71
115
|
}
|
|
@@ -75,7 +119,7 @@ async function uninstallWindows() {
|
|
|
75
119
|
}
|
|
76
120
|
async function startServiceWindows() {
|
|
77
121
|
try {
|
|
78
|
-
await execAsync(`sc start ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
122
|
+
await execAsync(`sc.exe start ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
79
123
|
return { success: true, message: '服务启动成功' };
|
|
80
124
|
}
|
|
81
125
|
catch (err) {
|
|
@@ -83,7 +127,7 @@ async function startServiceWindows() {
|
|
|
83
127
|
}
|
|
84
128
|
}
|
|
85
129
|
async function restartServiceWindows() {
|
|
86
|
-
const nssmPath =
|
|
130
|
+
const nssmPath = getNssmPath();
|
|
87
131
|
// 无 nssm 时需要用户手动更新
|
|
88
132
|
if (!fs_1.default.existsSync(nssmPath)) {
|
|
89
133
|
const info = await getServiceInfo();
|
|
@@ -109,7 +153,7 @@ async function restartServiceWindows() {
|
|
|
109
153
|
}
|
|
110
154
|
async function stopServiceWindows() {
|
|
111
155
|
try {
|
|
112
|
-
await execAsync(`sc stop ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
156
|
+
await execAsync(`sc.exe stop ${exports.SERVICE_NAME}`, { timeout: 10000 });
|
|
113
157
|
return { success: true, message: '服务停止成功' };
|
|
114
158
|
}
|
|
115
159
|
catch (err) {
|
|
@@ -136,28 +180,30 @@ async function queryServiceLinux() {
|
|
|
136
180
|
}
|
|
137
181
|
}
|
|
138
182
|
async function installLinux() {
|
|
183
|
+
const isPkg = isPackagedExe();
|
|
139
184
|
const script = getServiceScript();
|
|
140
|
-
const
|
|
141
|
-
const
|
|
185
|
+
const exe = getNodeExec();
|
|
186
|
+
const workDir = isPkg ? path_1.default.dirname(exe) : path_1.default.dirname(script);
|
|
142
187
|
try {
|
|
143
188
|
const { installed } = await queryServiceLinux();
|
|
144
189
|
if (installed)
|
|
145
190
|
await stopService();
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
191
|
+
const execStart = isPkg ? exe : `${exe} ${script}`;
|
|
192
|
+
const unitContent = `[Unit]
|
|
193
|
+
Description=${exports.SERVICE_DISPLAY_NAME}
|
|
194
|
+
After=network.target
|
|
195
|
+
|
|
196
|
+
[Service]
|
|
197
|
+
Type=simple
|
|
198
|
+
User=${process.env.USER || 'root'}
|
|
199
|
+
WorkingDirectory=${workDir}
|
|
200
|
+
ExecStart=${execStart}
|
|
201
|
+
Restart=always
|
|
202
|
+
RestartSec=5
|
|
203
|
+
Environment=NODE_ENV=production
|
|
204
|
+
|
|
205
|
+
[Install]
|
|
206
|
+
WantedBy=multi-user.target
|
|
161
207
|
`;
|
|
162
208
|
await fs_1.default.promises.writeFile('/tmp/myassis-gateway.service', unitContent, 'utf8');
|
|
163
209
|
await execAsync('cp /tmp/myassis-gateway.service /etc/systemd/system/myassis-gateway.service', { timeout: 10000 });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myassis/gateway",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
4
4
|
"description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"author": "duzhengjie",
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@myassis/shared": "
|
|
43
|
+
"@myassis/shared": "1.0.19",
|
|
44
44
|
"@nut-tree/nut-js": "^4.2.0",
|
|
45
45
|
"axios": "^1.15.2",
|
|
46
46
|
"bcryptjs": "^2.4.3",
|
|
@@ -84,7 +84,8 @@
|
|
|
84
84
|
"README*",
|
|
85
85
|
"node_modules/better-sqlite3/build/Release/better_sqlite3.node",
|
|
86
86
|
"node_modules/.pnpm/@nut-tree+libnut-linux@2.7.1/node_modules/@nut-tree/libnut-linux/build/Release/libnut.node",
|
|
87
|
-
"node_modules/.pnpm/@nut-tree+libnut-win32@2.7.1/node_modules/@nut-tree/libnut-win32/build/Release/libnut.node"
|
|
87
|
+
"node_modules/.pnpm/@nut-tree+libnut-win32@2.7.1/node_modules/@nut-tree/libnut-win32/build/Release/libnut.node",
|
|
88
|
+
"nssm.exe"
|
|
88
89
|
],
|
|
89
90
|
"targets": [
|
|
90
91
|
"node24-win-x64",
|