base_parts_ai 1.0.52 → 1.0.53
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 +32 -2
- package/bin/jcc.js +17 -142
- package/bin/jcx.js +33 -14
- package/bin/postinstall.js +1 -0
- package/bin/weds.js +30 -0
- package/lib/ai_utils.js +39 -9
- package/lib/claude_cli_common.js +183 -0
- package/lib/setapi_cc.js +13 -6
- package/main.js +169 -2
- package/package.json +13 -3
- package/.claude/settings.local.json +0 -28
- package/.eslintrc.js +0 -261
- package/AGENTS.md +0 -42
- package/TODO.md +0 -1
- package/test_home/.claude/jcc.json +0 -3
- package/test_home/.claude/settings.json +0 -19
- package/test_home/.claude.json +0 -4
- package/test_home/.codex/AGENTS.md +0 -3
- package/test_home/.codex/auth.json +0 -8
- package/test_home/.codex/config.toml +0 -9
- package/test_jcc.js +0 -548
- package/test_jcx.js +0 -489
package/README.md
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
|
|
2
|
-
##
|
|
3
|
-
|
|
2
|
+
## npx 运行
|
|
3
|
+
|
|
4
|
+
已发布到 npm 仓库后,可以直接运行入口命令:
|
|
5
|
+
|
|
6
|
+
```powershell
|
|
7
|
+
npx base_parts_ai
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
无参数时会让用户选择 `jcc`、`jcx` 或 `weds`,再继续执行对应配置流程。
|
|
11
|
+
|
|
12
|
+
也可以明确指定工具,适合脚本或需要带参数的场景:
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
npx base_parts_ai jcc
|
|
16
|
+
npx base_parts_ai jcx
|
|
17
|
+
npx base_parts_ai weds
|
|
18
|
+
npx base_parts_ai jcc setapi
|
|
19
|
+
npx base_parts_ai jcx setapi
|
|
20
|
+
npx base_parts_ai weds setapi
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
入口级帮助和版本:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
npx base_parts_ai --help
|
|
27
|
+
npx base_parts_ai --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
通过 `base_parts_ai` 入口执行时,会屏蔽 `base_parts_ai` 自身自动升级;`jcc`、`jcx`、`weds` 单独运行行为保持不变。
|
|
31
|
+
|
|
32
|
+
## 下载发布的包
|
|
33
|
+
https://registry.npmjs.org/base_parts_ai/-/base_parts_ai-1.0.45.tgz
|
|
4
34
|
|
|
5
35
|
## 然后放在jdwfiles目录下
|
|
6
36
|
https://jdwfiles.oss-cn-hangzhou.aliyuncs.com/npm_pkg/base_parts_ai-1.0.34.tgz
|
package/bin/jcc.js
CHANGED
|
@@ -1,155 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var os = require('os');
|
|
5
|
-
var path = require('path');
|
|
6
|
-
// commander v14+ 改为具名导出,需用 Command 类实例化
|
|
7
|
-
var { Command } = require('commander');
|
|
8
|
-
var program = new Command();
|
|
3
|
+
'use strict';
|
|
9
4
|
|
|
5
|
+
var claudeCli = require('../lib/claude_cli_common');
|
|
10
6
|
|
|
11
7
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param {string}
|
|
14
|
-
* @returns {*} 配置段
|
|
15
|
-
*/
|
|
16
|
-
function getBuildCfg(cmd) {
|
|
17
|
-
// 配置路径
|
|
18
|
-
if (!cmd.config) {
|
|
19
|
-
cmd.config = process.cwd() + "/jai_cfg.json";
|
|
20
|
-
}
|
|
21
|
-
cmd.config = path.resolve(cmd.config);
|
|
22
|
-
|
|
23
|
-
// 读入配置
|
|
24
|
-
// console.log("Read jai_cfg.json ...");
|
|
25
|
-
var buildCfg = {}
|
|
26
|
-
try {
|
|
27
|
-
// console.log("[Config path] " + cmd.config);
|
|
28
|
-
buildCfg = JSON.parse(jutils.jsonTrim(fs.readFileSync(cmd.config, "utf8")));
|
|
29
|
-
} catch (error) {
|
|
30
|
-
// console.log("[Notice] not found jai_cfg.json");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// 详情输出
|
|
34
|
-
buildCfg.details = cmd.details;
|
|
35
|
-
|
|
36
|
-
// 缺省路径
|
|
37
|
-
if (!buildCfg.rootPath) {
|
|
38
|
-
buildCfg.rootPath = process.cwd();
|
|
39
|
-
}
|
|
40
|
-
buildCfg.rootPath = path.resolve(buildCfg.rootPath);
|
|
41
|
-
// console.log("[Root path] " + buildCfg.rootPath);
|
|
42
|
-
|
|
43
|
-
// 从 npm 全局目录下的 @anthropic-ai/claude-code/package.json 提取 Claude 版本号
|
|
44
|
-
try {
|
|
45
|
-
// 通过子进程执行 npm root -g 获取全局包根目录,兼容不同系统安装位置
|
|
46
|
-
var npmRootG = require('child_process').execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
47
|
-
var claudePkgPath = path.join(npmRootG, '@anthropic-ai', 'claude-code', 'package.json');
|
|
48
|
-
var claudePkg = JSON.parse(fs.readFileSync(claudePkgPath, 'utf8'));
|
|
49
|
-
buildCfg.claudeVersion = claudePkg.version || '';
|
|
50
|
-
} catch (e) {
|
|
51
|
-
buildCfg.claudeVersion = '';
|
|
52
|
-
}
|
|
53
|
-
console.log("[Claude version] " + (buildCfg.claudeVersion || '(未找到)'));
|
|
54
|
-
|
|
55
|
-
// Claude 配置文件路径(跨平台支持)
|
|
56
|
-
// 支持环境变量 JCC_TEST_HOME 覆盖 homedir,方便测试时隔离环境
|
|
57
|
-
var homeDir = process.env.JCC_TEST_HOME || os.homedir();
|
|
58
|
-
// ~/.claude.json 全局配置
|
|
59
|
-
buildCfg.claudeJsonPath = path.join(homeDir, '.claude.json');
|
|
60
|
-
// ~/.claude/settings.json Claude Code 设置文件
|
|
61
|
-
buildCfg.claudeSettingsPath = path.join(homeDir, '.claude', 'settings.json');
|
|
62
|
-
// ~/.claude/jcc.json 自定义渠道Key存储文件
|
|
63
|
-
buildCfg.jccJsonPath = path.join(homeDir, '.claude', 'jcc.json');
|
|
64
|
-
|
|
65
|
-
// 确保 ~/.claude 目录存在,防止未安装 Claude 时崩溃
|
|
66
|
-
var claudeDir = path.join(homeDir, '.claude');
|
|
67
|
-
if (!fs.existsSync(claudeDir)) {
|
|
68
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
69
|
-
console.log('[Init] 已创建目录: ' + claudeDir);
|
|
70
|
-
}
|
|
71
|
-
// 确保 settings.json 存在(空对象兜底)
|
|
72
|
-
if (!fs.existsSync(buildCfg.claudeSettingsPath)) {
|
|
73
|
-
fs.writeFileSync(buildCfg.claudeSettingsPath, '{}', 'utf8');
|
|
74
|
-
console.log('[Init] 已创建文件: ' + buildCfg.claudeSettingsPath);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return buildCfg;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* 动态加载 lib 下的命令模块(热更新)
|
|
82
|
-
* @param {string} libName lib目录下的文件名(不含.js)
|
|
83
|
-
* @returns {Function} 导出的模块函数
|
|
84
|
-
*/
|
|
85
|
-
function loadLib(libName) {
|
|
86
|
-
var libPath = path.resolve(__dirname, '../lib/' + libName + '.js');
|
|
87
|
-
// 清除 require 缓存,实现热更新
|
|
88
|
-
delete require.cache[require.resolve(libPath)];
|
|
89
|
-
return require(libPath);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 检查系统环境变量中是否存在会干扰 Claude 配置的变量
|
|
94
|
-
* 若存在 ANTHROPIC_AUTH_TOKEN 或 ANTHROPIC_BASE_URL,则红色警告并等待用户确认
|
|
8
|
+
* 运行 jcc CLI,可供命令行直接执行或被 base_parts_ai require 调用
|
|
9
|
+
* @param {Array<string>} args 用户参数,不包含 node 和脚本路径
|
|
95
10
|
* @returns {Promise<void>}
|
|
96
11
|
*/
|
|
97
|
-
async function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log('\x1b[31m');
|
|
106
|
-
console.log('⚠️ 警告:检测到以下系统环境变量,可能导致工具配置无效!');
|
|
107
|
-
conflictVars.forEach(function (name) {
|
|
108
|
-
console.log(' ' + name + ' = ' + process.env[name]);
|
|
109
|
-
});
|
|
110
|
-
console.log('\x1b[0m');
|
|
111
|
-
|
|
112
|
-
// 等待用户按回车确认后继续
|
|
113
|
-
await new Promise(function (resolve) {
|
|
114
|
-
var rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
115
|
-
rl.question('按回车键继续...', function () {
|
|
116
|
-
rl.close();
|
|
117
|
-
resolve();
|
|
118
|
-
});
|
|
12
|
+
async function runCli(args) {
|
|
13
|
+
await claudeCli.runClaudeCli(args, {
|
|
14
|
+
cliName: 'jcc',
|
|
15
|
+
cacheFileName: 'jcc.json',
|
|
16
|
+
noticeType: 'default',
|
|
17
|
+
getChannels: function (utils) {
|
|
18
|
+
return utils.API_CHANNELS;
|
|
19
|
+
},
|
|
119
20
|
});
|
|
120
21
|
}
|
|
121
22
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
*/
|
|
127
|
-
async function runCommand(libName, opts) {
|
|
128
|
-
// 检查系统环境变量冲突,有冲突则红色警告并等待确认
|
|
129
|
-
await checkEnvConflict();
|
|
130
|
-
var buildCfg = getBuildCfg(opts);
|
|
131
|
-
// 从服务端拉取渠道配置(含 jcc 自动升级检查)
|
|
132
|
-
var utils = loadLib('ai_utils');
|
|
133
|
-
await utils.fetchConfig(buildCfg);
|
|
134
|
-
// 显示公告,有公告时等待用户按回车继续
|
|
135
|
-
await utils.showNotice();
|
|
136
|
-
// 执行业务模块
|
|
137
|
-
await loadLib(libName)(opts, buildCfg);
|
|
138
|
-
console.log('\n✅ 完成!重启 Claude 后生效。');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// 命令:setapi —— 选择 AI 渠道,修改 Claude settings.json 中的 ANTHROPIC_BASE_URL
|
|
142
|
-
program
|
|
143
|
-
.command('setapi')
|
|
144
|
-
.action(async function () {
|
|
145
|
-
await runCommand('setapi_cc', this.opts());
|
|
23
|
+
if (require.main === module) {
|
|
24
|
+
runCli(process.argv.slice(2)).catch(function (err) {
|
|
25
|
+
console.error('jcc 执行失败:', err.message);
|
|
26
|
+
process.exit(1);
|
|
146
27
|
});
|
|
147
|
-
|
|
148
|
-
// 若未传入任何子命令,默认执行 setapi
|
|
149
|
-
if (process.argv.length <= 2) {
|
|
150
|
-
process.argv.push('setapi');
|
|
151
28
|
}
|
|
152
29
|
|
|
153
|
-
|
|
154
|
-
program.parse(process.argv);
|
|
155
|
-
|
|
30
|
+
module.exports = runCli;
|
package/bin/jcx.js
CHANGED
|
@@ -7,7 +7,6 @@ var os = require('os');
|
|
|
7
7
|
var path = require('path');
|
|
8
8
|
var { execSync } = require('child_process');
|
|
9
9
|
var { Command } = require('commander');
|
|
10
|
-
var program = new Command();
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* 构建 Codex 配置路径,并确保 ~/.codex 目录存在
|
|
@@ -70,20 +69,40 @@ async function runCommand(libName, opts) {
|
|
|
70
69
|
console.log('\n✅ 完成!重启 Codex 后生效。');
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
/**
|
|
73
|
+
* 运行 jcx CLI,可供命令行直接执行或被 base_parts_ai require 调用
|
|
74
|
+
* @param {Array<string>} args 用户参数,不包含 node 和脚本路径
|
|
75
|
+
* @returns {Promise<void>}
|
|
76
|
+
*/
|
|
77
|
+
async function runCli(args) {
|
|
78
|
+
var cliArgs = (args || []).slice();
|
|
79
|
+
var program = new Command();
|
|
79
80
|
|
|
80
|
-
program
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
program
|
|
82
|
+
.name('jcx')
|
|
83
|
+
.description('Codex 配置工具');
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
program
|
|
86
|
+
.command('setapi')
|
|
87
|
+
.description('选择 Codex AI 渠道并写入 ~/.codex 配置')
|
|
88
|
+
.action(async function () {
|
|
89
|
+
await runCommand('setapi_cx', this.opts());
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 若未传入任何子命令,默认执行 setapi
|
|
93
|
+
if (cliArgs.length <= 0) {
|
|
94
|
+
cliArgs.push('setapi');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 使用 from:user 避免读取或修改全局 process.argv,便于被主入口 require 调用
|
|
98
|
+
await program.parseAsync(cliArgs, { from: 'user' });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (require.main === module) {
|
|
102
|
+
runCli(process.argv.slice(2)).catch(function (err) {
|
|
103
|
+
console.error('jcx 执行失败:', err.message);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
|
|
108
|
+
module.exports = runCli;
|
package/bin/postinstall.js
CHANGED
|
@@ -15,5 +15,6 @@ console.log('');
|
|
|
15
15
|
console.log(c.cyan + ' ╔══════════════════════════════════════════╗' + c.reset);
|
|
16
16
|
console.log(c.cyan + ' ║ ' + c.yellow + '安装完成!输入以下命令开始配置:' + c.reset);
|
|
17
17
|
console.log(c.cyan + ' ║ ' + c.green + 'jcc' + c.white + ' 配置或安装 ClaudeCode' + c.reset);
|
|
18
|
+
console.log(c.cyan + ' ║ ' + c.green + 'weds' + c.white + ' 配置或安装 weds ClaudeCode' + c.reset);
|
|
18
19
|
console.log(c.cyan + ' ╚══════════════════════════════════════════╝' + c.reset);
|
|
19
20
|
console.log('');
|
package/bin/weds.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
var claudeCli = require('../lib/claude_cli_common');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 运行 weds CLI,可供命令行直接执行或被 base_parts_ai require 调用
|
|
9
|
+
* @param {Array<string>} args 用户参数,不包含 node 和脚本路径
|
|
10
|
+
* @returns {Promise<void>}
|
|
11
|
+
*/
|
|
12
|
+
async function runCli(args) {
|
|
13
|
+
await claudeCli.runClaudeCli(args, {
|
|
14
|
+
cliName: 'weds',
|
|
15
|
+
cacheFileName: 'weds.json',
|
|
16
|
+
noticeType: 'weds',
|
|
17
|
+
getChannels: function (utils) {
|
|
18
|
+
return utils.WEDS_CHANNELS;
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (require.main === module) {
|
|
24
|
+
runCli(process.argv.slice(2)).catch(function (err) {
|
|
25
|
+
console.error('weds 执行失败:', err.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = runCli;
|
package/lib/ai_utils.js
CHANGED
|
@@ -17,9 +17,15 @@ var API_CHANNELS = [];
|
|
|
17
17
|
// Codex 渠道列表(运行时由 fetchConfig 从服务端 cxChannels 填充)
|
|
18
18
|
var CX_CHANNELS = [];
|
|
19
19
|
|
|
20
|
+
// weds 渠道列表(运行时由 fetchConfig 从服务端 wedsChannels 填充)
|
|
21
|
+
var WEDS_CHANNELS = [];
|
|
22
|
+
|
|
20
23
|
// 公告列表(运行时由 fetchConfig 从服务端拉取填充)
|
|
21
24
|
var noticeList = [];
|
|
22
25
|
|
|
26
|
+
// weds 公告列表(运行时由 fetchConfig 从服务端 noticeWedsList 填充)
|
|
27
|
+
var noticeWedsList = [];
|
|
28
|
+
|
|
23
29
|
// jcc.json 中保存最后一次手动输入 Token 的内部字段名
|
|
24
30
|
var LAST_MANUAL_AUTH_TOKEN_KEY = '__lastManualAuthToken';
|
|
25
31
|
|
|
@@ -45,12 +51,13 @@ async function fetchRemoteText(url, timeoutMs) {
|
|
|
45
51
|
|
|
46
52
|
/**
|
|
47
53
|
* 从当前入口文件名推断正在运行的 CLI 命令名
|
|
48
|
-
* @returns {string} jcc、jcx 或空字符串
|
|
54
|
+
* @returns {string} jcc、jcx、weds 或空字符串
|
|
49
55
|
*/
|
|
50
56
|
function getCurrentCliName() {
|
|
51
57
|
var entryName = path.basename(process.argv[1] || '').toLowerCase();
|
|
52
58
|
if (entryName === 'jcx' || entryName === 'jcx.js') return 'jcx';
|
|
53
59
|
if (entryName === 'jcc' || entryName === 'jcc.js') return 'jcc';
|
|
60
|
+
if (entryName === 'weds' || entryName === 'weds.js') return 'weds';
|
|
54
61
|
return '';
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -120,6 +127,14 @@ async function fetchConfig(buildCfg) {
|
|
|
120
127
|
});
|
|
121
128
|
console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
|
|
122
129
|
}
|
|
130
|
+
// 将服务端返回的 weds 渠道列表填充到 WEDS_CHANNELS
|
|
131
|
+
if (Array.isArray(result.data.wedsChannels)) {
|
|
132
|
+
WEDS_CHANNELS.length = 0;
|
|
133
|
+
result.data.wedsChannels.forEach(function (ch) {
|
|
134
|
+
WEDS_CHANNELS.push(ch);
|
|
135
|
+
});
|
|
136
|
+
console.log('[fetchConfig] 已拉取 ' + WEDS_CHANNELS.length + ' 个 weds 渠道');
|
|
137
|
+
}
|
|
123
138
|
// 解析公告列表
|
|
124
139
|
if (Array.isArray(result.data.noticeList)) {
|
|
125
140
|
noticeList.length = 0;
|
|
@@ -127,6 +142,13 @@ async function fetchConfig(buildCfg) {
|
|
|
127
142
|
noticeList.push(item);
|
|
128
143
|
});
|
|
129
144
|
}
|
|
145
|
+
// 解析 weds 公告列表
|
|
146
|
+
if (Array.isArray(result.data.noticeWedsList)) {
|
|
147
|
+
noticeWedsList.length = 0;
|
|
148
|
+
result.data.noticeWedsList.forEach(function (item) {
|
|
149
|
+
noticeWedsList.push(item);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
130
152
|
// 检查 jcc 自身版本,若需要升级则询问用户确认后安装
|
|
131
153
|
var currentVer = '';
|
|
132
154
|
try { currentVer = require('../package.json').version; } catch (e) { }
|
|
@@ -338,14 +360,16 @@ function getManualAuthTokenDefault(jccJson, channelId, settings) {
|
|
|
338
360
|
/**
|
|
339
361
|
* 根据 settings 中的 __jid 字段匹配当前渠道
|
|
340
362
|
* @param {object} settings Claude settings.json 对象
|
|
363
|
+
* @param {Array<object>} channels 渠道列表,默认使用 API_CHANNELS
|
|
341
364
|
* @returns {{ channel: object|null, key: string, jid: string }}
|
|
342
365
|
*/
|
|
343
|
-
function getCurrentInfo(settings) {
|
|
366
|
+
function getCurrentInfo(settings, channels) {
|
|
367
|
+
var channelList = channels || API_CHANNELS;
|
|
344
368
|
// 当前渠道 id,存储在 settings 顶层 __jid 字段
|
|
345
369
|
var currentJid = settings.__jid || '';
|
|
346
370
|
var currentKey = (settings.env && settings.env.ANTHROPIC_AUTH_TOKEN) || '';
|
|
347
371
|
// 按 id 匹配渠道
|
|
348
|
-
var channel = currentJid ? (
|
|
372
|
+
var channel = currentJid ? (channelList.find(function (c) { return c.id === currentJid; }) || null) : null;
|
|
349
373
|
return { channel: channel, key: currentKey, jid: currentJid };
|
|
350
374
|
}
|
|
351
375
|
|
|
@@ -362,10 +386,11 @@ function maskKey(key) {
|
|
|
362
386
|
|
|
363
387
|
/**
|
|
364
388
|
* 打印当前渠道和Key状态
|
|
365
|
-
* @param {object} settings
|
|
389
|
+
* @param {object} settings Claude settings.json 对象
|
|
390
|
+
* @param {Array<object>} channels 渠道列表,默认使用 API_CHANNELS
|
|
366
391
|
*/
|
|
367
|
-
function printCurrentInfo(settings) {
|
|
368
|
-
var info = getCurrentInfo(settings);
|
|
392
|
+
function printCurrentInfo(settings, channels) {
|
|
393
|
+
var info = getCurrentInfo(settings, channels);
|
|
369
394
|
// console.log('\n====== 当前 Claude 配置 ======');
|
|
370
395
|
let curSelectedChannel = '(' + (info.jid || '未设置') + ')';
|
|
371
396
|
if (info.channel) {
|
|
@@ -377,12 +402,14 @@ function printCurrentInfo(settings) {
|
|
|
377
402
|
|
|
378
403
|
/**
|
|
379
404
|
* 显示公告信息(黄色字体逐行输出),有公告时等待用户按回车继续
|
|
405
|
+
* @param {string} type 公告类型,weds 使用 noticeWedsList,其它使用默认 noticeList
|
|
380
406
|
* @returns {Promise<void>}
|
|
381
407
|
*/
|
|
382
|
-
function showNotice() {
|
|
383
|
-
|
|
408
|
+
function showNotice(type) {
|
|
409
|
+
var list = type === 'weds' ? noticeWedsList : noticeList;
|
|
410
|
+
if (!list.length) return Promise.resolve();
|
|
384
411
|
console.log('\n\x1b[33m=========================== 公告 ===========================');
|
|
385
|
-
|
|
412
|
+
list.forEach(function (line) {
|
|
386
413
|
console.log(line);
|
|
387
414
|
});
|
|
388
415
|
console.log('============================================================\x1b[0m');
|
|
@@ -399,6 +426,7 @@ function showNotice() {
|
|
|
399
426
|
module.exports = {
|
|
400
427
|
API_CHANNELS,
|
|
401
428
|
CX_CHANNELS,
|
|
429
|
+
WEDS_CHANNELS,
|
|
402
430
|
fetchConfig,
|
|
403
431
|
fetchText,
|
|
404
432
|
readJsonFile,
|
|
@@ -415,4 +443,6 @@ module.exports = {
|
|
|
415
443
|
maskKey,
|
|
416
444
|
printCurrentInfo,
|
|
417
445
|
showNotice,
|
|
446
|
+
noticeList,
|
|
447
|
+
noticeWedsList,
|
|
418
448
|
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude 类 CLI 公共入口逻辑
|
|
5
|
+
* 供 jcc、weds 等配置 Claude Code 的命令复用
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
var fs = require('fs');
|
|
9
|
+
var os = require('os');
|
|
10
|
+
var path = require('path');
|
|
11
|
+
var { Command } = require('commander');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 获取 Claude 配置文件路径,并确保基础目录和 settings.json 存在
|
|
15
|
+
* @param {object} cmd commander 解析出的选项
|
|
16
|
+
* @param {object} options CLI 入口配置
|
|
17
|
+
* @returns {object} 构建后的配置对象
|
|
18
|
+
*/
|
|
19
|
+
function getBuildCfg(cmd, options) {
|
|
20
|
+
var buildCfg = {};
|
|
21
|
+
var cacheFileName = options.cacheFileName || 'jcc.json';
|
|
22
|
+
|
|
23
|
+
// 配置路径
|
|
24
|
+
if (!cmd.config) {
|
|
25
|
+
cmd.config = process.cwd() + "/jai_cfg.json";
|
|
26
|
+
}
|
|
27
|
+
cmd.config = path.resolve(cmd.config);
|
|
28
|
+
|
|
29
|
+
// 读入 jai_cfg.json,兼容旧入口保留 details/rootPath 等字段
|
|
30
|
+
try {
|
|
31
|
+
buildCfg = JSON.parse(fs.readFileSync(cmd.config, "utf8"));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
buildCfg = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 详情输出
|
|
37
|
+
buildCfg.details = cmd.details;
|
|
38
|
+
|
|
39
|
+
// 缺省路径
|
|
40
|
+
if (!buildCfg.rootPath) {
|
|
41
|
+
buildCfg.rootPath = process.cwd();
|
|
42
|
+
}
|
|
43
|
+
buildCfg.rootPath = path.resolve(buildCfg.rootPath);
|
|
44
|
+
|
|
45
|
+
// 从 npm 全局目录下的 @anthropic-ai/claude-code/package.json 提取 Claude 版本号
|
|
46
|
+
try {
|
|
47
|
+
// 通过子进程执行 npm root -g 获取全局包根目录,兼容不同系统安装位置
|
|
48
|
+
var npmRootG = require('child_process').execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
49
|
+
var claudePkgPath = path.join(npmRootG, '@anthropic-ai', 'claude-code', 'package.json');
|
|
50
|
+
var claudePkg = JSON.parse(fs.readFileSync(claudePkgPath, 'utf8'));
|
|
51
|
+
buildCfg.claudeVersion = claudePkg.version || '';
|
|
52
|
+
} catch (e) {
|
|
53
|
+
buildCfg.claudeVersion = '';
|
|
54
|
+
}
|
|
55
|
+
console.log("[Claude version] " + (buildCfg.claudeVersion || '(未找到)'));
|
|
56
|
+
|
|
57
|
+
// Claude 配置文件路径(跨平台支持)
|
|
58
|
+
// 支持环境变量 JCC_TEST_HOME 覆盖 homedir,方便测试时隔离环境
|
|
59
|
+
var homeDir = process.env.JCC_TEST_HOME || os.homedir();
|
|
60
|
+
// ~/.claude.json 全局配置
|
|
61
|
+
buildCfg.claudeJsonPath = path.join(homeDir, '.claude.json');
|
|
62
|
+
// ~/.claude/settings.json Claude Code 设置文件
|
|
63
|
+
buildCfg.claudeSettingsPath = path.join(homeDir, '.claude', 'settings.json');
|
|
64
|
+
// Claude 渠道 Key 缓存文件,jcc 和 weds 可使用不同文件隔离缓存
|
|
65
|
+
buildCfg.jccJsonPath = path.join(homeDir, '.claude', 'jcc.json');
|
|
66
|
+
buildCfg.channelCachePath = path.join(homeDir, '.claude', cacheFileName);
|
|
67
|
+
|
|
68
|
+
// 确保 ~/.claude 目录存在,防止未安装 Claude 时崩溃
|
|
69
|
+
var claudeDir = path.join(homeDir, '.claude');
|
|
70
|
+
if (!fs.existsSync(claudeDir)) {
|
|
71
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
72
|
+
console.log('[Init] 已创建目录: ' + claudeDir);
|
|
73
|
+
}
|
|
74
|
+
// 确保 settings.json 存在(空对象兜底)
|
|
75
|
+
if (!fs.existsSync(buildCfg.claudeSettingsPath)) {
|
|
76
|
+
fs.writeFileSync(buildCfg.claudeSettingsPath, '{}', 'utf8');
|
|
77
|
+
console.log('[Init] 已创建文件: ' + buildCfg.claudeSettingsPath);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return buildCfg;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 动态加载 lib 下的命令模块(热更新)
|
|
85
|
+
* @param {string} libName lib目录下的文件名(不含.js)
|
|
86
|
+
* @returns {Function} 导出的模块函数
|
|
87
|
+
*/
|
|
88
|
+
function loadLib(libName) {
|
|
89
|
+
var libPath = path.resolve(__dirname, libName + '.js');
|
|
90
|
+
// 清除 require 缓存,实现热更新
|
|
91
|
+
delete require.cache[require.resolve(libPath)];
|
|
92
|
+
return require(libPath);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 检查系统环境变量中是否存在会干扰 Claude 配置的变量
|
|
97
|
+
* 若存在 ANTHROPIC_AUTH_TOKEN 或 ANTHROPIC_BASE_URL,则红色警告并等待用户确认
|
|
98
|
+
* @returns {Promise<void>}
|
|
99
|
+
*/
|
|
100
|
+
async function checkEnvConflict() {
|
|
101
|
+
// 检查可能覆盖工具配置的系统环境变量
|
|
102
|
+
var conflictVars = ['ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL'].filter(function (name) {
|
|
103
|
+
return !!process.env[name];
|
|
104
|
+
});
|
|
105
|
+
if (!conflictVars.length) return;
|
|
106
|
+
|
|
107
|
+
// 红色警告输出
|
|
108
|
+
console.log('\x1b[31m');
|
|
109
|
+
console.log('⚠️ 警告:检测到以下系统环境变量,可能导致工具配置无效!');
|
|
110
|
+
conflictVars.forEach(function (name) {
|
|
111
|
+
console.log(' ' + name + ' = ' + process.env[name]);
|
|
112
|
+
});
|
|
113
|
+
console.log('\x1b[0m');
|
|
114
|
+
|
|
115
|
+
// 等待用户按回车确认后继续
|
|
116
|
+
await new Promise(function (resolve) {
|
|
117
|
+
var rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
118
|
+
rl.question('按回车键继续...', function () {
|
|
119
|
+
rl.close();
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 公共命令执行流程:拉取配置 → 显示公告 → 执行业务模块 → 完成提示
|
|
127
|
+
* @param {string} libName lib 目录下的业务模块名(不含 .js)
|
|
128
|
+
* @param {object} opts commander 解析出的选项
|
|
129
|
+
* @param {object} options CLI 入口配置
|
|
130
|
+
* @returns {Promise<void>}
|
|
131
|
+
*/
|
|
132
|
+
async function runCommand(libName, opts, options) {
|
|
133
|
+
// 检查系统环境变量冲突,有冲突则红色警告并等待确认
|
|
134
|
+
await checkEnvConflict();
|
|
135
|
+
var buildCfg = getBuildCfg(opts, options);
|
|
136
|
+
// 从服务端拉取渠道配置(含自动升级检查)
|
|
137
|
+
var utils = loadLib('ai_utils');
|
|
138
|
+
await utils.fetchConfig(buildCfg);
|
|
139
|
+
// 注入当前 CLI 使用的渠道数组,业务模块无需关心来源字段
|
|
140
|
+
buildCfg.channels = options.getChannels(utils);
|
|
141
|
+
// 显示当前 CLI 对应公告,有公告时输出
|
|
142
|
+
await utils.showNotice(options.noticeType);
|
|
143
|
+
// 执行业务模块
|
|
144
|
+
await loadLib(libName)(opts, buildCfg);
|
|
145
|
+
console.log('\n✅ 完成!重启 Claude 后生效。');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 运行 Claude 类 CLI,可供命令行直接执行或被 base_parts_ai require 调用
|
|
150
|
+
* @param {Array<string>} args 用户参数,不包含 node 和脚本路径
|
|
151
|
+
* @param {object} options CLI 入口配置
|
|
152
|
+
* @returns {Promise<void>}
|
|
153
|
+
*/
|
|
154
|
+
async function runClaudeCli(args, options) {
|
|
155
|
+
var cliArgs = (args || []).slice();
|
|
156
|
+
var program = new Command();
|
|
157
|
+
|
|
158
|
+
program
|
|
159
|
+
.name(options.cliName);
|
|
160
|
+
|
|
161
|
+
// 命令:setapi —— 选择 AI 渠道,修改 Claude settings.json 中的 ANTHROPIC_BASE_URL
|
|
162
|
+
program
|
|
163
|
+
.command('setapi')
|
|
164
|
+
.action(async function () {
|
|
165
|
+
await runCommand('setapi_cc', this.opts(), options);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// 若未传入任何子命令,默认执行 setapi
|
|
169
|
+
if (cliArgs.length <= 0) {
|
|
170
|
+
cliArgs.push('setapi');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 使用 from:user 避免读取或修改全局 process.argv,便于被主入口 require 调用
|
|
174
|
+
await program.parseAsync(cliArgs, { from: 'user' });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
getBuildCfg,
|
|
179
|
+
loadLib,
|
|
180
|
+
checkEnvConflict,
|
|
181
|
+
runCommand,
|
|
182
|
+
runClaudeCli,
|
|
183
|
+
};
|
package/lib/setapi_cc.js
CHANGED
|
@@ -41,14 +41,21 @@ function ensureClaudeVersion(requiredVer, currentVer) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async function setapiCc(cmd, buildCfg) {
|
|
44
|
+
// 渠道列表和缓存路径可由 jcc/weds 入口注入,实现同一套 Claude 配置逻辑复用
|
|
45
|
+
var channels = buildCfg.channels || utils.API_CHANNELS;
|
|
46
|
+
var cachePath = buildCfg.channelCachePath || buildCfg.jccJsonPath;
|
|
44
47
|
var settings = utils.loadSettings(buildCfg.claudeSettingsPath);
|
|
45
|
-
var jccJson = utils.readJsonFile(
|
|
48
|
+
var jccJson = utils.readJsonFile(cachePath, {});
|
|
46
49
|
|
|
47
50
|
// 显示当前配置状态
|
|
48
|
-
utils.printCurrentInfo(settings);
|
|
51
|
+
utils.printCurrentInfo(settings, channels);
|
|
52
|
+
|
|
53
|
+
if (!channels.length) {
|
|
54
|
+
throw new Error('Claude 渠道列表为空,请检查网络或服务端配置');
|
|
55
|
+
}
|
|
49
56
|
|
|
50
57
|
// 构建渠道选择列表
|
|
51
|
-
var choices =
|
|
58
|
+
var choices = channels.map(function (c) {
|
|
52
59
|
return {
|
|
53
60
|
name: c.name + ' — ' + c.description,
|
|
54
61
|
value: c.id,
|
|
@@ -66,7 +73,7 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
66
73
|
var channelId = answers.channelId;
|
|
67
74
|
|
|
68
75
|
// 找到选中的渠道配置
|
|
69
|
-
var selected =
|
|
76
|
+
var selected = channels.find(function (c) { return c.id === channelId; });
|
|
70
77
|
|
|
71
78
|
// 将选中渠道 id 写入 settings.__jid,作为当前渠道标识
|
|
72
79
|
settings.__jid = selected.id;
|
|
@@ -152,7 +159,7 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
152
159
|
delete jccJson.keys[selected.id];
|
|
153
160
|
}
|
|
154
161
|
}
|
|
155
|
-
utils.writeJsonFile(
|
|
162
|
+
utils.writeJsonFile(cachePath, jccJson);
|
|
156
163
|
|
|
157
164
|
console.log('✅ 已切换到渠道: ' + selected.name);
|
|
158
165
|
console.log(' BASE_URL: ' + (settings.env.ANTHROPIC_BASE_URL || '(已删除)'));
|
|
@@ -182,7 +189,7 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
182
189
|
if (!jccJson.keys) { jccJson.keys = {}; }
|
|
183
190
|
jccJson.keys[selected.id] = newKey;
|
|
184
191
|
utils.saveLastManualAuthToken(jccJson, newKey);
|
|
185
|
-
utils.writeJsonFile(
|
|
192
|
+
utils.writeJsonFile(cachePath, jccJson);
|
|
186
193
|
|
|
187
194
|
console.log('✅ 已切换到渠道: ' + selected.name + ',Key 已保存到本地缓存。');
|
|
188
195
|
console.log(' Key: ' + utils.maskKey(newKey));
|