42cc 0.2.1
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 +108 -0
- package/bin/42cc.js +128 -0
- package/index.js +5 -0
- package/package.json +43 -0
- package/scripts/install.js +377 -0
- package/scripts/release.sh +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# 42cc
|
|
2
|
+
|
|
3
|
+
Claude Code 模型配置工具 - npm 安装器
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
### npm
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 全局安装
|
|
11
|
+
npm install -g 42cc
|
|
12
|
+
|
|
13
|
+
# 或使用 npx
|
|
14
|
+
npx 42cc
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### bun
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 全局安装
|
|
21
|
+
bun install -g 42cc
|
|
22
|
+
|
|
23
|
+
# 或直接运行
|
|
24
|
+
bunx 42cc
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 其他包管理器
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# yarn
|
|
31
|
+
yarn global add 42cc
|
|
32
|
+
|
|
33
|
+
# pnpm
|
|
34
|
+
pnpm install -g 42cc
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 使用
|
|
38
|
+
|
|
39
|
+
安装完成后,应用会自动下载、安装并启动。
|
|
40
|
+
|
|
41
|
+
### macOS
|
|
42
|
+
|
|
43
|
+
- 自动安装到 `/Applications/42cc.app`
|
|
44
|
+
- 自动启动应用(首次需要确认 Gatekeeper 安全提示)
|
|
45
|
+
- 下次可从 Launchpad 或访达搜索 "42cc" 启动
|
|
46
|
+
|
|
47
|
+
### Windows
|
|
48
|
+
|
|
49
|
+
- 自动安装到 `%LocalAppData%\42cc`
|
|
50
|
+
- 自动创建开始菜单快捷方式
|
|
51
|
+
- 自动启动应用
|
|
52
|
+
- 下次可从开始菜单搜索 "42cc" 启动
|
|
53
|
+
|
|
54
|
+
## 系统要求
|
|
55
|
+
|
|
56
|
+
- **macOS**: 10.13 (High Sierra) 或更高
|
|
57
|
+
- **Windows**: Windows 10 或更高
|
|
58
|
+
- **Node.js**: 14.0.0 或更高
|
|
59
|
+
- **网络**: 需要访问 https://get.42plugin.com
|
|
60
|
+
|
|
61
|
+
## 注意事项
|
|
62
|
+
|
|
63
|
+
- **macOS**: 首次运行时需要在 Gatekeeper 安全提示中点击"打开"
|
|
64
|
+
- **Windows**: 如遇到 SmartScreen 提示,点击"仍要运行"
|
|
65
|
+
- 安装过程中可能需要管理员权限
|
|
66
|
+
|
|
67
|
+
## 其他安装方式
|
|
68
|
+
|
|
69
|
+
**注意**: npm 安装需要网络下载。如果网络受限或需要离线安装,请使用以下方式:
|
|
70
|
+
|
|
71
|
+
### Homebrew (macOS)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
brew tap 42ailab/42cc
|
|
75
|
+
brew install --cask 42cc
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 直接下载(离线可用)
|
|
79
|
+
|
|
80
|
+
访问 [https://get.42plugin.com/42cc/](https://get.42plugin.com/42cc/) 下载安装包。
|
|
81
|
+
|
|
82
|
+
**适用场景**:
|
|
83
|
+
- 网络受限环境
|
|
84
|
+
- 企业内网
|
|
85
|
+
- 离线安装
|
|
86
|
+
|
|
87
|
+
## 功能
|
|
88
|
+
|
|
89
|
+
42cc 是一款极简的 Claude Code 模型配置工具:
|
|
90
|
+
|
|
91
|
+
- 选择模型供应商
|
|
92
|
+
- 输入 API 密钥
|
|
93
|
+
- 一键完成配置
|
|
94
|
+
|
|
95
|
+
支持的模型供应商:
|
|
96
|
+
- GLM (智谱)
|
|
97
|
+
- MiniMax
|
|
98
|
+
- Anthropic 官方
|
|
99
|
+
- 自定义供应商
|
|
100
|
+
|
|
101
|
+
## 链接
|
|
102
|
+
|
|
103
|
+
- **主页**: https://42cc.42ailab.com
|
|
104
|
+
- **问题反馈**: https://cnb.cool/42ailab/42plugin/meta/-/issues
|
|
105
|
+
|
|
106
|
+
## LICENSE
|
|
107
|
+
|
|
108
|
+
Proprietary
|
package/bin/42cc.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
const VERSION_URL = 'https://get.42plugin.com/42cc/version.json';
|
|
10
|
+
|
|
11
|
+
function showHelp() {
|
|
12
|
+
console.log(`
|
|
13
|
+
42cc - Claude Code 模型配置工具
|
|
14
|
+
|
|
15
|
+
用法:
|
|
16
|
+
42cc 启动应用(仅 macOS)
|
|
17
|
+
42cc --help 显示帮助信息
|
|
18
|
+
42cc --version 显示版本信息
|
|
19
|
+
42cc --update 检查并更新到最新版本
|
|
20
|
+
|
|
21
|
+
安装位置:
|
|
22
|
+
macOS: /Applications/42cc.app
|
|
23
|
+
Windows: 请从开始菜单启动
|
|
24
|
+
|
|
25
|
+
更多信息: https://42ailab.com
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getVersion() {
|
|
30
|
+
const pkg = require('../package.json');
|
|
31
|
+
console.log(`v${pkg.version}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function launch() {
|
|
35
|
+
if (platform === 'darwin') {
|
|
36
|
+
try {
|
|
37
|
+
execSync('open -a 42cc', { stdio: 'inherit' });
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('错误: 未找到 42cc 应用');
|
|
40
|
+
console.error('请确保已将 42cc.app 移动到 /Applications 目录');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
} else if (platform === 'win32') {
|
|
44
|
+
console.log('Windows: 请从开始菜单启动 42cc');
|
|
45
|
+
console.log('或运行安装目录下的 42cc.exe');
|
|
46
|
+
} else {
|
|
47
|
+
console.error('不支持的平台');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 获取本地已安装的版本号
|
|
53
|
+
function getLocalVersion() {
|
|
54
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
55
|
+
|
|
56
|
+
let versionFile;
|
|
57
|
+
if (platform === 'darwin') {
|
|
58
|
+
versionFile = path.join(homeDir, '.42cc', 'version');
|
|
59
|
+
} else {
|
|
60
|
+
versionFile = path.join(process.env.APPDATA || homeDir, '42cc', 'version');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
return fs.readFileSync(versionFile, 'utf8').trim();
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 获取远程最新版本号
|
|
71
|
+
function fetchRemoteVersion() {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
https.get(VERSION_URL, (res) => {
|
|
74
|
+
let data = '';
|
|
75
|
+
res.on('data', (chunk) => data += chunk);
|
|
76
|
+
res.on('end', () => {
|
|
77
|
+
try {
|
|
78
|
+
const json = JSON.parse(data);
|
|
79
|
+
resolve(json.version);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
reject(new Error('解析版本信息失败'));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}).on('error', (err) => {
|
|
85
|
+
reject(new Error(`网络请求失败: ${err.message}`));
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 检查并更新
|
|
91
|
+
async function checkAndUpdate() {
|
|
92
|
+
console.log('检查更新中...\n');
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const localVersion = getLocalVersion();
|
|
96
|
+
const remoteVersion = await fetchRemoteVersion();
|
|
97
|
+
|
|
98
|
+
console.log(`本地版本: ${localVersion || '未安装'}`);
|
|
99
|
+
console.log(`最新版本: ${remoteVersion}`);
|
|
100
|
+
|
|
101
|
+
if (localVersion === remoteVersion) {
|
|
102
|
+
console.log('\n已是最新版本,无需更新。');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log('\n发现新版本,开始更新...\n');
|
|
107
|
+
|
|
108
|
+
// 调用 install.js 进行安装
|
|
109
|
+
const installScript = path.join(__dirname, '..', 'scripts', 'install.js');
|
|
110
|
+
require(installScript).install();
|
|
111
|
+
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(`更新失败: ${error.message}`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const arg = process.argv[2];
|
|
119
|
+
|
|
120
|
+
if (arg === '--help' || arg === '-h') {
|
|
121
|
+
showHelp();
|
|
122
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
123
|
+
getVersion();
|
|
124
|
+
} else if (arg === '--update' || arg === '-u') {
|
|
125
|
+
checkAndUpdate();
|
|
126
|
+
} else {
|
|
127
|
+
launch();
|
|
128
|
+
}
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "42cc",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Claude Code model configuration tool - Installer | Claude Code 模型配置工具 - 安装器",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"42cc": "./bin/42cc.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node scripts/install.js",
|
|
11
|
+
"test": "bash test.sh"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude-code",
|
|
15
|
+
"ai",
|
|
16
|
+
"model-configuration",
|
|
17
|
+
"42plugin",
|
|
18
|
+
"42ailab"
|
|
19
|
+
],
|
|
20
|
+
"author": "42ailab <email@huoshuiai.com>",
|
|
21
|
+
"license": "Proprietary",
|
|
22
|
+
"homepage": "https://42cc.42ailab.com",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/42ailab/homebrew-42cc.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://cnb.cool/42ailab/42plugin/meta/-/issues"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=14.0.0"
|
|
32
|
+
},
|
|
33
|
+
"os": [
|
|
34
|
+
"darwin",
|
|
35
|
+
"win32"
|
|
36
|
+
],
|
|
37
|
+
"files": [
|
|
38
|
+
"bin/",
|
|
39
|
+
"scripts/",
|
|
40
|
+
"index.js",
|
|
41
|
+
"README.md"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
|
|
9
|
+
const VERSION_URL = 'https://get.42plugin.com/42cc/version.json';
|
|
10
|
+
const INSTALL_DIR = path.join(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
// 检测包管理器
|
|
13
|
+
function getPackageManager() {
|
|
14
|
+
const userAgent = process.env.npm_config_user_agent || '';
|
|
15
|
+
if (userAgent.includes('bun')) return 'bun';
|
|
16
|
+
if (userAgent.includes('npm')) return 'npm';
|
|
17
|
+
if (userAgent.includes('yarn')) return 'yarn';
|
|
18
|
+
if (userAgent.includes('pnpm')) return 'pnpm';
|
|
19
|
+
return 'unknown';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 颜色输出
|
|
23
|
+
const colors = {
|
|
24
|
+
reset: '\x1b[0m',
|
|
25
|
+
green: '\x1b[32m',
|
|
26
|
+
yellow: '\x1b[33m',
|
|
27
|
+
blue: '\x1b[34m',
|
|
28
|
+
red: '\x1b[31m'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function log(msg, color = 'reset') {
|
|
32
|
+
console.log(`${colors[color]}${msg}${colors.reset}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getPlatform() {
|
|
36
|
+
const platform = process.platform;
|
|
37
|
+
if (platform === 'darwin') return 'macos';
|
|
38
|
+
if (platform === 'win32') return 'windows';
|
|
39
|
+
throw new Error(`不支持的平台: ${platform}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fetchJSON(url) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
https.get(url, (res) => {
|
|
45
|
+
let data = '';
|
|
46
|
+
res.on('data', (chunk) => data += chunk);
|
|
47
|
+
res.on('end', () => {
|
|
48
|
+
try {
|
|
49
|
+
resolve(JSON.parse(data));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
reject(e);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}).on('error', reject);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function downloadFile(url, dest, maxRedirects = 5) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
if (maxRedirects <= 0) {
|
|
61
|
+
return reject(new Error('\u91cd\u5b9a\u5411\u6b21\u6570\u8fc7\u591a'));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const file = fs.createWriteStream(dest);
|
|
65
|
+
https.get(url, (res) => {
|
|
66
|
+
// \u5904\u7406\u91cd\u5b9a\u5411
|
|
67
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
68
|
+
file.close();
|
|
69
|
+
fs.unlink(dest, () => {});
|
|
70
|
+
return downloadFile(res.headers.location, dest, maxRedirects - 1)
|
|
71
|
+
.then(resolve)
|
|
72
|
+
.catch(reject);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// \u68c0\u67e5\u72b6\u6001\u7801
|
|
76
|
+
if (res.statusCode !== 200) {
|
|
77
|
+
file.close();
|
|
78
|
+
fs.unlink(dest, () => {});
|
|
79
|
+
return reject(new Error(`\u4e0b\u8f7d\u5931\u8d25\uff0c\u72b6\u6001\u7801: ${res.statusCode}`));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const total = parseInt(res.headers['content-length'], 10);
|
|
83
|
+
let downloaded = 0;
|
|
84
|
+
|
|
85
|
+
res.on('data', (chunk) => {
|
|
86
|
+
downloaded += chunk.length;
|
|
87
|
+
const percent = ((downloaded / total) * 100).toFixed(1);
|
|
88
|
+
process.stdout.write(`\r\u4e0b\u8f7d\u8fdb\u5ea6: ${percent}%`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
res.pipe(file);
|
|
92
|
+
file.on('finish', () => {
|
|
93
|
+
file.close();
|
|
94
|
+
process.stdout.write('\n');
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
}).on('error', (err) => {
|
|
98
|
+
fs.unlink(dest, () => {});
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function verifySHA256(filePath, expectedHash) {
|
|
105
|
+
const hash = crypto.createHash('sha256');
|
|
106
|
+
const data = fs.readFileSync(filePath);
|
|
107
|
+
hash.update(data);
|
|
108
|
+
const actualHash = hash.digest('hex');
|
|
109
|
+
return actualHash === expectedHash;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 保存已安装的版本号到本地文件
|
|
113
|
+
function saveInstalledVersion(version) {
|
|
114
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
115
|
+
const platform = process.platform;
|
|
116
|
+
|
|
117
|
+
let versionDir;
|
|
118
|
+
if (platform === 'darwin') {
|
|
119
|
+
versionDir = path.join(homeDir, '.42cc');
|
|
120
|
+
} else {
|
|
121
|
+
versionDir = path.join(process.env.APPDATA || homeDir, '42cc');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const versionFile = path.join(versionDir, 'version');
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
if (!fs.existsSync(versionDir)) {
|
|
128
|
+
fs.mkdirSync(versionDir, { recursive: true });
|
|
129
|
+
}
|
|
130
|
+
fs.writeFileSync(versionFile, version, 'utf8');
|
|
131
|
+
} catch (e) {
|
|
132
|
+
// 写入版本文件失败不影响安装流程
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// macOS 自动安装
|
|
137
|
+
async function autoInstallMacOS(dmgPath) {
|
|
138
|
+
log('\n开始自动安装...', 'yellow');
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
// 1. 挂载 DMG
|
|
142
|
+
log('1. 挂载 DMG...', 'blue');
|
|
143
|
+
execSync(`hdiutil attach "${dmgPath}" -nobrowse -quiet`);
|
|
144
|
+
|
|
145
|
+
// 2. 找到挂载点
|
|
146
|
+
const volumes = fs.readdirSync('/Volumes');
|
|
147
|
+
const mountPoint = volumes.find(v => v.includes('42cc'));
|
|
148
|
+
if (!mountPoint) {
|
|
149
|
+
throw new Error('未找到挂载点');
|
|
150
|
+
}
|
|
151
|
+
const mountPath = `/Volumes/${mountPoint}`;
|
|
152
|
+
|
|
153
|
+
// 3. 复制到 Applications
|
|
154
|
+
log('2. 安装到 Applications...', 'blue');
|
|
155
|
+
const appPath = `${mountPath}/42cc.app`;
|
|
156
|
+
const targetPath = '/Applications/42cc.app';
|
|
157
|
+
|
|
158
|
+
// 检查是否已存在
|
|
159
|
+
if (fs.existsSync(targetPath)) {
|
|
160
|
+
log('⚠ 检测到已安装的版本,将替换...', 'yellow');
|
|
161
|
+
try {
|
|
162
|
+
execSync(`rm -rf "${targetPath}"`);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
log('需要管理员权限删除旧版本,请输入密码:', 'yellow');
|
|
165
|
+
execSync(`sudo rm -rf "${targetPath}"`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 复制应用
|
|
170
|
+
try {
|
|
171
|
+
execSync(`cp -R "${appPath}" /Applications/`);
|
|
172
|
+
log('✓ 安装成功', 'green');
|
|
173
|
+
} catch (e) {
|
|
174
|
+
log('需要管理员权限,请输入密码:', 'yellow');
|
|
175
|
+
execSync(`sudo cp -R "${appPath}" /Applications/`);
|
|
176
|
+
log('✓ 安装成功', 'green');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 4. 卸载 DMG
|
|
180
|
+
log('3. 清理临时文件...', 'blue');
|
|
181
|
+
execSync(`hdiutil detach "${mountPath}" -quiet`);
|
|
182
|
+
|
|
183
|
+
// 5. 启动应用
|
|
184
|
+
log('4. 启动应用...', 'blue');
|
|
185
|
+
log('\n⚠ Gatekeeper 安全提示:', 'yellow');
|
|
186
|
+
log('即将弹出安全确认窗口,请点击"打开"', 'yellow');
|
|
187
|
+
log('(此提示仅首次运行时出现)\n', 'blue');
|
|
188
|
+
|
|
189
|
+
execSync('open -a 42cc');
|
|
190
|
+
|
|
191
|
+
log('\n✓ 安装完成', 'green');
|
|
192
|
+
log('═══════════════════════════════════════', 'blue');
|
|
193
|
+
log('');
|
|
194
|
+
log('✓ 安装位置: /Applications/42cc.app', 'green');
|
|
195
|
+
log('✓ 应用已启动并显示在菜单栏', 'green');
|
|
196
|
+
log('✓ 下次可从 Launchpad 或访达搜索 "42cc" 启动', 'green');
|
|
197
|
+
log('');
|
|
198
|
+
log('═══════════════════════════════════════\n', 'blue');
|
|
199
|
+
|
|
200
|
+
return true;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
log(`\n自动安装失败: ${error.message}`, 'red');
|
|
203
|
+
log('将回退到手动安装模式...\n', 'yellow');
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Windows 自动安装(绿色单文件应用)
|
|
209
|
+
async function autoInstallWindows(exePath) {
|
|
210
|
+
log('\n开始自动安装...', 'yellow');
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// 42cc.exe 是单文件应用,直接运行即可
|
|
214
|
+
// 不需要安装,但可以复制到用户目录方便使用
|
|
215
|
+
|
|
216
|
+
const userProfile = process.env.USERPROFILE || process.env.HOME;
|
|
217
|
+
const installDir = path.join(userProfile, 'AppData', 'Local', '42cc');
|
|
218
|
+
const targetPath = path.join(installDir, '42cc.exe');
|
|
219
|
+
|
|
220
|
+
log('1. 安装到用户目录...', 'blue');
|
|
221
|
+
|
|
222
|
+
// 创建目录
|
|
223
|
+
if (!fs.existsSync(installDir)) {
|
|
224
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 复制可执行文件
|
|
228
|
+
fs.copyFileSync(exePath, targetPath);
|
|
229
|
+
log(`✓ 已安装到: ${installDir}`, 'green');
|
|
230
|
+
|
|
231
|
+
// 创建开始菜单快捷方式
|
|
232
|
+
log('\n2. 创建开始菜单快捷方式...', 'blue');
|
|
233
|
+
try {
|
|
234
|
+
const shell = require('child_process');
|
|
235
|
+
const startMenuDir = path.join(userProfile, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs');
|
|
236
|
+
const shortcutPath = path.join(startMenuDir, '42cc.lnk');
|
|
237
|
+
|
|
238
|
+
// 使用 PowerShell 创建快捷方式
|
|
239
|
+
const psScript = `
|
|
240
|
+
$WshShell = New-Object -ComObject WScript.Shell
|
|
241
|
+
$Shortcut = $WshShell.CreateShortcut("${shortcutPath.replace(/\\/g, '\\\\')}")
|
|
242
|
+
$Shortcut.TargetPath = "${targetPath.replace(/\\/g, '\\\\')}"
|
|
243
|
+
$Shortcut.WorkingDirectory = "${installDir.replace(/\\/g, '\\\\')}"
|
|
244
|
+
$Shortcut.Description = "42cc - Claude Code 模型配置工具"
|
|
245
|
+
$Shortcut.Save()
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
shell.execSync(`powershell -Command "${psScript}"`, { stdio: 'ignore' });
|
|
249
|
+
log('✓ 已添加到开始菜单', 'green');
|
|
250
|
+
} catch (e) {
|
|
251
|
+
log('⚠ 创建开始菜单快捷方式失败,可手动创建', 'yellow');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 直接运行应用
|
|
255
|
+
log('\n3. 启动应用...', 'blue');
|
|
256
|
+
try {
|
|
257
|
+
execSync(`start "" "${targetPath}"`, { stdio: 'ignore' });
|
|
258
|
+
|
|
259
|
+
log('\n✓ 安装完成', 'green');
|
|
260
|
+
log('═══════════════════════════════════════', 'blue');
|
|
261
|
+
log('');
|
|
262
|
+
log('✓ 安装位置: ' + installDir, 'green');
|
|
263
|
+
log('✓ 应用已启动并显示在系统托盘', 'green');
|
|
264
|
+
log('✓ 下次可从开始菜单搜索 "42cc" 启动', 'green');
|
|
265
|
+
log('');
|
|
266
|
+
log('═══════════════════════════════════════\n', 'blue');
|
|
267
|
+
} catch (e) {
|
|
268
|
+
log('\n✓ 安装完成', 'green');
|
|
269
|
+
log('═══════════════════════════════════════', 'blue');
|
|
270
|
+
log('');
|
|
271
|
+
log('✓ 安装位置: ' + installDir, 'green');
|
|
272
|
+
log('✓ 可从开始菜单搜索 "42cc" 启动', 'green');
|
|
273
|
+
log('⚠ 自动启动失败,请手动打开', 'yellow');
|
|
274
|
+
log('');
|
|
275
|
+
log('═══════════════════════════════════════\n', 'blue');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return true;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
log(`\n自动安装失败: ${error.message}`, 'red');
|
|
281
|
+
log('将回退到手动安装模式...\n', 'yellow');
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function install() {
|
|
287
|
+
try {
|
|
288
|
+
const pm = getPackageManager();
|
|
289
|
+
|
|
290
|
+
log('\n42cc 安装程序', 'blue');
|
|
291
|
+
log('================\n', 'blue');
|
|
292
|
+
|
|
293
|
+
// 检测平台和包管理器
|
|
294
|
+
const platform = getPlatform();
|
|
295
|
+
log(`检测到平台: ${platform}`, 'green');
|
|
296
|
+
if (pm !== 'unknown') {
|
|
297
|
+
log(`包管理器: ${pm}`, 'blue');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 获取版本信息
|
|
301
|
+
log('获取版本信息...', 'yellow');
|
|
302
|
+
const versionInfo = await fetchJSON(VERSION_URL);
|
|
303
|
+
const { version, [platform]: platformInfo } = versionInfo;
|
|
304
|
+
|
|
305
|
+
if (!platformInfo) {
|
|
306
|
+
throw new Error(`未找到 ${platform} 平台的下载信息`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
log(`版本: ${version}`, 'green');
|
|
310
|
+
|
|
311
|
+
// 下载文件
|
|
312
|
+
const ext = platform === 'macos' ? 'dmg' : 'exe';
|
|
313
|
+
const fileName = `42cc-${version}.${ext}`;
|
|
314
|
+
const filePath = path.join(INSTALL_DIR, fileName);
|
|
315
|
+
|
|
316
|
+
log(`\n开始下载: ${fileName}`, 'yellow');
|
|
317
|
+
log(`下载地址: ${platformInfo.url}`, 'blue');
|
|
318
|
+
await downloadFile(platformInfo.url, filePath);
|
|
319
|
+
|
|
320
|
+
// 验证 SHA256
|
|
321
|
+
log('\n验证文件完整性...', 'yellow');
|
|
322
|
+
if (!verifySHA256(filePath, platformInfo.sha256)) {
|
|
323
|
+
throw new Error('文件校验失败,请重新安装');
|
|
324
|
+
}
|
|
325
|
+
log('校验通过', 'green');
|
|
326
|
+
|
|
327
|
+
// 自动安装
|
|
328
|
+
log('\n================', 'blue');
|
|
329
|
+
log('下载完成!正在安装...', 'green');
|
|
330
|
+
log('================\n', 'blue');
|
|
331
|
+
|
|
332
|
+
// 默认启用自动安装
|
|
333
|
+
let success = false;
|
|
334
|
+
if (platform === 'macos') {
|
|
335
|
+
success = await autoInstallMacOS(filePath);
|
|
336
|
+
} else {
|
|
337
|
+
success = await autoInstallWindows(filePath);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 保存已安装的版本号
|
|
341
|
+
if (success) {
|
|
342
|
+
saveInstalledVersion(version);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 如果自动安装失败,提供手动安装指引
|
|
346
|
+
if (!success) {
|
|
347
|
+
log('\n手动安装步骤:', 'yellow');
|
|
348
|
+
log('================', 'blue');
|
|
349
|
+
|
|
350
|
+
if (platform === 'macos') {
|
|
351
|
+
log(`1. 双击打开: ${filePath}`, 'green');
|
|
352
|
+
log('2. 将 42cc.app 拖到 Applications 文件夹', 'green');
|
|
353
|
+
log('3. 从 Launchpad 或访达搜索 "42cc" 启动', 'green');
|
|
354
|
+
} else {
|
|
355
|
+
log(`1. 安装文件已下载到: ${filePath}`, 'green');
|
|
356
|
+
log('2. 双击运行该文件进行安装', 'green');
|
|
357
|
+
log('3. 安装完成后从开始菜单搜索 "42cc" 启动', 'green');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
log('================\n', 'blue');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
log('\n更多信息: https://42ailab.com\n', 'blue');
|
|
364
|
+
|
|
365
|
+
} catch (error) {
|
|
366
|
+
log(`\n错误: ${error.message}`, 'red');
|
|
367
|
+
log('\n如需帮助,请访问: https://cnb.cool/42ailab/42plugin/meta/-/issues\n', 'yellow');
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 仅在 postinstall 时运行
|
|
373
|
+
if (require.main === module) {
|
|
374
|
+
install();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = { install };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# 42cc npm 包自动发布脚本
|
|
4
|
+
# 功能:同步版本、发布到 npm、更新 Homebrew Cask
|
|
5
|
+
#
|
|
6
|
+
# 用法: ./scripts/release.sh [version]
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# 颜色输出
|
|
12
|
+
RED='\033[0;31m'
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
BLUE='\033[0;34m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
19
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
20
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
21
|
+
log_step() { echo -e "${BLUE}>>${NC} $1"; }
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
24
|
+
NPM_PKG_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
25
|
+
|
|
26
|
+
cd "$NPM_PKG_DIR"
|
|
27
|
+
|
|
28
|
+
# 获取版本号
|
|
29
|
+
if [ -n "$1" ]; then
|
|
30
|
+
VERSION="$1"
|
|
31
|
+
else
|
|
32
|
+
# 从 version.json 读取
|
|
33
|
+
log_info "从 CDN 获取版本信息..."
|
|
34
|
+
VERSION=$(curl -s "https://get.42plugin.com/42cc/version.json" | jq -r '.version')
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
log_info "版本: ${VERSION}"
|
|
38
|
+
|
|
39
|
+
# 检查必要工具
|
|
40
|
+
log_step "检查工具..."
|
|
41
|
+
command -v npm >/dev/null 2>&1 || { log_error "未找到 npm"; exit 1; }
|
|
42
|
+
command -v jq >/dev/null 2>&1 || { log_error "未找到 jq"; exit 1; }
|
|
43
|
+
command -v gh >/dev/null 2>&1 || log_warn "未找到 gh (GitHub CLI),跳过 GitHub Release"
|
|
44
|
+
|
|
45
|
+
# 更新 package.json 版本
|
|
46
|
+
log_step "更新 package.json 版本..."
|
|
47
|
+
jq --arg v "$VERSION" '.version = $v' package.json > package.json.tmp
|
|
48
|
+
mv package.json.tmp package.json
|
|
49
|
+
log_info "package.json 版本已更新为 ${VERSION}"
|
|
50
|
+
|
|
51
|
+
# 发布到 npm
|
|
52
|
+
log_step "发布到 npm..."
|
|
53
|
+
read -p "是否发布到 npm? (y/N) " -n 1 -r
|
|
54
|
+
echo
|
|
55
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
56
|
+
npm publish
|
|
57
|
+
log_info "npm 发布成功: 42cc@${VERSION}"
|
|
58
|
+
else
|
|
59
|
+
log_warn "跳过 npm 发布"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# 创建 GitHub Release(可选)
|
|
63
|
+
if command -v gh >/dev/null 2>&1; then
|
|
64
|
+
log_step "创建 GitHub Release..."
|
|
65
|
+
read -p "是否创建 GitHub Release? (y/N) " -n 1 -r
|
|
66
|
+
echo
|
|
67
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
68
|
+
gh release create "v${VERSION}" \
|
|
69
|
+
--repo "42ailab/42cc" \
|
|
70
|
+
--title "42cc v${VERSION}" \
|
|
71
|
+
--notes "## 安装方式
|
|
72
|
+
|
|
73
|
+
**npm:**
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
npm install -g 42cc
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
**Homebrew:**
|
|
79
|
+
\`\`\`bash
|
|
80
|
+
brew tap 42ailab/42cc
|
|
81
|
+
brew install --cask 42cc
|
|
82
|
+
\`\`\`
|
|
83
|
+
" || log_warn "Release 可能已存在"
|
|
84
|
+
log_info "GitHub Release 创建完成"
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# 提交版本更新
|
|
89
|
+
log_step "提交版本更新..."
|
|
90
|
+
git add package.json
|
|
91
|
+
git commit -m "chore: bump version to ${VERSION}" || log_warn "无需提交"
|
|
92
|
+
git tag "npm-v${VERSION}" || log_warn "Tag 已存在"
|
|
93
|
+
|
|
94
|
+
log_info "===================================="
|
|
95
|
+
log_info "发布完成!"
|
|
96
|
+
log_info "===================================="
|
|
97
|
+
log_info "版本: ${VERSION}"
|
|
98
|
+
log_info "npm: https://www.npmjs.com/package/42cc"
|
|
99
|
+
log_info "Homebrew: brew install --cask 42ailab/42cc/42cc"
|