clash-kit 1.0.2 → 1.0.4
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 +20 -0
- package/LICENSE.md +9 -0
- package/README.md +8 -20
- package/index.js +12 -6
- package/lib/commands/init.js +1 -1
- package/lib/commands/stop.js +2 -2
- package/lib/kernel.js +1 -1
- package/lib/subscription.js +61 -5
- package/lib/tun.js +1 -1
- package/package.json +5 -3
package/.gitignore
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-present, 荣顶 and wechat-bot contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Clash Kit
|
|
2
2
|
|
|
3
|
-
<img width="800" alt="clash-kit" src="https://github.com/user-attachments/assets/dd7dfd29-f59a-418b-8623-6ab08ece9ddb" />
|
|
4
|
-
|
|
5
3
|
一个基于 Node.js 的 Clash 命令行管理工具,旨在简化 Clash 的配置管理、订阅切换和节点测速等操作。
|
|
6
4
|
|
|
5
|
+
## 截图
|
|
6
|
+
|
|
7
|
+
<img width="1920" alt="image" src="https://github.com/user-attachments/assets/1183f778-62b0-4ac7-ab55-b821b66161f0" />
|
|
8
|
+
|
|
7
9
|
## 特性
|
|
8
10
|
|
|
9
11
|
- 🔄 **订阅管理**:支持添加、切换多个订阅源。
|
|
@@ -95,24 +97,10 @@ sudo ck tun on
|
|
|
95
97
|
| `ck sysproxy` (`sys`) | 设置系统代理 (on/off) | `ck sys on` |
|
|
96
98
|
| `ck tun` | 设置 TUN 模式 (on/off) | `sudo ck tun on` |
|
|
97
99
|
|
|
98
|
-
## 截图
|
|
99
|
-
|
|
100
|
-
<img width="2742" height="1994" alt="image" src="https://github.com/user-attachments/assets/1183f778-62b0-4ac7-ab55-b821b66161f0" />
|
|
101
|
-
|
|
102
|
-
## 目录结构
|
|
103
|
-
|
|
104
|
-
- `bin/`: CLI 入口文件
|
|
105
|
-
- `lib/`: 核心逻辑库
|
|
106
|
-
- `profiles/`: 存放下载的订阅配置文件
|
|
107
|
-
- `clash-meta`: Clash 核心二进制文件
|
|
108
|
-
- `config.yaml`: 当前生效的配置文件
|
|
109
|
-
|
|
110
|
-
## 注意事项
|
|
111
|
-
|
|
112
|
-
- 本工具依赖 `clash-meta` 二进制文件,请确保其与本工具在同一目录下(安装包已包含)。
|
|
113
|
-
- 测速功能依赖 Clash API,请确保 `clash start` 正在运行。
|
|
114
|
-
- 默认 API 地址为 `http://127.0.0.1:9090`。
|
|
115
100
|
|
|
116
101
|
## License
|
|
117
102
|
|
|
118
|
-
|
|
103
|
+
[MIT](./LICENSE).
|
|
104
|
+
|
|
105
|
+
开源不易,点赞鼓励!
|
|
106
|
+
点一个 Star⭐️ 支持我们~ 🌸Let's enjoy it!
|
package/index.js
CHANGED
|
@@ -11,14 +11,14 @@ const __filename = fileURLToPath(import.meta.url)
|
|
|
11
11
|
const __dirname = path.dirname(__filename)
|
|
12
12
|
|
|
13
13
|
// ---------------- 1. 配置项 ----------------
|
|
14
|
-
const CLASH_BIN_PATH = path.join(__dirname, 'clash-
|
|
14
|
+
const CLASH_BIN_PATH = path.join(__dirname, 'clash-kit') // 解压后的二进制文件路径
|
|
15
15
|
const CLASH_CONFIG_PATH = path.join(__dirname, 'config.yaml') // 配置文件路径
|
|
16
16
|
|
|
17
17
|
// ---------------- 2. 启动 Clash.Meta 进程 ----------------
|
|
18
18
|
function startClash() {
|
|
19
19
|
// 尝试停止已存在的进程
|
|
20
20
|
try {
|
|
21
|
-
execSync('pkill -f clash-
|
|
21
|
+
execSync('pkill -f clash-kit')
|
|
22
22
|
} catch (e) {
|
|
23
23
|
// 忽略错误,说明没有运行中的进程
|
|
24
24
|
}
|
|
@@ -65,7 +65,7 @@ async function cleanup() {
|
|
|
65
65
|
|
|
66
66
|
// 停止 Clash 进程
|
|
67
67
|
try {
|
|
68
|
-
execSync('pkill -f clash-
|
|
68
|
+
execSync('pkill -f clash-kit')
|
|
69
69
|
console.log('Clash 服务已停止')
|
|
70
70
|
} catch (e) {
|
|
71
71
|
// 进程可能已经停止
|
|
@@ -101,7 +101,7 @@ function setupExitHandlers() {
|
|
|
101
101
|
|
|
102
102
|
// ---------------- 5. 执行流程 ----------------
|
|
103
103
|
export function main() {
|
|
104
|
-
// 检查 clash-
|
|
104
|
+
// 检查 clash-kit 二进制文件是否存在
|
|
105
105
|
if (!fs.existsSync(CLASH_BIN_PATH)) {
|
|
106
106
|
return console.error(chalk.red('\n找不到 Clash.Meta 内核文件,请先运行 clash init 命令初始化内核!\n'))
|
|
107
107
|
}
|
|
@@ -118,7 +118,7 @@ export function main() {
|
|
|
118
118
|
|
|
119
119
|
console.log(chalk.green('\n代理服务已在后台启动✅'))
|
|
120
120
|
if (clashProcess.pid) {
|
|
121
|
-
console.log(
|
|
121
|
+
console.log(`进程名称:${chalk.yellow('clash-kit')} PID: ${chalk.yellow(clashProcess.pid)}`)
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
console.log(``)
|
|
@@ -126,7 +126,13 @@ export function main() {
|
|
|
126
126
|
console.log(`SOCKS5 Proxy: ${chalk.cyan(`127.0.0.1:${socks}`)}`)
|
|
127
127
|
console.log(`API: ${chalk.cyan.underline(getApiBase())}`)
|
|
128
128
|
console.log(``)
|
|
129
|
-
|
|
129
|
+
|
|
130
|
+
console.log(chalk.gray('运行日志文件: ' + path.join(__dirname, 'clash.log')))
|
|
131
|
+
console.log(chalk.blue('提示:如需查看运行状态可使用 clash status 命令'))
|
|
132
|
+
console.log(chalk.blue('如需停止代理可使用 clash stop 命令'))
|
|
133
|
+
console.log(chalk.gray('----------------------------------------'))
|
|
134
|
+
console.log(chalk.gray('如需切换系统代理模式可使用 clash sysproxy on/off 命令'))
|
|
135
|
+
console.log(chalk.gray('如需切换 TUN 模式可使用 clash tun on/off 命令'))
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
// 运行脚本
|
package/lib/commands/init.js
CHANGED
|
@@ -56,7 +56,7 @@ async function downloadResource(resource, targetDir) {
|
|
|
56
56
|
|
|
57
57
|
export async function init(options) {
|
|
58
58
|
const rootDir = path.join(__dirname, '../..')
|
|
59
|
-
const binName = process.platform === 'win32' ? 'clash-
|
|
59
|
+
const binName = process.platform === 'win32' ? 'clash-kit.exe' : 'clash-kit'
|
|
60
60
|
const binPath = path.join(rootDir, binName)
|
|
61
61
|
const configPath = path.join(rootDir, 'config.yaml')
|
|
62
62
|
|
package/lib/commands/stop.js
CHANGED
|
@@ -17,9 +17,9 @@ export async function stop() {
|
|
|
17
17
|
console.log('TUN 模式已关闭')
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// 使用 pkill 匹配进程名包含 clash-
|
|
20
|
+
// 使用 pkill 匹配进程名包含 clash-kit 的进程
|
|
21
21
|
spinner.text = '正在停止 Clash 服务...'
|
|
22
|
-
execSync('pkill -f clash-
|
|
22
|
+
execSync('pkill -f clash-kit')
|
|
23
23
|
spinner.succeed('Clash 服务已停止')
|
|
24
24
|
} catch (err) {
|
|
25
25
|
// pkill 如果没找到进程会抛出错误,这里捕获并提示
|
package/lib/kernel.js
CHANGED
|
@@ -52,7 +52,7 @@ export async function downloadClash(targetDir) {
|
|
|
52
52
|
const downloadUrl = `https://github.com/MetaCubeX/mihomo/releases/download/${version}/${name}-${version}.${urlExt}`
|
|
53
53
|
const tempFileName = `mihomo-temp.${urlExt}`
|
|
54
54
|
const tempPath = path.join(targetDir, tempFileName)
|
|
55
|
-
const targetBinName = `clash-
|
|
55
|
+
const targetBinName = `clash-kit${isWin ? '.exe' : ''}`
|
|
56
56
|
const targetBinPath = path.join(targetDir, targetBinName)
|
|
57
57
|
|
|
58
58
|
// 3. 下载文件
|
package/lib/subscription.js
CHANGED
|
@@ -4,6 +4,7 @@ import axios from 'axios'
|
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import ora from 'ora'
|
|
6
6
|
import { reloadConfig, isClashRunning } from './api.js'
|
|
7
|
+
import YAML from 'yaml'
|
|
7
8
|
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url)
|
|
9
10
|
const __dirname = path.dirname(__filename)
|
|
@@ -20,9 +21,46 @@ if (!fs.existsSync(PROFILES_DIR)) {
|
|
|
20
21
|
export async function downloadSubscription(url, name) {
|
|
21
22
|
const spinner = ora(`正在下载订阅 ${name}...`).start()
|
|
22
23
|
try {
|
|
23
|
-
const res = await axios.get(url, {
|
|
24
|
+
const res = await axios.get(url, {
|
|
25
|
+
responseType: 'text',
|
|
26
|
+
headers: {
|
|
27
|
+
'User-Agent': 'Clash/1.0.0', // 伪装成 Clash 客户端,通常能直接获取 YAML 格式
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
let content = res.data
|
|
32
|
+
|
|
33
|
+
// 尝试解析 YAML,如果不是对象或者看起来不像配置,尝试 Base64 解码
|
|
34
|
+
let isConfig = false
|
|
35
|
+
try {
|
|
36
|
+
const parsed = YAML.parse(content)
|
|
37
|
+
if (parsed && typeof parsed === 'object' && (parsed.proxies || parsed.Proxy || parsed.port)) {
|
|
38
|
+
isConfig = true
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.warn('订阅服务商返回的不是有效 YAML,尝试 Base64 解码...')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!isConfig) {
|
|
45
|
+
try {
|
|
46
|
+
// 尝试 Base64 解码
|
|
47
|
+
const decoded = Buffer.from(content, 'base64').toString('utf-8')
|
|
48
|
+
// 再次检查解码后是否为有效 YAML 配置
|
|
49
|
+
const parsedDecoded = YAML.parse(decoded)
|
|
50
|
+
if (
|
|
51
|
+
parsedDecoded &&
|
|
52
|
+
typeof parsedDecoded === 'object' &&
|
|
53
|
+
(parsedDecoded.proxies || parsedDecoded.Proxy || parsedDecoded.port)
|
|
54
|
+
) {
|
|
55
|
+
content = decoded
|
|
56
|
+
}
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.warn('Base64 解码失败,保留原始内容')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
24
62
|
const filePath = path.join(PROFILES_DIR, `${name}.yaml`)
|
|
25
|
-
fs.writeFileSync(filePath,
|
|
63
|
+
fs.writeFileSync(filePath, content)
|
|
26
64
|
spinner.succeed(`订阅 ${name} 下载成功`)
|
|
27
65
|
return filePath
|
|
28
66
|
} catch (err) {
|
|
@@ -51,9 +89,27 @@ export async function useProfile(name) {
|
|
|
51
89
|
|
|
52
90
|
const spinner = ora(`正在切换到配置 ${name}...`).start()
|
|
53
91
|
|
|
54
|
-
//
|
|
55
|
-
fs.
|
|
56
|
-
|
|
92
|
+
// 读取订阅配置
|
|
93
|
+
const subscriptionConfig = YAML.parse(fs.readFileSync(source, 'utf8'))
|
|
94
|
+
|
|
95
|
+
// 读取现有配置(如果存在)
|
|
96
|
+
let existingConfig = {}
|
|
97
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
98
|
+
existingConfig = YAML.parse(fs.readFileSync(CONFIG_PATH, 'utf8'))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 合并配置:保留用户自定义字段,更新订阅字段
|
|
102
|
+
const mergedConfig = {
|
|
103
|
+
...subscriptionConfig,
|
|
104
|
+
port: existingConfig['port'],
|
|
105
|
+
'bind-address': existingConfig['bind-address'],
|
|
106
|
+
'socks-port': existingConfig['socks-port'],
|
|
107
|
+
'allow-lan': existingConfig['allow-lan'],
|
|
108
|
+
// 其他需要保留的自定义字段...
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 写入合并后的配置
|
|
112
|
+
fs.writeFileSync(CONFIG_PATH, YAML.stringify(mergedConfig))
|
|
57
113
|
|
|
58
114
|
// 尝试热重载
|
|
59
115
|
if (await isClashRunning()) {
|
package/lib/tun.js
CHANGED
|
@@ -8,7 +8,7 @@ import { reloadConfig } from './api.js'
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url)
|
|
9
9
|
const __dirname = path.dirname(__filename)
|
|
10
10
|
const CONFIG_PATH = path.join(__dirname, '../config.yaml')
|
|
11
|
-
const BIN_PATH = path.join(__dirname, '../clash-
|
|
11
|
+
const BIN_PATH = path.join(__dirname, '../clash-kit')
|
|
12
12
|
|
|
13
13
|
export function checkTunPermissions() {
|
|
14
14
|
if (process.platform === 'win32') return true // Windows 需要管理员权限终端,难以通过文件属性判断
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clash-kit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A command-line interface for managing Clash configurations, subscriptions, and proxies.",
|
|
6
6
|
"bin": {
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"bin",
|
|
12
12
|
"lib",
|
|
13
|
-
"
|
|
14
|
-
"country.mmdb",
|
|
13
|
+
".gitignore",
|
|
15
14
|
"default.yaml",
|
|
15
|
+
"country.mmdb",
|
|
16
|
+
"index.js",
|
|
17
|
+
"LICENSE.md",
|
|
16
18
|
"package.json",
|
|
17
19
|
"README.md"
|
|
18
20
|
],
|