clash-kit 1.1.1 → 1.1.5
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 +159 -126
- package/bin/index.js +113 -85
- package/default.yaml +46 -46
- package/lib/api.js +178 -116
- package/lib/commands/init.js +133 -127
- package/lib/commands/proxy.js +81 -73
- package/lib/commands/restart.js +10 -0
- package/lib/commands/start.js +38 -30
- package/lib/commands/status.js +126 -85
- package/lib/commands/stop.js +67 -30
- package/lib/commands/sub.js +124 -81
- package/lib/commands/sysproxy.js +60 -24
- package/lib/commands/test.js +74 -70
- package/lib/commands/tun.js +123 -95
- package/lib/kernel.js +197 -178
- package/lib/port.js +115 -115
- package/{index.js → lib/service.js} +200 -218
- package/lib/subscription.js +152 -125
- package/lib/sysnet.js +50 -52
- package/lib/sysproxy.js +133 -145
- package/lib/tun.js +126 -126
- package/package.json +49 -49
package/lib/sysproxy.js
CHANGED
|
@@ -1,145 +1,133 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'*.
|
|
18
|
-
'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'172.
|
|
28
|
-
'172.
|
|
29
|
-
'172.
|
|
30
|
-
'172.
|
|
31
|
-
'172.
|
|
32
|
-
'172.
|
|
33
|
-
'172.
|
|
34
|
-
'172.
|
|
35
|
-
'172.
|
|
36
|
-
'172.
|
|
37
|
-
'172.
|
|
38
|
-
'172.
|
|
39
|
-
'172.
|
|
40
|
-
'172.
|
|
41
|
-
'172.
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
return { enabled: true, server, port }
|
|
136
|
-
}
|
|
137
|
-
return { enabled: false }
|
|
138
|
-
} catch (e) {
|
|
139
|
-
// 如果注册表键不存在或查询出错
|
|
140
|
-
return { enabled: false, error: e.message }
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
return { enabled: false, error: '不支持的平台' }
|
|
144
|
-
}
|
|
145
|
-
}
|
|
1
|
+
import { triggerManualProxy } from '@mihomo-party/sysproxy'
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import * as api from './api.js'
|
|
4
|
+
|
|
5
|
+
const defaultBypass = (() => {
|
|
6
|
+
switch (process.platform) {
|
|
7
|
+
case 'linux':
|
|
8
|
+
return ['localhost', '127.0.0.1', '192.168.0.0/16', '10.0.0.0/8', '172.16.0.0/12', '::1']
|
|
9
|
+
case 'darwin':
|
|
10
|
+
return [
|
|
11
|
+
'127.0.0.1',
|
|
12
|
+
'192.168.0.0/16',
|
|
13
|
+
'10.0.0.0/8',
|
|
14
|
+
'172.16.0.0/12',
|
|
15
|
+
'localhost',
|
|
16
|
+
'*.local',
|
|
17
|
+
'*.crashlytics.com',
|
|
18
|
+
'<local>',
|
|
19
|
+
]
|
|
20
|
+
case 'win32':
|
|
21
|
+
return [
|
|
22
|
+
'localhost',
|
|
23
|
+
'127.*',
|
|
24
|
+
'192.168.*',
|
|
25
|
+
'10.*',
|
|
26
|
+
'172.16.*',
|
|
27
|
+
'172.17.*',
|
|
28
|
+
'172.18.*',
|
|
29
|
+
'172.19.*',
|
|
30
|
+
'172.20.*',
|
|
31
|
+
'172.21.*',
|
|
32
|
+
'172.22.*',
|
|
33
|
+
'172.23.*',
|
|
34
|
+
'172.24.*',
|
|
35
|
+
'172.25.*',
|
|
36
|
+
'172.26.*',
|
|
37
|
+
'172.27.*',
|
|
38
|
+
'172.28.*',
|
|
39
|
+
'172.29.*',
|
|
40
|
+
'172.30.*',
|
|
41
|
+
'172.31.*',
|
|
42
|
+
'<local>',
|
|
43
|
+
]
|
|
44
|
+
default:
|
|
45
|
+
return ['localhost', '127.0.0.1', '192.168.0.0/16', '10.0.0.0/8', '172.16.0.0/12', '::1']
|
|
46
|
+
}
|
|
47
|
+
})()
|
|
48
|
+
|
|
49
|
+
export async function enableSystemProxy() {
|
|
50
|
+
try {
|
|
51
|
+
const config = await api.getConfig()
|
|
52
|
+
const port = config['mixed-port'] || config['port']
|
|
53
|
+
if (!port) throw new Error('未找到 HTTP 代理端口配置 (port 或 mixed-port)')
|
|
54
|
+
// 默认代理地址为 127.0.0.1
|
|
55
|
+
const host = '127.0.0.1'
|
|
56
|
+
const bypass = defaultBypass.join(',')
|
|
57
|
+
|
|
58
|
+
// 开启系统代理
|
|
59
|
+
triggerManualProxy(true, host, port, bypass)
|
|
60
|
+
return { success: true, host, port }
|
|
61
|
+
} catch (err) {
|
|
62
|
+
return { success: false, error: err.message }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function disableSystemProxy() {
|
|
67
|
+
try {
|
|
68
|
+
// 关闭系统代理
|
|
69
|
+
triggerManualProxy(false, '', 0, '')
|
|
70
|
+
return { success: true }
|
|
71
|
+
} catch (err) {
|
|
72
|
+
return { success: false, error: err.message }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function getSystemProxyStatus() {
|
|
77
|
+
// 使用 macOS networksetup 命令获取系统代理状态
|
|
78
|
+
if (process.platform === 'darwin') {
|
|
79
|
+
try {
|
|
80
|
+
const output = execSync('networksetup -getwebproxy Wi-Fi', { encoding: 'utf-8' })
|
|
81
|
+
const enabled = output.includes('Enabled: Yes')
|
|
82
|
+
if (enabled) {
|
|
83
|
+
const serverMatch = output.match(/Server: (.+)/)
|
|
84
|
+
const portMatch = output.match(/Port: (\d+)/)
|
|
85
|
+
const server = serverMatch ? serverMatch[1].trim() : ''
|
|
86
|
+
const port = portMatch ? portMatch[1].trim() : ''
|
|
87
|
+
return { enabled: true, server, port }
|
|
88
|
+
}
|
|
89
|
+
return { enabled: false }
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return { enabled: false, error: e.message }
|
|
92
|
+
}
|
|
93
|
+
} else if (process.platform === 'win32') {
|
|
94
|
+
try {
|
|
95
|
+
const regPath = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings'
|
|
96
|
+
// 查询 ProxyEnable
|
|
97
|
+
const enableOutput = execSync(`reg query "${regPath}" /v ProxyEnable`, { encoding: 'utf-8' })
|
|
98
|
+
// 检查输出中是否包含 0x1,表示代理已开启
|
|
99
|
+
const isEnabled = /ProxyEnable\s+REG_DWORD\s+0x1/.test(enableOutput)
|
|
100
|
+
|
|
101
|
+
if (isEnabled) {
|
|
102
|
+
let server = ''
|
|
103
|
+
let port = ''
|
|
104
|
+
try {
|
|
105
|
+
// 查询 ProxyServer
|
|
106
|
+
const serverOutput = execSync(`reg query "${regPath}" /v ProxyServer`, { encoding: 'utf-8' })
|
|
107
|
+
const match = serverOutput.match(/ProxyServer\s+REG_SZ\s+(.*)/)
|
|
108
|
+
|
|
109
|
+
if (match && match[1]) {
|
|
110
|
+
const fullAddress = match[1].trim()
|
|
111
|
+
// 简单的处理 host:port 格式
|
|
112
|
+
const lastColonIndex = fullAddress.lastIndexOf(':')
|
|
113
|
+
if (lastColonIndex !== -1) {
|
|
114
|
+
server = fullAddress.substring(0, lastColonIndex)
|
|
115
|
+
port = fullAddress.substring(lastColonIndex + 1)
|
|
116
|
+
} else {
|
|
117
|
+
server = fullAddress
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
// 忽略获取详细信息的错误
|
|
122
|
+
}
|
|
123
|
+
return { enabled: true, server, port }
|
|
124
|
+
}
|
|
125
|
+
return { enabled: false }
|
|
126
|
+
} catch (e) {
|
|
127
|
+
// 如果注册表键不存在或查询出错
|
|
128
|
+
return { enabled: false, error: e.message }
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
return { enabled: false, error: '不支持的平台' }
|
|
132
|
+
}
|
|
133
|
+
}
|
package/lib/tun.js
CHANGED
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
import { execSync } from 'child_process'
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import YAML from 'yaml'
|
|
5
|
-
import { fileURLToPath } from 'url'
|
|
6
|
-
import { reloadConfig } from './api.js'
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
-
const __dirname = path.dirname(__filename)
|
|
10
|
-
const CONFIG_PATH = path.join(__dirname, '../config.yaml')
|
|
11
|
-
const BIN_PATH = path.join(__dirname, '../clash-kit')
|
|
12
|
-
|
|
13
|
-
export function checkTunPermissions() {
|
|
14
|
-
if (process.platform === 'win32') return true // Windows 需要管理员权限终端,难以通过文件属性判断
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const stats = fs.statSync(BIN_PATH)
|
|
18
|
-
// SUID 位是 0o4000
|
|
19
|
-
// 检查所有者是否为 root (uid 0) 并且拥有 SUID 位
|
|
20
|
-
const isRootOwned = stats.uid === 0
|
|
21
|
-
const hasSuid = (stats.mode & 0o4000) === 0o4000
|
|
22
|
-
return isRootOwned && hasSuid
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return false
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function setupPermissions() {
|
|
29
|
-
if (process.platform === 'win32') {
|
|
30
|
-
throw new Error('Windows 请使用管理员身份运行终端即可')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const group = process.platform === 'darwin' ? 'admin' : 'root'
|
|
34
|
-
const cmdChown = `chown root:${group} "${BIN_PATH}"`
|
|
35
|
-
const cmdChmod = `chmod +sx "${BIN_PATH}"`
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
console.log('正在提升内核权限 (需要 sudo 密码)...')
|
|
39
|
-
execSync(`sudo ${cmdChown}`, { stdio: 'inherit' })
|
|
40
|
-
execSync(`sudo ${cmdChmod}`, { stdio: 'inherit' })
|
|
41
|
-
return true
|
|
42
|
-
} catch (e) {
|
|
43
|
-
throw new Error('权限设置失败')
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function isTunEnabled() {
|
|
48
|
-
try {
|
|
49
|
-
if (!fs.existsSync(CONFIG_PATH)) return false
|
|
50
|
-
const file = fs.readFileSync(CONFIG_PATH, 'utf8')
|
|
51
|
-
const config = YAML.parse(file)
|
|
52
|
-
return config?.tun?.enable === true
|
|
53
|
-
} catch (error) {
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function enableTun() {
|
|
59
|
-
await updateTunConfig(true)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export async function disableTun() {
|
|
63
|
-
await updateTunConfig(false)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function updateTunConfig(enable) {
|
|
67
|
-
try {
|
|
68
|
-
if (!fs.existsSync(CONFIG_PATH)) {
|
|
69
|
-
throw new Error('配置文件不存在')
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const file = fs.readFileSync(CONFIG_PATH, 'utf8')
|
|
73
|
-
const config = YAML.parse(file) || {}
|
|
74
|
-
|
|
75
|
-
// 基础 TUN 配置
|
|
76
|
-
if (!config.tun) {
|
|
77
|
-
config.tun = {}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 强制更新关键配置
|
|
81
|
-
config.tun.enable = enable
|
|
82
|
-
if (enable) {
|
|
83
|
-
config.tun.stack = 'mixed' // 推荐使用 mixed 模式 (system/gvisor)
|
|
84
|
-
config.tun['auto-route'] = true
|
|
85
|
-
config.tun['auto-detect-interface'] = true
|
|
86
|
-
config.tun['dns-hijack'] = ['any:53']
|
|
87
|
-
if (process.platform === 'darwin') {
|
|
88
|
-
config.tun.device = 'utun1500' // macOS 推荐指定 device
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 开启 TUN 时必须配合 DNS 增强模式
|
|
93
|
-
if (enable) {
|
|
94
|
-
if (!config.dns) config.dns = {}
|
|
95
|
-
|
|
96
|
-
config.dns.enable = true
|
|
97
|
-
config.dns['enhanced-mode'] = 'fake-ip'
|
|
98
|
-
config.dns.listen = config.dns.listen || '0.0.0.0:1053'
|
|
99
|
-
config.dns.ipv6 = false // 避免 IPv6 导致的一些漏网之鱼,视情况开启
|
|
100
|
-
|
|
101
|
-
// 确保有可用的 Nameservers (参考 clash-party 默认值)
|
|
102
|
-
const defaultNameservers = [
|
|
103
|
-
'https://223.5.5.5/dns-query', // AliDNS
|
|
104
|
-
'https://doh.pub/dns-query', // DNSPod
|
|
105
|
-
'8.8.8.8',
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
if (!config.dns.nameserver || config.dns.nameserver.length === 0) {
|
|
109
|
-
config.dns.nameserver = defaultNameservers
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 添加 Fake-IP 过滤,防止回环
|
|
113
|
-
if (!config.dns['fake-ip-filter']) {
|
|
114
|
-
config.dns['fake-ip-filter'] = ['*', '+.lan', '+.local']
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const newYaml = YAML.stringify(config)
|
|
119
|
-
fs.writeFileSync(CONFIG_PATH, newYaml, 'utf8')
|
|
120
|
-
|
|
121
|
-
// 重载配置使生效
|
|
122
|
-
await reloadConfig(CONFIG_PATH)
|
|
123
|
-
} catch (error) {
|
|
124
|
-
throw new Error(`修改 TUN 配置失败: ${error.message}`)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
1
|
+
import { execSync } from 'child_process'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import YAML from 'yaml'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import { reloadConfig } from './api.js'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = path.dirname(__filename)
|
|
10
|
+
const CONFIG_PATH = path.join(__dirname, '../config.yaml')
|
|
11
|
+
const BIN_PATH = path.join(__dirname, '../clash-kit')
|
|
12
|
+
|
|
13
|
+
export function checkTunPermissions() {
|
|
14
|
+
if (process.platform === 'win32') return true // Windows 需要管理员权限终端,难以通过文件属性判断
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const stats = fs.statSync(BIN_PATH)
|
|
18
|
+
// SUID 位是 0o4000
|
|
19
|
+
// 检查所有者是否为 root (uid 0) 并且拥有 SUID 位
|
|
20
|
+
const isRootOwned = stats.uid === 0
|
|
21
|
+
const hasSuid = (stats.mode & 0o4000) === 0o4000
|
|
22
|
+
return isRootOwned && hasSuid
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function setupPermissions() {
|
|
29
|
+
if (process.platform === 'win32') {
|
|
30
|
+
throw new Error('Windows 请使用管理员身份运行终端即可')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const group = process.platform === 'darwin' ? 'admin' : 'root'
|
|
34
|
+
const cmdChown = `chown root:${group} "${BIN_PATH}"`
|
|
35
|
+
const cmdChmod = `chmod +sx "${BIN_PATH}"`
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
console.log('正在提升内核权限 (需要 sudo 密码)...')
|
|
39
|
+
execSync(`sudo ${cmdChown}`, { stdio: 'inherit' })
|
|
40
|
+
execSync(`sudo ${cmdChmod}`, { stdio: 'inherit' })
|
|
41
|
+
return true
|
|
42
|
+
} catch (e) {
|
|
43
|
+
throw new Error('权限设置失败')
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function isTunEnabled() {
|
|
48
|
+
try {
|
|
49
|
+
if (!fs.existsSync(CONFIG_PATH)) return false
|
|
50
|
+
const file = fs.readFileSync(CONFIG_PATH, 'utf8')
|
|
51
|
+
const config = YAML.parse(file)
|
|
52
|
+
return config?.tun?.enable === true
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function enableTun() {
|
|
59
|
+
await updateTunConfig(true)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function disableTun() {
|
|
63
|
+
await updateTunConfig(false)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function updateTunConfig(enable) {
|
|
67
|
+
try {
|
|
68
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
69
|
+
throw new Error('配置文件不存在')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const file = fs.readFileSync(CONFIG_PATH, 'utf8')
|
|
73
|
+
const config = YAML.parse(file) || {}
|
|
74
|
+
|
|
75
|
+
// 基础 TUN 配置
|
|
76
|
+
if (!config.tun) {
|
|
77
|
+
config.tun = {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 强制更新关键配置
|
|
81
|
+
config.tun.enable = enable
|
|
82
|
+
if (enable) {
|
|
83
|
+
config.tun.stack = 'mixed' // 推荐使用 mixed 模式 (system/gvisor)
|
|
84
|
+
config.tun['auto-route'] = true
|
|
85
|
+
config.tun['auto-detect-interface'] = true
|
|
86
|
+
config.tun['dns-hijack'] = ['any:53']
|
|
87
|
+
if (process.platform === 'darwin') {
|
|
88
|
+
config.tun.device = 'utun1500' // macOS 推荐指定 device
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 开启 TUN 时必须配合 DNS 增强模式
|
|
93
|
+
if (enable) {
|
|
94
|
+
if (!config.dns) config.dns = {}
|
|
95
|
+
|
|
96
|
+
config.dns.enable = true
|
|
97
|
+
config.dns['enhanced-mode'] = 'fake-ip'
|
|
98
|
+
config.dns.listen = config.dns.listen || '0.0.0.0:1053'
|
|
99
|
+
config.dns.ipv6 = false // 避免 IPv6 导致的一些漏网之鱼,视情况开启
|
|
100
|
+
|
|
101
|
+
// 确保有可用的 Nameservers (参考 clash-party 默认值)
|
|
102
|
+
const defaultNameservers = [
|
|
103
|
+
'https://223.5.5.5/dns-query', // AliDNS
|
|
104
|
+
'https://doh.pub/dns-query', // DNSPod
|
|
105
|
+
'8.8.8.8',
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
if (!config.dns.nameserver || config.dns.nameserver.length === 0) {
|
|
109
|
+
config.dns.nameserver = defaultNameservers
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 添加 Fake-IP 过滤,防止回环
|
|
113
|
+
if (!config.dns['fake-ip-filter']) {
|
|
114
|
+
config.dns['fake-ip-filter'] = ['*', '+.lan', '+.local']
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const newYaml = YAML.stringify(config)
|
|
119
|
+
fs.writeFileSync(CONFIG_PATH, newYaml, 'utf8')
|
|
120
|
+
|
|
121
|
+
// 重载配置使生效
|
|
122
|
+
await reloadConfig(CONFIG_PATH)
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw new Error(`修改 TUN 配置失败: ${error.message}`)
|
|
125
|
+
}
|
|
126
|
+
}
|
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "clash-kit",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "A command-line interface for managing Clash configurations, subscriptions, and proxies.",
|
|
6
|
-
"bin": {
|
|
7
|
-
"ck": "./bin/index.js",
|
|
8
|
-
"clash": "./bin/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"bin",
|
|
12
|
-
"lib",
|
|
13
|
-
".gitignore",
|
|
14
|
-
"default.yaml",
|
|
15
|
-
"country.mmdb",
|
|
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
|
-
"yaml": "^2.8.2"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "clash-kit",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A command-line interface for managing Clash configurations, subscriptions, and proxies.",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ck": "./bin/index.js",
|
|
8
|
+
"clash": "./bin/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"lib",
|
|
13
|
+
".gitignore",
|
|
14
|
+
"default.yaml",
|
|
15
|
+
"country.mmdb",
|
|
16
|
+
"LICENSE.md",
|
|
17
|
+
"package.json",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"start": "node bin/index.js start",
|
|
22
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
23
|
+
"prepublishOnly": "node scripts/release.js"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"clash",
|
|
27
|
+
"proxy",
|
|
28
|
+
"cli",
|
|
29
|
+
"subscription",
|
|
30
|
+
"manager"
|
|
31
|
+
],
|
|
32
|
+
"author": "荣顶",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"packageManager": "pnpm@10.23.0",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@inquirer/prompts": "^8.1.0",
|
|
37
|
+
"@mihomo-party/sysproxy": "^2.0.8",
|
|
38
|
+
"adm-zip": "^0.5.16",
|
|
39
|
+
"axios": "^1.13.2",
|
|
40
|
+
"boxen": "^8.0.1",
|
|
41
|
+
"chalk": "^5.6.2",
|
|
42
|
+
"cli-progress": "^3.12.0",
|
|
43
|
+
"commander": "^14.0.2",
|
|
44
|
+
"inquirer": "^13.1.0",
|
|
45
|
+
"ora": "^9.0.0",
|
|
46
|
+
"update-notifier": "^7.3.1",
|
|
47
|
+
"yaml": "^2.8.2"
|
|
48
|
+
}
|
|
49
|
+
}
|