@lancewu602/mihomo-dash 0.6.0 → 0.6.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/package.json +1 -1
- package/server/CHANGELOG.md +14 -0
- package/server/package.json +1 -1
- package/server/src/cli/index.js +2 -2
- package/server/src/cli/service.js +3 -2
- package/server/src/cli/status.js +4 -2
- package/server/src/shared/service.js +5 -3
- package/server/tests/service.test.js +31 -1
- package/server/tests/status.test.js +6 -3
package/package.json
CHANGED
package/server/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.6.2] - 2026-10-31
|
|
9
|
+
|
|
10
|
+
### 修复
|
|
11
|
+
|
|
12
|
+
- `service uninstall` / `service start` 回显的动作命令按作用域生成:system 作用域下不再印
|
|
13
|
+
`systemctl --user …`(真实执行的 argv 本来就不带 `--user`,只有文案错)
|
|
14
|
+
|
|
15
|
+
## [0.6.1] - 2026-10-31
|
|
16
|
+
|
|
17
|
+
### 修复
|
|
18
|
+
|
|
19
|
+
- CLI 与生成的服务定义里不再提「自启」页:`status` 的提示改为 `mihomo-dash service install`,
|
|
20
|
+
unit 头注释改为「由 `mihomo-dash service install` 生成」;`--help` 里 Debian 作用域不再只写 `--user`
|
|
21
|
+
|
|
8
22
|
## [0.6.0] - 2026-10-31
|
|
9
23
|
|
|
10
24
|
### 新功能
|
package/server/package.json
CHANGED
package/server/src/cli/index.js
CHANGED
|
@@ -22,13 +22,13 @@ const HELP = `mihomo-dash —— mihomo 管理工具:CLI + Web 面板
|
|
|
22
22
|
用法: mihomo-dash [命令]
|
|
23
23
|
|
|
24
24
|
status 总览:内核 / 面板 / 系统代理 / 开机自启(默认命令;内核不通时退出码 1)
|
|
25
|
-
service <子命令> 开机自启:print / install / uninstall / status(macOS launchd · Debian systemd
|
|
25
|
+
service <子命令> 开机自启:print / install / uninstall / status(macOS launchd · Debian systemd)
|
|
26
26
|
|
|
27
27
|
-V, --version 版本
|
|
28
28
|
-h, --help 帮助
|
|
29
29
|
|
|
30
30
|
面板进程不再由自带的守护进程管理:交给系统服务管理器,服务定义指向 server/src/index.js(面板)
|
|
31
|
-
与 server/src/kernel/run.js
|
|
31
|
+
与 server/src/kernel/run.js(内核),开机自启用上面的 service 子命令。
|
|
32
32
|
内核的装 / 升 / 卸用面板的「内核」页,进程启停在侧栏底部;系统代理开关也不在 CLI(用面板顶栏的
|
|
33
33
|
胶囊开关或配置页的「系统代理」卡片)。(面板默认 http://127.0.0.1:8981)
|
|
34
34
|
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* 但平台面不一样,**不要照抄 deck 的实现**:
|
|
6
6
|
*
|
|
7
7
|
* - deck 只跑 Linux + **system** 单元(`/etc/systemd/system`,要 root);
|
|
8
|
-
* - 这里要同时支持 **macOS(launchd
|
|
8
|
+
* - 这里要同时支持 **macOS(launchd 用户级)与 Debian(systemd system 与 --user 两种)**:
|
|
9
|
+
* Debian 默认 system(要 root、开机即起),--user 是备选;macOS 只有用户级;
|
|
9
10
|
* 两个平台都交给 `createServiceManager()`(`server/src/shared/service.js`),CLI 只管打印结果。
|
|
10
11
|
* - 两个 job:`panel`(server/src/index.js)与 `kernel`(server/src/kernel/run.js)。
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
+
* 行为约定:**装/卸都不停正在跑的进程**(这个面板很可能正是被服务拉起来的),
|
|
13
14
|
* 「只开机拉起一次、不保活」(launchd 不写 KeepAlive、systemd 写 Restart=no)。
|
|
14
15
|
*/
|
|
15
16
|
import { ownKernelRunning } from '../kernel/lifecycle.js';
|
package/server/src/cli/status.js
CHANGED
|
@@ -197,7 +197,9 @@ export function formatStatus(d) {
|
|
|
197
197
|
bits.push(`已运行 ${duration(panel.uptime)}`);
|
|
198
198
|
out.push(line('面板', `运行中 · ${panel.url} · ${bits.join(' · ')}`));
|
|
199
199
|
} else {
|
|
200
|
-
out.push(
|
|
200
|
+
out.push(
|
|
201
|
+
line('面板', `未运行(端口 ${panel.port};用 \`mihomo-dash service install\` 装成系统服务)`),
|
|
202
|
+
);
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
// 系统代理
|
|
@@ -220,7 +222,7 @@ export function formatStatus(d) {
|
|
|
220
222
|
if (!autostart.supported) {
|
|
221
223
|
out.push(line('开机自启', autostart.hint ? `不支持(${autostart.hint})` : '不支持'));
|
|
222
224
|
} else if (!autostart.installed) {
|
|
223
|
-
out.push(line('开机自启', '
|
|
225
|
+
out.push(line('开机自启', '未安装(`mihomo-dash service install`)'));
|
|
224
226
|
} else {
|
|
225
227
|
const where = autostart.platform === 'darwin' ? 'macOS LaunchAgent' : 'systemd --user';
|
|
226
228
|
out.push(
|
|
@@ -143,7 +143,7 @@ export function renderUnit(job, opts) {
|
|
|
143
143
|
const entry = opts.entry ?? opts.entries[job];
|
|
144
144
|
const argv = [opts.node, entry, ...def.args({ port, host })];
|
|
145
145
|
const log = def.log(opts.cliDir);
|
|
146
|
-
return `#
|
|
146
|
+
return `# 由 \`mihomo-dash service install\` 生成(server/src/shared/service.js),勿手改 —— 重新安装会覆盖
|
|
147
147
|
# 作用域:${scope === 'system' ? 'system(/etc/systemd/system,以 root 运行,开机即起)' : 'user(~/.config/systemd/user,登录后启动 or linger)'}
|
|
148
148
|
[Unit]
|
|
149
149
|
Description=${def.description}
|
|
@@ -227,6 +227,8 @@ export function createServiceManager(options = {}) {
|
|
|
227
227
|
const scope = platform === 'linux' ? (options.scope ?? (isRoot ? 'system' : 'user')) : 'user';
|
|
228
228
|
/** systemd 的公共参数:system 作用域不带 `--user` */
|
|
229
229
|
const systemdArgs = scope === 'system' ? [] : ['--user'];
|
|
230
|
+
/** 回显给人看的 systemctl 前缀,必须与 systemdArgs 一致 */
|
|
231
|
+
const systemctlCmd = ['systemctl', ...systemdArgs].join(' ');
|
|
230
232
|
/** launchd 的域:GUI 会话固定在 gui/<uid>,只有 SSH 时会话里退到 user/<uid> */
|
|
231
233
|
const domains = [`gui/${uid}`, `user/${uid}`];
|
|
232
234
|
const dir = serviceDir({ platform, home, scope, systemDir: options.systemDir });
|
|
@@ -433,7 +435,7 @@ export function createServiceManager(options = {}) {
|
|
|
433
435
|
const stillLoaded = platform === 'darwin' && !!(await loadedDomain(job));
|
|
434
436
|
if (platform === 'linux') {
|
|
435
437
|
const r = await run('systemctl', [...systemdArgs, 'disable', jobOf(job).unit]);
|
|
436
|
-
if (r.ok) actions.push(
|
|
438
|
+
if (r.ok) actions.push(`${systemctlCmd} disable ${jobOf(job).unit}`);
|
|
437
439
|
else if (r.stderr && !/not loaded|does not exist|No such file/i.test(r.stderr)) {
|
|
438
440
|
errors.push(`${job}: ${r.stderr.trim()}`);
|
|
439
441
|
}
|
|
@@ -541,7 +543,7 @@ export function createServiceManager(options = {}) {
|
|
|
541
543
|
else errors.push(`${j}: ${r.stderr.trim() || 'launchctl kickstart 失败'}`);
|
|
542
544
|
} else {
|
|
543
545
|
const r = await run('systemctl', [...systemdArgs, 'start', jobOf(j).unit]);
|
|
544
|
-
if (r.ok) started.push(
|
|
546
|
+
if (r.ok) started.push(`${systemctlCmd} start ${jobOf(j).unit}`);
|
|
545
547
|
else errors.push(`${j}: ${r.stderr.trim() || 'systemctl start 失败'}`);
|
|
546
548
|
}
|
|
547
549
|
}
|
|
@@ -106,7 +106,7 @@ test('plist:路径里的 XML 特殊字符会被转义', () => {
|
|
|
106
106
|
|
|
107
107
|
test('systemd 单元:ExecStart 固定 argv、Restart=no、日志 append', () => {
|
|
108
108
|
const unit = renderUnit('panel', OPTS());
|
|
109
|
-
assert.match(unit, /^#
|
|
109
|
+
assert.match(unit, /^# 由 `mihomo-dash service install` 生成/m);
|
|
110
110
|
assert.match(unit, /Description=mihomo-dash 面板/);
|
|
111
111
|
assert.match(
|
|
112
112
|
unit,
|
|
@@ -252,6 +252,36 @@ test('Linux 安装/卸载:daemon-reload + enable --now / disable(不 --now
|
|
|
252
252
|
assert.ok(!calls.some((c) => c.includes('--now') && c.includes('disable')));
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
test('Linux system 作用域:回显的动作命令不带 --user', async () => {
|
|
256
|
+
const { run, calls } = stubRun();
|
|
257
|
+
// system 作用域默认写 /etc(测试里换成临时目录,别碰本机)
|
|
258
|
+
const systemDir = path.join(tmpDir, 'etc-systemd');
|
|
259
|
+
const svc = createServiceManager({
|
|
260
|
+
run,
|
|
261
|
+
platform: 'linux',
|
|
262
|
+
scope: 'system',
|
|
263
|
+
systemDir,
|
|
264
|
+
home,
|
|
265
|
+
cliDir,
|
|
266
|
+
});
|
|
267
|
+
await svc.install({ port: 8982, host: '0.0.0.0' });
|
|
268
|
+
// 真正执行的命令本来就不带 --user;回显文案以前写死了 --user,照着抄会失败
|
|
269
|
+
assert.ok(calls.some((c) => c.join(' ') === 'systemctl enable --now mihomo-dash-panel.service'));
|
|
270
|
+
|
|
271
|
+
const started = await svc.start();
|
|
272
|
+
assert.deepEqual(started.started, [
|
|
273
|
+
'systemctl start mihomo-dash-panel.service',
|
|
274
|
+
'systemctl start mihomo-dash-kernel.service',
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
const res = await svc.uninstall();
|
|
278
|
+
assert.deepEqual(res.actions, [
|
|
279
|
+
'systemctl disable mihomo-dash-panel.service',
|
|
280
|
+
'systemctl disable mihomo-dash-kernel.service',
|
|
281
|
+
]);
|
|
282
|
+
assert.deepEqual(res.errors, []);
|
|
283
|
+
});
|
|
284
|
+
|
|
255
285
|
test('Linux 安装:manualRunning 的 job 只 enable(不 --now),不拉第二个实例', async () => {
|
|
256
286
|
const { run, calls } = stubRun();
|
|
257
287
|
const svc = createServiceManager({ run, platform: 'linux', home, cliDir });
|
|
@@ -194,7 +194,7 @@ test('内核不通:如实说原因 + 给启动去处(退出码由 cmdStatus
|
|
|
194
194
|
assert.match(out, /^开机自启\s+/m);
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
-
test('
|
|
197
|
+
test('面板没起:如实说,并指向 service install', async () => {
|
|
198
198
|
const closed = await new Promise((resolve) => {
|
|
199
199
|
const srv = http.createServer();
|
|
200
200
|
srv.listen(0, '127.0.0.1', () => {
|
|
@@ -207,7 +207,10 @@ test('面板没起:如实说,并指向「自启」页', async () => {
|
|
|
207
207
|
const out = mods.status.formatStatus(d);
|
|
208
208
|
assert.match(
|
|
209
209
|
out,
|
|
210
|
-
new RegExp(
|
|
210
|
+
new RegExp(
|
|
211
|
+
'^面板\\s+未运行(端口 ' + closed + ';用 `mihomo-dash service install` 装成系统服务)$',
|
|
212
|
+
'm',
|
|
213
|
+
),
|
|
211
214
|
);
|
|
212
215
|
});
|
|
213
216
|
|
|
@@ -247,7 +250,7 @@ test('平台不支持 / 没装:系统代理与开机自启各写一行,不
|
|
|
247
250
|
});
|
|
248
251
|
const out2 = mods.status.formatStatus(off);
|
|
249
252
|
assert.match(out2, /^系统代理\s+关(系统直连,内核照跑)$/m);
|
|
250
|
-
assert.match(out2, /^开机自启\s
|
|
253
|
+
assert.match(out2, /^开机自启\s+未安装(`mihomo-dash service install`)$/m);
|
|
251
254
|
});
|
|
252
255
|
|
|
253
256
|
test('某个块抛错:只记下原因,不连累别的块', async () => {
|