clash-kit 1.1.0 → 1.1.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/.gitignore +30 -21
- package/LICENSE.md +8 -8
- package/README.md +133 -124
- package/bin/dns-helper +0 -0
- package/bin/index.js +96 -85
- package/default.yaml +46 -46
- package/index.js +204 -218
- package/lib/api.js +178 -116
- package/lib/commands/init.js +127 -127
- package/lib/commands/proxy.js +73 -73
- package/lib/commands/restart.js +10 -0
- package/lib/commands/start.js +30 -30
- package/lib/commands/status.js +105 -85
- package/lib/commands/stop.js +67 -30
- package/lib/commands/sub.js +81 -81
- package/lib/commands/sysproxy.js +60 -24
- package/lib/commands/test.js +70 -70
- package/lib/commands/tun.js +123 -95
- package/lib/kernel.js +197 -178
- package/lib/port.js +115 -115
- package/lib/subscription.js +128 -125
- package/lib/sysnet.js +73 -52
- package/lib/sysproxy.js +133 -145
- package/lib/tun.js +150 -126
- package/package.json +50 -48
package/lib/commands/proxy.js
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { select } from '@inquirer/prompts'
|
|
2
|
-
import ora from 'ora'
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
import * as api from '../api.js'
|
|
5
|
-
|
|
6
|
-
export async function proxy() {
|
|
7
|
-
let spinner = ora('正在获取最新代理列表...').start()
|
|
8
|
-
try {
|
|
9
|
-
let proxies = await api.getProxies()
|
|
10
|
-
spinner.stop()
|
|
11
|
-
|
|
12
|
-
// 通常我们只关心 Proxy 组或者 Selector 类型的组
|
|
13
|
-
const groups = Object.values(proxies).filter(p => p.type === 'Selector')
|
|
14
|
-
|
|
15
|
-
if (groups.length === 0) {
|
|
16
|
-
console.log('没有找到可选的节点组')
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// 选择组
|
|
21
|
-
const groupName = await select({
|
|
22
|
-
message: '请选择节点组:',
|
|
23
|
-
choices: groups.map(g => ({ name: g.name, value: g.name })),
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
const group = proxies[groupName]
|
|
27
|
-
|
|
28
|
-
// 自动对组内所有节点进行测速
|
|
29
|
-
spinner = ora(`测速后选择合适的节点,正在对 [${groupName}] 进行测速...`).start()
|
|
30
|
-
await Promise.all(group.all.map(n => api.getProxyDelay(n).catch(() => {})))
|
|
31
|
-
|
|
32
|
-
// 测速完成后,刷新数据以获取最新状态
|
|
33
|
-
proxies = await api.getProxies()
|
|
34
|
-
spinner.stop()
|
|
35
|
-
|
|
36
|
-
const updatedGroup = proxies[groupName]
|
|
37
|
-
|
|
38
|
-
// 选择节点
|
|
39
|
-
const proxyName = await select({
|
|
40
|
-
message: `[${groupName}] 当前: ${updatedGroup.now}, 请选择节点:`,
|
|
41
|
-
pageSize: 15,
|
|
42
|
-
choices: updatedGroup.all.map(n => {
|
|
43
|
-
const node = proxies[n]
|
|
44
|
-
const lastHistory = node?.history && node.history.length ? node.history[node.history.length - 1] : null
|
|
45
|
-
let delayInfo = ''
|
|
46
|
-
|
|
47
|
-
if (lastHistory && lastHistory.delay > 0) {
|
|
48
|
-
const delay = lastHistory.delay
|
|
49
|
-
if (delay < 800) {
|
|
50
|
-
delayInfo = chalk.green(` ${delay}ms`)
|
|
51
|
-
} else if (delay < 1500) {
|
|
52
|
-
delayInfo = chalk.yellow(` ${delay}ms`)
|
|
53
|
-
} else {
|
|
54
|
-
delayInfo = chalk.red(` ${delay}ms`)
|
|
55
|
-
}
|
|
56
|
-
} else if (lastHistory && lastHistory.delay === 0) {
|
|
57
|
-
delayInfo = chalk.red(' [超时]')
|
|
58
|
-
} else {
|
|
59
|
-
delayInfo = chalk.gray(' [未测速]')
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { name: `${n}${delayInfo}`, value: n }
|
|
63
|
-
}),
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
spinner = ora(`正在切换到 ${proxyName}...`).start()
|
|
67
|
-
await api.switchProxy(groupName, proxyName)
|
|
68
|
-
spinner.succeed(`已切换 ${groupName} -> ${proxyName}`)
|
|
69
|
-
} catch (err) {
|
|
70
|
-
if (spinner && spinner.isSpinning) spinner.fail(err.message)
|
|
71
|
-
else console.error(err.message)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
1
|
+
import { select } from '@inquirer/prompts'
|
|
2
|
+
import ora from 'ora'
|
|
3
|
+
import chalk from 'chalk'
|
|
4
|
+
import * as api from '../api.js'
|
|
5
|
+
|
|
6
|
+
export async function proxy() {
|
|
7
|
+
let spinner = ora('正在获取最新代理列表...').start()
|
|
8
|
+
try {
|
|
9
|
+
let proxies = await api.getProxies()
|
|
10
|
+
spinner.stop()
|
|
11
|
+
|
|
12
|
+
// 通常我们只关心 Proxy 组或者 Selector 类型的组
|
|
13
|
+
const groups = Object.values(proxies).filter(p => p.type === 'Selector')
|
|
14
|
+
|
|
15
|
+
if (groups.length === 0) {
|
|
16
|
+
console.log('没有找到可选的节点组')
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 选择组
|
|
21
|
+
const groupName = await select({
|
|
22
|
+
message: '请选择节点组:',
|
|
23
|
+
choices: groups.map(g => ({ name: g.name, value: g.name })),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const group = proxies[groupName]
|
|
27
|
+
|
|
28
|
+
// 自动对组内所有节点进行测速
|
|
29
|
+
spinner = ora(`测速后选择合适的节点,正在对 [${groupName}] 进行测速...`).start()
|
|
30
|
+
await Promise.all(group.all.map(n => api.getProxyDelay(n).catch(() => {})))
|
|
31
|
+
|
|
32
|
+
// 测速完成后,刷新数据以获取最新状态
|
|
33
|
+
proxies = await api.getProxies()
|
|
34
|
+
spinner.stop()
|
|
35
|
+
|
|
36
|
+
const updatedGroup = proxies[groupName]
|
|
37
|
+
|
|
38
|
+
// 选择节点
|
|
39
|
+
const proxyName = await select({
|
|
40
|
+
message: `[${groupName}] 当前: ${updatedGroup.now}, 请选择节点:`,
|
|
41
|
+
pageSize: 15,
|
|
42
|
+
choices: updatedGroup.all.map(n => {
|
|
43
|
+
const node = proxies[n]
|
|
44
|
+
const lastHistory = node?.history && node.history.length ? node.history[node.history.length - 1] : null
|
|
45
|
+
let delayInfo = ''
|
|
46
|
+
|
|
47
|
+
if (lastHistory && lastHistory.delay > 0) {
|
|
48
|
+
const delay = lastHistory.delay
|
|
49
|
+
if (delay < 800) {
|
|
50
|
+
delayInfo = chalk.green(` ${delay}ms`)
|
|
51
|
+
} else if (delay < 1500) {
|
|
52
|
+
delayInfo = chalk.yellow(` ${delay}ms`)
|
|
53
|
+
} else {
|
|
54
|
+
delayInfo = chalk.red(` ${delay}ms`)
|
|
55
|
+
}
|
|
56
|
+
} else if (lastHistory && lastHistory.delay === 0) {
|
|
57
|
+
delayInfo = chalk.red(' [超时]')
|
|
58
|
+
} else {
|
|
59
|
+
delayInfo = chalk.gray(' [未测速]')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { name: `${n}${delayInfo}`, value: n }
|
|
63
|
+
}),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
spinner = ora(`正在切换到 ${proxyName}...`).start()
|
|
67
|
+
await api.switchProxy(groupName, proxyName)
|
|
68
|
+
spinner.succeed(`已切换 ${groupName} -> ${proxyName}`)
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (spinner && spinner.isSpinning) spinner.fail(err.message)
|
|
71
|
+
else console.error(err.message)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { stop } from './stop.js'
|
|
2
|
+
import { start } from './start.js'
|
|
3
|
+
|
|
4
|
+
export async function restart(options) {
|
|
5
|
+
console.log('正在重启 Clash 服务...')
|
|
6
|
+
await stop()
|
|
7
|
+
// 稍微等待一下,确保资源释放
|
|
8
|
+
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
9
|
+
await start(options)
|
|
10
|
+
}
|
package/lib/commands/start.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import * as sysproxy from '../sysproxy.js'
|
|
2
|
-
import { main as startClashService } from '../../index.js'
|
|
3
|
-
import { setTun } from './tun.js'
|
|
4
|
-
|
|
5
|
-
export async function start(options) {
|
|
6
|
-
await startClashService()
|
|
7
|
-
|
|
8
|
-
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
9
|
-
|
|
10
|
-
if (options.sysproxy) {
|
|
11
|
-
console.log('正在等待 Clash API 就绪以设置系统代理...')
|
|
12
|
-
|
|
13
|
-
// 尝试 5 次,每次间隔 1 秒
|
|
14
|
-
for (let i = 0; i < 5; i++) {
|
|
15
|
-
await sleep(1000)
|
|
16
|
-
try {
|
|
17
|
-
const success = await sysproxy.enableSystemProxy()
|
|
18
|
-
if (success) break
|
|
19
|
-
} catch (e) {
|
|
20
|
-
if (i === 4) console.error('设置系统代理超时,请稍后手动设置: clash sysproxy on')
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (options.tun) {
|
|
26
|
-
// 稍微延迟一下,确保服务可能有响应
|
|
27
|
-
await sleep(1000)
|
|
28
|
-
await setTun('on')
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
import * as sysproxy from '../sysproxy.js'
|
|
2
|
+
import { main as startClashService } from '../../index.js'
|
|
3
|
+
import { setTun } from './tun.js'
|
|
4
|
+
|
|
5
|
+
export async function start(options) {
|
|
6
|
+
await startClashService()
|
|
7
|
+
|
|
8
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
9
|
+
|
|
10
|
+
if (options.sysproxy) {
|
|
11
|
+
console.log('正在等待 Clash API 就绪以设置系统代理...')
|
|
12
|
+
|
|
13
|
+
// 尝试 5 次,每次间隔 1 秒
|
|
14
|
+
for (let i = 0; i < 5; i++) {
|
|
15
|
+
await sleep(1000)
|
|
16
|
+
try {
|
|
17
|
+
const success = await sysproxy.enableSystemProxy()
|
|
18
|
+
if (success) break
|
|
19
|
+
} catch (e) {
|
|
20
|
+
if (i === 4) console.error('设置系统代理超时,请稍后手动设置: clash sysproxy on')
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (options.tun) {
|
|
26
|
+
// 稍微延迟一下,确保服务可能有响应
|
|
27
|
+
await sleep(1000)
|
|
28
|
+
await setTun('on')
|
|
29
|
+
}
|
|
30
|
+
}
|
package/lib/commands/status.js
CHANGED
|
@@ -1,85 +1,105 @@
|
|
|
1
|
-
import ora from 'ora'
|
|
2
|
-
import
|
|
3
|
-
import * as
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
console.
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
1
|
+
import ora from 'ora'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import * as api from '../api.js'
|
|
4
|
+
import * as sub from '../subscription.js'
|
|
5
|
+
import * as tun from '../tun.js'
|
|
6
|
+
import * as sysproxy from '../sysproxy.js'
|
|
7
|
+
import { getClashProcessInfo } from '../kernel.js'
|
|
8
|
+
import chalk from 'chalk'
|
|
9
|
+
import boxen from 'boxen'
|
|
10
|
+
|
|
11
|
+
export async function status() {
|
|
12
|
+
const spinner = ora('正在获取 Clash 状态...').start()
|
|
13
|
+
try {
|
|
14
|
+
const [config, currentProfile, tunEnabled, sysProxyStatus, proxies, processInfo] = await Promise.all([
|
|
15
|
+
api.getConfig(),
|
|
16
|
+
sub.getCurrentProfile(),
|
|
17
|
+
tun.isTunEnabled(),
|
|
18
|
+
sysproxy.getSystemProxyStatus(),
|
|
19
|
+
api.getProxies(),
|
|
20
|
+
getClashProcessInfo(),
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
spinner.stop()
|
|
24
|
+
const apiBase = api.getApiBase()
|
|
25
|
+
const configPath = sub.CONFIG_PATH
|
|
26
|
+
const logPath = path.resolve(path.dirname(configPath), 'clash.log')
|
|
27
|
+
|
|
28
|
+
const content = []
|
|
29
|
+
let statusLine = `状态:${chalk.green('运行中')}`
|
|
30
|
+
if (processInfo?.pid) {
|
|
31
|
+
statusLine += ` (PID: ${chalk.yellow(processInfo.pid)})`
|
|
32
|
+
}
|
|
33
|
+
content.push(statusLine)
|
|
34
|
+
content.push(`当前配置: ${currentProfile || '未知'}`)
|
|
35
|
+
content.push(`运行模式: ${config.mode}`)
|
|
36
|
+
content.push(`API 地址: ${chalk.cyan(apiBase)}`)
|
|
37
|
+
content.push(`HTTP 代理: ${chalk.cyan(`http://127.0.0.1:${config['port'] || '未设置'}`)}`)
|
|
38
|
+
content.push(`SOCKS5 代理: ${chalk.cyan(`socks5://127.0.0.1:${config['socks-port'] || '未设置'}`)}`)
|
|
39
|
+
content.push(
|
|
40
|
+
`混合代理: ${config['mixed-port'] ? chalk.cyan(`127.0.0.1:${config['mixed-port']}`) : chalk.gray('未设置')}`,
|
|
41
|
+
)
|
|
42
|
+
content.push('')
|
|
43
|
+
const sysProxyText = sysProxyStatus.enabled
|
|
44
|
+
? chalk.green(`已开启 (${sysProxyStatus.server}:${sysProxyStatus.port})`)
|
|
45
|
+
: chalk.gray('未开启')
|
|
46
|
+
content.push(`系统代理: ${sysProxyText}`)
|
|
47
|
+
content.push(`TUN 模式(拦截所有流量): ${tunEnabled ? chalk.green('已开启') : chalk.gray('未开启')}`)
|
|
48
|
+
content.push('')
|
|
49
|
+
content.push(`日志文件: ${chalk.blueBright.underline(logPath)}`)
|
|
50
|
+
content.push(`配置文件: ${chalk.blueBright.underline(configPath)}`)
|
|
51
|
+
content.push('')
|
|
52
|
+
|
|
53
|
+
content.push('')
|
|
54
|
+
content.push(
|
|
55
|
+
`${chalk.gray('系统代理: ck sys <on|off> , TUN 模式: ck tun <on|off>')} , 更多命令: ${chalk.blue('ck help')}`,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
console.log(
|
|
59
|
+
boxen(content.join('\n'), {
|
|
60
|
+
title: chalk.bold.bgGreen('Clash Kit'),
|
|
61
|
+
titleAlignment: 'center',
|
|
62
|
+
padding: 1,
|
|
63
|
+
borderStyle: 'bold',
|
|
64
|
+
borderColor: 'green',
|
|
65
|
+
margin: { top: 1, bottom: 0, left: 0, right: 0 },
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
// 默认测速 Proxy 组的所有节点
|
|
70
|
+
const group = proxies['Proxy'] || Object.values(proxies).find(p => p.type === 'Selector')
|
|
71
|
+
if (group?.now) {
|
|
72
|
+
const testUrl = group.now === 'DIRECT' ? 'http://connect.rom.miui.com/generate_204' : undefined
|
|
73
|
+
const delay = await api.getProxyDelay(group.now, testUrl)
|
|
74
|
+
let delayStr = ''
|
|
75
|
+
if (delay > 0) {
|
|
76
|
+
if (delay < 200) delayStr = chalk.green(`${delay}ms`)
|
|
77
|
+
else if (delay < 500) delayStr = chalk.yellow(`${delay}ms`)
|
|
78
|
+
else delayStr = chalk.red(`${delay}ms`)
|
|
79
|
+
} else {
|
|
80
|
+
delayStr = chalk.red('超时/失败')
|
|
81
|
+
}
|
|
82
|
+
console.log(`🚀 节点延迟 [${group.name}: ${group.now}]: ${delayStr}`)
|
|
83
|
+
}
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (spinner.isSpinning) spinner.stop()
|
|
86
|
+
if (err.message && (err.message.includes('ECONNREFUSED') || err.message.includes('无法连接'))) {
|
|
87
|
+
const configPath = sub.CONFIG_PATH
|
|
88
|
+
const content = []
|
|
89
|
+
content.push(`状态:${chalk.yellow('未运行')}`)
|
|
90
|
+
content.push('提示:请使用 `ck start` 启动服务')
|
|
91
|
+
content.push(`当前配置文件: ${configPath || '未知'}`)
|
|
92
|
+
console.log(
|
|
93
|
+
boxen(content.join('\n'), {
|
|
94
|
+
title: chalk.bold.bgYellow('Clash Kit'),
|
|
95
|
+
titleAlignment: 'center',
|
|
96
|
+
padding: 1,
|
|
97
|
+
borderStyle: 'single',
|
|
98
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
101
|
+
} else {
|
|
102
|
+
console.error(`获取状态失败: ${err.message}`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
package/lib/commands/stop.js
CHANGED
|
@@ -1,30 +1,67 @@
|
|
|
1
|
-
import ora from 'ora'
|
|
2
|
-
import * as sysproxy from '../sysproxy.js'
|
|
3
|
-
import * as tun from '../tun.js'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
import ora from 'ora'
|
|
2
|
+
import * as sysproxy from '../sysproxy.js'
|
|
3
|
+
import * as tun from '../tun.js'
|
|
4
|
+
import { setTun } from './tun.js'
|
|
5
|
+
import { killClashProcess } from '../kernel.js'
|
|
6
|
+
import boxen from 'boxen'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
|
|
9
|
+
export async function stop() {
|
|
10
|
+
const spinner = ora('正在停止服务...').start()
|
|
11
|
+
try {
|
|
12
|
+
let wasTunEnabled = false
|
|
13
|
+
|
|
14
|
+
// 1. 关闭系统代理
|
|
15
|
+
spinner.text = '正在关闭系统代理...'
|
|
16
|
+
await sysproxy.disableSystemProxy()
|
|
17
|
+
|
|
18
|
+
// 2. 检查并关闭 TUN 模式
|
|
19
|
+
const tunEnabled = await tun.isTunEnabled()
|
|
20
|
+
if (tunEnabled) {
|
|
21
|
+
wasTunEnabled = true
|
|
22
|
+
spinner.text = '正在关闭 TUN 模式...'
|
|
23
|
+
const result = await setTun('off', { silent: true })
|
|
24
|
+
if (!result.success) {
|
|
25
|
+
throw new Error(`关闭 TUN 模式失败: ${result.error}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 3. 停止 Clash 核心进程
|
|
30
|
+
spinner.text = '正在停止 Clash 核心进程...'
|
|
31
|
+
const stopped = killClashProcess()
|
|
32
|
+
spinner.stop() // All processing is done, stop spinner
|
|
33
|
+
|
|
34
|
+
// 4. 显示最终结果
|
|
35
|
+
if (stopped) {
|
|
36
|
+
const content = []
|
|
37
|
+
content.push(`Clash 服务: ${chalk.yellow('已停止')}`)
|
|
38
|
+
content.push(`系统代理: ${chalk.gray('已关闭')}`)
|
|
39
|
+
if (wasTunEnabled) content.push(`TUN 模式: ${chalk.gray('已关闭 (DNS 已恢复)')}`)
|
|
40
|
+
|
|
41
|
+
console.log(
|
|
42
|
+
boxen(content.join('\n'), {
|
|
43
|
+
title: 'Clash Kit',
|
|
44
|
+
padding: 1,
|
|
45
|
+
margin: 1,
|
|
46
|
+
borderStyle: 'round',
|
|
47
|
+
borderColor: 'green',
|
|
48
|
+
titleAlignment: 'center',
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
} else {
|
|
52
|
+
console.log(
|
|
53
|
+
boxen(chalk.yellow('未找到运行中的 Clash 服务'), {
|
|
54
|
+
title: 'Clash Kit',
|
|
55
|
+
padding: 1,
|
|
56
|
+
margin: 1,
|
|
57
|
+
borderStyle: 'round',
|
|
58
|
+
borderColor: 'yellow',
|
|
59
|
+
titleAlignment: 'center',
|
|
60
|
+
}),
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (spinner.isSpinning) spinner.stop()
|
|
65
|
+
console.error(`\n停止服务时出错: ${err.message}`)
|
|
66
|
+
}
|
|
67
|
+
}
|