clash-kit 1.0.0
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 +110 -0
- package/bin/index.js +56 -0
- package/index.js +135 -0
- package/lib/api.js +106 -0
- package/lib/commands/init.js +58 -0
- package/lib/commands/proxy.js +40 -0
- package/lib/commands/start.js +22 -0
- package/lib/commands/status.js +85 -0
- package/lib/commands/stop.js +28 -0
- package/lib/commands/sub.js +78 -0
- package/lib/commands/sysproxy.js +24 -0
- package/lib/commands/test.js +70 -0
- package/lib/commands/tun.js +95 -0
- package/lib/kernel.js +152 -0
- package/lib/subscription.js +68 -0
- package/lib/sysnet.js +52 -0
- package/lib/sysproxy.js +111 -0
- package/lib/tun.js +126 -0
- package/package.json +43 -0
package/lib/tun.js
ADDED
|
@@ -0,0 +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-meta')
|
|
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
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clash-kit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A command-line interface for managing Clash configurations, subscriptions, and proxies.",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clash": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"index.js",
|
|
13
|
+
"package.json",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node bin/index.js start",
|
|
19
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"clash",
|
|
23
|
+
"proxy",
|
|
24
|
+
"cli",
|
|
25
|
+
"subscription",
|
|
26
|
+
"manager"
|
|
27
|
+
],
|
|
28
|
+
"author": "",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"packageManager": "pnpm@10.23.0",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@inquirer/prompts": "^8.1.0",
|
|
33
|
+
"@mihomo-party/sysproxy": "^2.0.8",
|
|
34
|
+
"adm-zip": "^0.5.16",
|
|
35
|
+
"axios": "^1.13.2",
|
|
36
|
+
"chalk": "^5.6.2",
|
|
37
|
+
"cli-progress": "^3.12.0",
|
|
38
|
+
"commander": "^14.0.2",
|
|
39
|
+
"inquirer": "^13.1.0",
|
|
40
|
+
"ora": "^9.0.0",
|
|
41
|
+
"yaml": "^2.8.2"
|
|
42
|
+
}
|
|
43
|
+
}
|