base_parts_ai 1.0.42 → 1.0.44
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/.eslintrc.js +3 -3
- package/AGENTS.md +42 -0
- package/README.md +9 -1
- package/{NEXT.md → TODO.md} +1 -1
- package/bin/jcc.js +2 -12
- package/bin/jcx.js +89 -0
- package/lib/{claude_utils.js → ai_utils.js} +136 -1
- package/lib/{setapi.js → setapi_cc.js} +77 -49
- package/lib/setapi_cx.js +483 -0
- package/package.json +7 -4
- package/test_home/.claude/jcc.json +1 -4
- package/test_home/.claude/settings.json +3 -13
- package/test_home/.codex/AGENTS.md +3 -0
- package/test_home/.codex/auth.json +4 -0
- package/test_home/.codex/config.toml +9 -0
- package/test_jcc.js +224 -50
- package/test_jcx.js +379 -0
- package/CLAUDE.md +0 -85
- package/lib/setkey.js +0 -46
package/.eslintrc.js
CHANGED
|
@@ -16,7 +16,7 @@ module.exports = {
|
|
|
16
16
|
"extends": "eslint:recommended",
|
|
17
17
|
"rules": {
|
|
18
18
|
"accessor-pairs": "error",
|
|
19
|
-
"array-bracket-newline": "
|
|
19
|
+
"array-bracket-newline": "off",
|
|
20
20
|
"array-bracket-spacing": "error",
|
|
21
21
|
"array-callback-return": "error",
|
|
22
22
|
"array-element-newline": "off",
|
|
@@ -124,7 +124,7 @@ module.exports = {
|
|
|
124
124
|
"no-label-var": "error",
|
|
125
125
|
"no-labels": "error",
|
|
126
126
|
"no-lone-blocks": "off",
|
|
127
|
-
"no-lonely-if": "
|
|
127
|
+
"no-lonely-if": "off",
|
|
128
128
|
"no-loop-func": "off",
|
|
129
129
|
"no-magic-numbers": "off",
|
|
130
130
|
"no-mixed-operators": "error",
|
|
@@ -252,7 +252,7 @@ module.exports = {
|
|
|
252
252
|
"valid-jsdoc": "off",
|
|
253
253
|
"vars-on-top": "off",
|
|
254
254
|
"wrap-iife": "off",
|
|
255
|
-
"wrap-regex": "
|
|
255
|
+
"wrap-regex": "off",
|
|
256
256
|
"yield-star-spacing": "error",
|
|
257
257
|
"yoda": "off",
|
|
258
258
|
"no-console": "off",
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## 项目结构与模块组织
|
|
4
|
+
|
|
5
|
+
本仓库是 Node.js CLI 包,包名为 `base_parts_ai`。`main.js` 是包入口;`bin/jcc.js` 是 Claude Code 命令行入口,对外暴露为 `jcc`;`bin/jcx.js` 是 Codex 命令行入口,对外暴露为 `jcx`;`bin/postinstall.js` 负责安装后处理。核心逻辑放在 `lib/`,其中 `ai_utils.js` 管理 AI 配置读写、渠道配置和 `ai_tip` 地址构建,`setapi_cc.js` 处理 Claude Code API 渠道切换与 Token 写入,`setapi_cx.js` 处理 Codex API 渠道切换与配置写入。`test_jcc.js`、`test_jcx.js` 是当前主要测试脚本,`test_home/` 用作隔离测试目录,避免污染真实用户目录。发布与使用说明集中在 `README.md`。
|
|
6
|
+
|
|
7
|
+
## 构建、测试与开发命令
|
|
8
|
+
|
|
9
|
+
- `npm install`:安装依赖,并触发 `postinstall`。
|
|
10
|
+
- `npm test`:运行 `node ./test_jcc.js && node ./test_jcx.js`,验证 `jcc` 与 `jcx` 相关配置流程。测试会联网拉取渠道配置,失败时先检查网络与服务端可用性。
|
|
11
|
+
- `node ./bin/jcc.js --help`:查看 CLI 参数与可用子命令。
|
|
12
|
+
- `node ./bin/jcx.js --help`:查看 Codex CLI 参数与可用子命令。
|
|
13
|
+
- `npx eslint .`:按 `.eslintrc.js` 检查代码风格;当前未在 `package.json` 中配置 lint 脚本。
|
|
14
|
+
|
|
15
|
+
本项目没有独立构建步骤,修改后直接通过 Node.js 执行验证。
|
|
16
|
+
|
|
17
|
+
## 编码风格与命名约定
|
|
18
|
+
|
|
19
|
+
使用 CommonJS(`require` / `module.exports`)和 `'use strict'`。保持现有缩进与分号风格,不做无关格式化。文件名采用小写加下划线,例如 `ai_utils.js`。变量和函数优先使用清晰的英文驼峰命名,如 `getBuildCfg`、`loadSettings`。新增关键函数、类、接口、重要分支和配置写入逻辑必须添加简体中文注释,说明目的和影响。
|
|
20
|
+
|
|
21
|
+
## 测试规范
|
|
22
|
+
|
|
23
|
+
新增行为应补充到 `test_jcc.js` 或新增 `test_*.js` 脚本。测试必须优先使用 `JCC_TEST_HOME` 或临时目录隔离用户配置,禁止直接改写真实 `~/.claude`。涉及网络配置拉取的测试应输出明确失败原因,便于区分代码错误和网络错误。
|
|
24
|
+
|
|
25
|
+
`jcc` 测试覆盖 Claude Code 配置文件:`~/.claude.json`、`~/.claude/settings.json`、`~/.claude/jcc.json`。`jcx` 测试覆盖 Codex 配置文件:`~/.codex/auth.json`、`~/.codex/config.toml`、`~/.codex/AGENTS.md`。新增 Codex 行为应优先补充到 `test_jcx.js`。
|
|
26
|
+
|
|
27
|
+
## CLI 行为约定
|
|
28
|
+
|
|
29
|
+
- `jcc` 无子命令时默认执行 `jcc setapi`,调用 `lib/setapi_cc.js`。
|
|
30
|
+
- `jcx` 无子命令时默认执行 `jcx setapi`,调用 `lib/setapi_cx.js`。
|
|
31
|
+
- `ai_utils.fetchConfig()` 从服务端拉取 `channels` 填充 `API_CHANNELS`,拉取 `cxChannels` 填充 `CX_CHANNELS`。
|
|
32
|
+
- `jcx setapi` 每次配置都要通过 `ai_tip?cli=cx&os=xxx` 重新拉取并覆盖写入 `~/.codex/AGENTS.md`。
|
|
33
|
+
- 需要手动输入 URL 的流程必须先输入 URL,再输入 Key。
|
|
34
|
+
- 需要手动输入 URL/Key 的输入框应保留默认值;直接回车表示沿用默认值,输入空格后回车表示删除该配置。
|
|
35
|
+
|
|
36
|
+
## 提交与 Pull Request 规范
|
|
37
|
+
|
|
38
|
+
当前 Git 历史提交信息较简单,仅见 `commit`,后续建议使用简短祈使句或约定式提交,例如 `fix: handle empty settings file`、`feat: add channel key command`。PR 应包含变更目的、测试命令与结果、影响的配置文件路径;CLI 输出或交互变化建议附截图或关键日志。
|
|
39
|
+
|
|
40
|
+
## 安全与配置提示
|
|
41
|
+
|
|
42
|
+
不要提交真实 API Key、用户级 `.claude.json` 或 `.claude/settings.json`。读写文件时在 Windows PowerShell 中明确使用 UTF-8,避免中文乱码。处理路径时使用 `path.join` / `path.resolve`,保持跨平台兼容。
|
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
## 下载发布的包
|
|
3
|
-
https://registry.npmjs.org/base_parts_ai/-/base_parts_ai-1.0.
|
|
3
|
+
https://registry.npmjs.org/base_parts_ai/-/base_parts_ai-1.0.43.tgz
|
|
4
4
|
|
|
5
5
|
## 然后放在jdwfiles目录下
|
|
6
6
|
https://jdwfiles.oss-cn-hangzhou.aliyuncs.com/npm_pkg/base_parts_ai-1.0.34.tgz
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# 2.1.34
|
|
10
|
+
User-Agent: claude-cli/2.1.34 (external, cli)
|
|
11
|
+
claude-opus-4-6
|
|
12
|
+
claude-sonnet-4-6
|
|
13
|
+
claude-haiku-4-5-20251001
|
|
14
|
+
|
package/{NEXT.md → TODO.md}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
|
package/bin/jcc.js
CHANGED
|
@@ -129,7 +129,7 @@ async function runCommand(libName, opts) {
|
|
|
129
129
|
await checkEnvConflict();
|
|
130
130
|
var buildCfg = getBuildCfg(opts);
|
|
131
131
|
// 从服务端拉取渠道配置(含 jcc 自动升级检查)
|
|
132
|
-
var utils = loadLib('
|
|
132
|
+
var utils = loadLib('ai_utils');
|
|
133
133
|
await utils.fetchConfig(buildCfg);
|
|
134
134
|
// 显示公告,有公告时等待用户按回车继续
|
|
135
135
|
await utils.showNotice();
|
|
@@ -142,17 +142,9 @@ async function runCommand(libName, opts) {
|
|
|
142
142
|
program
|
|
143
143
|
.command('setapi')
|
|
144
144
|
.action(async function () {
|
|
145
|
-
await runCommand('
|
|
145
|
+
await runCommand('setapi_cc', this.opts());
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
// 命令:setkey —— 设置 AI Key,修改 Claude settings.json 中的 ANTHROPIC_API_KEY
|
|
149
|
-
program
|
|
150
|
-
.command('setkey')
|
|
151
|
-
.action(async function () {
|
|
152
|
-
await runCommand('setkey', this.opts());
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
|
|
156
148
|
// 若未传入任何子命令,默认执行 setapi
|
|
157
149
|
if (process.argv.length <= 2) {
|
|
158
150
|
process.argv.push('setapi');
|
|
@@ -161,5 +153,3 @@ if (process.argv.length <= 2) {
|
|
|
161
153
|
// 开始解析
|
|
162
154
|
program.parse(process.argv);
|
|
163
155
|
|
|
164
|
-
|
|
165
|
-
|
package/bin/jcx.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var os = require('os');
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var { execSync } = require('child_process');
|
|
9
|
+
var { Command } = require('commander');
|
|
10
|
+
var program = new Command();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 构建 Codex 配置路径,并确保 ~/.codex 目录存在
|
|
14
|
+
* @param {object} cmd commander 解析出的选项
|
|
15
|
+
* @returns {object}
|
|
16
|
+
*/
|
|
17
|
+
function getBuildCfg(cmd) {
|
|
18
|
+
var buildCfg = {};
|
|
19
|
+
buildCfg.details = cmd.details;
|
|
20
|
+
|
|
21
|
+
// 从 npm 全局目录下的 @openai/codex/package.json 提取 Codex 版本号
|
|
22
|
+
// 该版本号用于后续和渠道 cxVersionList 做一致性校验
|
|
23
|
+
try {
|
|
24
|
+
var npmRootG = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
25
|
+
var codexPkgPath = path.join(npmRootG, '@openai', 'codex', 'package.json');
|
|
26
|
+
var codexPkg = JSON.parse(fs.readFileSync(codexPkgPath, 'utf8'));
|
|
27
|
+
buildCfg.codexVersion = codexPkg.version || '';
|
|
28
|
+
} catch (e) {
|
|
29
|
+
buildCfg.codexVersion = '';
|
|
30
|
+
}
|
|
31
|
+
console.log('[Codex version] ' + (buildCfg.codexVersion || '(未找到)'));
|
|
32
|
+
|
|
33
|
+
// 支持 JCC_TEST_HOME 隔离测试目录,避免改写真实用户 Codex 配置
|
|
34
|
+
var homeDir = process.env.JCC_TEST_HOME || os.homedir();
|
|
35
|
+
var codexDir = path.join(homeDir, '.codex');
|
|
36
|
+
if (!fs.existsSync(codexDir)) {
|
|
37
|
+
fs.mkdirSync(codexDir, { recursive: true });
|
|
38
|
+
console.log('[Init] 已创建目录: ' + codexDir);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
buildCfg.codexDir = codexDir;
|
|
42
|
+
buildCfg.codexAuthPath = path.join(codexDir, 'auth.json');
|
|
43
|
+
buildCfg.codexConfigPath = path.join(codexDir, 'config.toml');
|
|
44
|
+
buildCfg.codexAgentsPath = path.join(codexDir, 'AGENTS.md');
|
|
45
|
+
return buildCfg;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 动态加载 lib 下的命令模块,便于本地开发时读取最新文件
|
|
50
|
+
* @param {string} libName lib目录下的文件名(不含.js)
|
|
51
|
+
* @returns {Function}
|
|
52
|
+
*/
|
|
53
|
+
function loadLib(libName) {
|
|
54
|
+
var libPath = path.resolve(__dirname, '../lib/' + libName + '.js');
|
|
55
|
+
delete require.cache[require.resolve(libPath)];
|
|
56
|
+
return require(libPath);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Codex 命令公共流程:拉取配置、展示公告、执行业务模块
|
|
61
|
+
* @param {string} libName 业务模块名
|
|
62
|
+
* @param {object} opts commander 选项
|
|
63
|
+
*/
|
|
64
|
+
async function runCommand(libName, opts) {
|
|
65
|
+
var buildCfg = getBuildCfg(opts);
|
|
66
|
+
var utils = loadLib('ai_utils');
|
|
67
|
+
await utils.fetchConfig(buildCfg);
|
|
68
|
+
await utils.showNotice();
|
|
69
|
+
await loadLib(libName)(opts, buildCfg);
|
|
70
|
+
console.log('\n✅ 完成!重启 Codex 后生效。');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
program
|
|
74
|
+
.command('setapi')
|
|
75
|
+
.description('选择 Codex AI 渠道并写入 ~/.codex 配置')
|
|
76
|
+
.action(async function () {
|
|
77
|
+
await runCommand('setapi_cx', this.opts());
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
program
|
|
81
|
+
.name('jcx')
|
|
82
|
+
.description('Codex 配置工具');
|
|
83
|
+
|
|
84
|
+
// 若未传入任何子命令,默认执行 setapi
|
|
85
|
+
if (process.argv.length <= 2) {
|
|
86
|
+
process.argv.push('setapi');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
program.parse(process.argv);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Claude 配置工具函数
|
|
5
|
-
* 供
|
|
5
|
+
* 供 setapi_cc.js、setapi_cx.js 等命令模块共享使用
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
var fs = require('fs');
|
|
@@ -15,9 +15,15 @@ var { execSync } = require('child_process');
|
|
|
15
15
|
// ============================================================
|
|
16
16
|
var API_CHANNELS = [];
|
|
17
17
|
|
|
18
|
+
// Codex 渠道列表(运行时由 fetchConfig 从服务端 cxChannels 填充)
|
|
19
|
+
var CX_CHANNELS = [];
|
|
20
|
+
|
|
18
21
|
// 公告列表(运行时由 fetchConfig 从服务端拉取填充)
|
|
19
22
|
var noticeList = [];
|
|
20
23
|
|
|
24
|
+
// jcc.json 中保存最后一次手动输入 Token 的内部字段名
|
|
25
|
+
var LAST_MANUAL_AUTH_TOKEN_KEY = '__lastManualAuthToken';
|
|
26
|
+
|
|
21
27
|
/**
|
|
22
28
|
* 从服务端拉取渠道配置,并检查 jcc 自身是否需要升级
|
|
23
29
|
* @param {object} buildCfg getBuildCfg 返回的配置对象
|
|
@@ -41,6 +47,14 @@ function fetchConfig(buildCfg) {
|
|
|
41
47
|
});
|
|
42
48
|
console.log('[fetchConfig] 已拉取 ' + API_CHANNELS.length + ' 个渠道');
|
|
43
49
|
}
|
|
50
|
+
// 将服务端返回的 Codex 渠道列表填充到 CX_CHANNELS
|
|
51
|
+
if (Array.isArray(result.data.cxChannels)) {
|
|
52
|
+
CX_CHANNELS.length = 0;
|
|
53
|
+
result.data.cxChannels.forEach(function (ch) {
|
|
54
|
+
CX_CHANNELS.push(ch);
|
|
55
|
+
});
|
|
56
|
+
console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
|
|
57
|
+
}
|
|
44
58
|
// 解析公告列表
|
|
45
59
|
if (Array.isArray(result.data.noticeList)) {
|
|
46
60
|
noticeList.length = 0;
|
|
@@ -148,6 +162,118 @@ function loadSettings(settingsPath) {
|
|
|
148
162
|
return settings;
|
|
149
163
|
}
|
|
150
164
|
|
|
165
|
+
/**
|
|
166
|
+
* 获取 ai_tip 接口使用的当前系统标识
|
|
167
|
+
* 将 Node.js 平台和 CPU 架构归一化为服务端识别的 os 参数
|
|
168
|
+
* @returns {string} 例如 windows_x64、linux_x64、macos_arm64
|
|
169
|
+
*/
|
|
170
|
+
function getAiTipOsParam() {
|
|
171
|
+
var platformMap = {
|
|
172
|
+
win32: 'windows',
|
|
173
|
+
linux: 'linux',
|
|
174
|
+
darwin: 'macos',
|
|
175
|
+
};
|
|
176
|
+
var archMap = {
|
|
177
|
+
ia32: 'x86',
|
|
178
|
+
x64: 'x64',
|
|
179
|
+
arm: 'arm',
|
|
180
|
+
arm64: 'arm64',
|
|
181
|
+
};
|
|
182
|
+
var platformName = platformMap[process.platform] || process.platform;
|
|
183
|
+
var archName = archMap[process.arch] || process.arch;
|
|
184
|
+
return platformName + '_' + archName;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 构建 SessionStart Hook 中调用 ai_tip 的命令
|
|
189
|
+
* os 参数在写入配置时按当前运行环境固定,避免运行时依赖占位符替换
|
|
190
|
+
* @returns {string}
|
|
191
|
+
*/
|
|
192
|
+
function buildAiTipCommand() {
|
|
193
|
+
return 'curl -s -m 2 ' + buildAiTipUrl('cc');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 构建 ai_tip 接口地址
|
|
198
|
+
* @param {string} cli 客户端标识,例如 cc 或 cx
|
|
199
|
+
* @returns {string}
|
|
200
|
+
*/
|
|
201
|
+
function buildAiTipUrl(cli) {
|
|
202
|
+
return 'http://116.62.243.108:7180/pub/ai_tip?cli=' + cli + '-' + getAiTipOsParam();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 读取远程文本内容,失败时返回空字符串
|
|
207
|
+
* @param {string} url 请求地址
|
|
208
|
+
* @returns {Promise<string>}
|
|
209
|
+
*/
|
|
210
|
+
function fetchText(url) {
|
|
211
|
+
return new Promise(function (resolve) {
|
|
212
|
+
http.get(url, { timeout: 8000 }, function (res) {
|
|
213
|
+
var chunks = '';
|
|
214
|
+
res.on('data', function (chunk) { chunks += chunk; });
|
|
215
|
+
res.on('end', function () { resolve(chunks); });
|
|
216
|
+
}).on('error', function (err) {
|
|
217
|
+
console.warn('[fetchText] 请求失败:', err.message);
|
|
218
|
+
resolve('');
|
|
219
|
+
}).on('timeout', function () {
|
|
220
|
+
console.warn('[fetchText] 请求超时');
|
|
221
|
+
this.destroy();
|
|
222
|
+
resolve('');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 获取渠道配置中强制指定的 ANTHROPIC_AUTH_TOKEN
|
|
229
|
+
* 仅当服务端 envObj 显式带有非空 Token 时返回,用于跳过用户输入
|
|
230
|
+
* @param {object} channel 渠道配置对象
|
|
231
|
+
* @returns {string}
|
|
232
|
+
*/
|
|
233
|
+
function getChannelAuthToken(channel) {
|
|
234
|
+
if (!channel || !channel.envObj || !Object.prototype.hasOwnProperty.call(channel.envObj, 'ANTHROPIC_AUTH_TOKEN')) {
|
|
235
|
+
return '';
|
|
236
|
+
}
|
|
237
|
+
var token = channel.envObj.ANTHROPIC_AUTH_TOKEN;
|
|
238
|
+
return typeof token === 'string' ? token.trim() : '';
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 记录最后一次由用户手动输入的 Token
|
|
243
|
+
* 该值只作为后续输入提示默认值,不代表当前渠道配置
|
|
244
|
+
* @param {object} jccJson jcc.json 对象
|
|
245
|
+
* @param {string} token 用户手动输入的 Token
|
|
246
|
+
*/
|
|
247
|
+
function saveLastManualAuthToken(jccJson, token) {
|
|
248
|
+
if (!jccJson.keys) { jccJson.keys = {}; }
|
|
249
|
+
if (token) {
|
|
250
|
+
jccJson.keys[LAST_MANUAL_AUTH_TOKEN_KEY] = token;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* 获取最后一次手动输入的 Token
|
|
256
|
+
* @param {object} jccJson jcc.json 对象
|
|
257
|
+
* @returns {string}
|
|
258
|
+
*/
|
|
259
|
+
function getLastManualAuthToken(jccJson) {
|
|
260
|
+
return (jccJson.keys && jccJson.keys[LAST_MANUAL_AUTH_TOKEN_KEY]) || '';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 计算需要提示用户输入 Token 时的默认值
|
|
265
|
+
* 优先使用当前渠道缓存,其次当前 settings,最后使用最后一次手动输入的 Token
|
|
266
|
+
* @param {object} jccJson jcc.json 对象
|
|
267
|
+
* @param {string} channelId 当前渠道 id
|
|
268
|
+
* @param {object} settings Claude settings.json 对象
|
|
269
|
+
* @returns {string}
|
|
270
|
+
*/
|
|
271
|
+
function getManualAuthTokenDefault(jccJson, channelId, settings) {
|
|
272
|
+
var cachedToken = (jccJson.keys && jccJson.keys[channelId]) || '';
|
|
273
|
+
var currentToken = (settings.env && settings.env.ANTHROPIC_AUTH_TOKEN) || '';
|
|
274
|
+
return cachedToken || currentToken || getLastManualAuthToken(jccJson);
|
|
275
|
+
}
|
|
276
|
+
|
|
151
277
|
/**
|
|
152
278
|
* 根据 settings 中的 __jid 字段匹配当前渠道
|
|
153
279
|
* @param {object} settings Claude settings.json 对象
|
|
@@ -211,10 +337,19 @@ function showNotice() {
|
|
|
211
337
|
|
|
212
338
|
module.exports = {
|
|
213
339
|
API_CHANNELS,
|
|
340
|
+
CX_CHANNELS,
|
|
214
341
|
fetchConfig,
|
|
342
|
+
fetchText,
|
|
215
343
|
readJsonFile,
|
|
216
344
|
writeJsonFile,
|
|
217
345
|
loadSettings,
|
|
346
|
+
getAiTipOsParam,
|
|
347
|
+
buildAiTipUrl,
|
|
348
|
+
buildAiTipCommand,
|
|
349
|
+
getChannelAuthToken,
|
|
350
|
+
saveLastManualAuthToken,
|
|
351
|
+
getLastManualAuthToken,
|
|
352
|
+
getManualAuthTokenDefault,
|
|
218
353
|
getCurrentInfo,
|
|
219
354
|
maskKey,
|
|
220
355
|
printCurrentInfo,
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* setapi 命令:交互式选择 AI 渠道
|
|
5
5
|
* 将选中的渠道 id 写入 ~/.claude/settings.json 的 __jid 字段
|
|
6
6
|
* 渠道专属动作(如 baseUrl 等)由后续扩展的 if 分支单独处理
|
|
7
|
-
*
|
|
7
|
+
* 若服务端未强制下发 Key,则提示用户输入并保存到 ~/.claude/jcc.json
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
// inquirer@8 使用传统 prompt API
|
|
11
11
|
var inquirer = require('inquirer');
|
|
12
|
-
var utils = require('./
|
|
12
|
+
var utils = require('./ai_utils');
|
|
13
13
|
var { execSync } = require('child_process');
|
|
14
14
|
|
|
15
15
|
module.exports = async function (cmd, buildCfg) {
|
|
@@ -56,7 +56,7 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
56
56
|
"SessionStart": [
|
|
57
57
|
{
|
|
58
58
|
"matcher": ".*",
|
|
59
|
-
"hooks": [{ "type": "command", "command":
|
|
59
|
+
"hooks": [{ "type": "command", "command": utils.buildAiTipCommand() }]
|
|
60
60
|
}
|
|
61
61
|
]
|
|
62
62
|
};
|
|
@@ -74,62 +74,91 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
74
74
|
delete settings.env[key];
|
|
75
75
|
}
|
|
76
76
|
});
|
|
77
|
+
// 服务端渠道配置显式下发 Token 时强制使用,不再提示用户输入
|
|
78
|
+
var forcedAuthToken = utils.getChannelAuthToken(selected);
|
|
77
79
|
|
|
78
|
-
// useSettingsFile=true 时,ANTHROPIC_BASE_URL
|
|
79
|
-
//
|
|
80
|
+
// useSettingsFile=true 时,ANTHROPIC_BASE_URL 需手动输入;Token 可能由服务端强制下发
|
|
81
|
+
// Token 输入默认值优先取缓存文件(jcc.json),其次取环境变量配置(settings.env)
|
|
80
82
|
if (selected.useSettingsFile) {
|
|
81
|
-
// 手动输入 ANTHROPIC_BASE_URL
|
|
82
|
-
var
|
|
83
|
+
// 手动输入 ANTHROPIC_BASE_URL,优先用缓存值,其次用 settings.env
|
|
84
|
+
var cachedBaseUrl = (jccJson.keys && jccJson.keys[selected.id + '_baseUrl']) || '';
|
|
85
|
+
var defaultBaseUrl = cachedBaseUrl || settings.env.ANTHROPIC_BASE_URL || '';
|
|
83
86
|
var baseUrlAnswer = await inquirer.prompt([{
|
|
84
87
|
type: 'input',
|
|
85
88
|
name: 'value',
|
|
86
|
-
message: '请输入 ANTHROPIC_BASE_URL
|
|
89
|
+
message: '请输入 ANTHROPIC_BASE_URL(输入空格后回车删除该配置):',
|
|
87
90
|
default: defaultBaseUrl,
|
|
88
|
-
validate: function (v) {
|
|
89
|
-
return v.trim().length > 0 ? true : 'ANTHROPIC_BASE_URL 不能为空';
|
|
90
|
-
},
|
|
91
91
|
}]);
|
|
92
|
-
|
|
92
|
+
var inputBaseUrl = baseUrlAnswer.value.trim();
|
|
93
93
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
// 缓存或删除 BASE_URL,空值表示清理该环境变量配置
|
|
95
|
+
if (!jccJson.keys) { jccJson.keys = {}; }
|
|
96
|
+
if (inputBaseUrl) {
|
|
97
|
+
settings.env.ANTHROPIC_BASE_URL = inputBaseUrl;
|
|
98
|
+
jccJson.keys[selected.id + '_baseUrl'] = inputBaseUrl;
|
|
99
|
+
} else {
|
|
100
|
+
delete settings.env.ANTHROPIC_BASE_URL;
|
|
101
|
+
delete jccJson.keys[selected.id + '_baseUrl'];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (forcedAuthToken) {
|
|
105
|
+
// 服务端强制 Token 不写入手动 Token 缓存,避免污染后续默认值
|
|
106
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = forcedAuthToken;
|
|
107
|
+
console.log('已使用渠道配置中的 ANTHROPIC_AUTH_TOKEN,跳过 Token 输入。');
|
|
108
|
+
} else {
|
|
109
|
+
// 手动输入 ANTHROPIC_AUTH_TOKEN,默认值依次取渠道缓存、当前 settings、最后手动 Token
|
|
110
|
+
var defaultToken = utils.getManualAuthTokenDefault(jccJson, selected.id, settings);
|
|
111
|
+
var tokenAnswer = await inquirer.prompt([{
|
|
112
|
+
type: 'input',
|
|
113
|
+
name: 'value',
|
|
114
|
+
message: '请输入 ANTHROPIC_AUTH_TOKEN(输入空格后回车删除该配置):',
|
|
115
|
+
default: defaultToken,
|
|
116
|
+
}]);
|
|
117
|
+
var inputToken = tokenAnswer.value.trim();
|
|
118
|
+
if (inputToken) {
|
|
119
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = inputToken;
|
|
120
|
+
jccJson.keys[selected.id] = inputToken;
|
|
121
|
+
utils.saveLastManualAuthToken(jccJson, inputToken);
|
|
122
|
+
} else {
|
|
123
|
+
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
124
|
+
delete jccJson.keys[selected.id];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
utils.writeJsonFile(buildCfg.jccJsonPath, jccJson);
|
|
106
128
|
|
|
107
|
-
// 写入 settings.json(useSettingsFile 模式不缓存 Key 到 jcc.json)
|
|
108
129
|
console.log('✅ 已切换到渠道: ' + selected.name);
|
|
109
|
-
console.log(' BASE_URL: ' + settings.env.ANTHROPIC_BASE_URL);
|
|
130
|
+
console.log(' BASE_URL: ' + (settings.env.ANTHROPIC_BASE_URL || '(已删除)'));
|
|
110
131
|
console.log(' Token: ' + utils.maskKey(settings.env.ANTHROPIC_AUTH_TOKEN));
|
|
111
132
|
} else {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
if (forcedAuthToken) {
|
|
134
|
+
// 服务端强制 Token 直接写入 settings,且不触发输入提示
|
|
135
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = forcedAuthToken;
|
|
136
|
+
console.log('✅ 已切换到渠道: ' + selected.name + ',已使用渠道配置中的 Key。');
|
|
137
|
+
console.log(' Key: ' + utils.maskKey(forcedAuthToken));
|
|
138
|
+
} else {
|
|
139
|
+
// 查询该渠道缓存的 Key;若当前 Token 为空,则使用最后手动 Token 作为默认值
|
|
140
|
+
var savedKey = utils.getManualAuthTokenDefault(jccJson, selected.id, settings);
|
|
141
|
+
var keyAnswer = await inquirer.prompt([{
|
|
142
|
+
type: 'input',
|
|
143
|
+
name: 'value',
|
|
144
|
+
message: '请输入 ANTHROPIC_AUTH_TOKEN:',
|
|
145
|
+
default: savedKey,
|
|
146
|
+
validate: function (v) {
|
|
147
|
+
return v.trim().length > 0 ? true : 'Key 不能为空';
|
|
148
|
+
},
|
|
149
|
+
}]);
|
|
150
|
+
var newKey = keyAnswer.value.trim();
|
|
151
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = newKey;
|
|
152
|
+
|
|
153
|
+
// 缓存到 jcc.json,并记录为最后一次手动输入的 Token
|
|
154
|
+
if (!jccJson.keys) { jccJson.keys = {}; }
|
|
155
|
+
jccJson.keys[selected.id] = newKey;
|
|
156
|
+
utils.saveLastManualAuthToken(jccJson, newKey);
|
|
157
|
+
utils.writeJsonFile(buildCfg.jccJsonPath, jccJson);
|
|
158
|
+
|
|
159
|
+
console.log('✅ 已切换到渠道: ' + selected.name + ',Key 已保存到本地缓存。');
|
|
160
|
+
console.log(' Key: ' + utils.maskKey(newKey));
|
|
161
|
+
}
|
|
133
162
|
}
|
|
134
163
|
|
|
135
164
|
// 从 ccVersionList 对象中确定目标版本号
|
|
@@ -148,7 +177,7 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
148
177
|
var ctxAnswer = await inquirer.prompt([{
|
|
149
178
|
type: 'list',
|
|
150
179
|
name: 'selectedCtx',
|
|
151
|
-
message: '
|
|
180
|
+
message: '请选择版本:',
|
|
152
181
|
choices: ctxChoices,
|
|
153
182
|
}]);
|
|
154
183
|
requiredVer = ccVersionList[ctxAnswer.selectedCtx];
|
|
@@ -180,4 +209,3 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
180
209
|
// 写入 settings.json
|
|
181
210
|
utils.writeJsonFile(buildCfg.claudeSettingsPath, settings);
|
|
182
211
|
};
|
|
183
|
-
|