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 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
@@ -0,0 +1,5 @@
1
+ // npm 包主入口(用于 require('42cc'))
2
+ module.exports = {
3
+ version: require('./package.json').version,
4
+ install: require('./scripts/install.js').install
5
+ };
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"