base_parts_ai 1.0.49 → 1.0.51
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/lib/ai_utils.js +117 -101
- package/lib/setapi_cc.js +38 -18
- package/lib/setapi_cx.js +5 -0
- package/package.json +1 -1
- package/test_home/.claude/settings.json +1 -1
- package/test_home/.codex/AGENTS.md +1 -1
- package/test_jcc.js +27 -0
- package/test_jcx.js +26 -0
package/lib/ai_utils.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
var fs = require('fs');
|
|
9
9
|
var path = require('path');
|
|
10
|
-
var http = require('http');
|
|
11
10
|
var { execSync, spawnSync } = require('child_process');
|
|
12
11
|
|
|
13
12
|
// ============================================================
|
|
@@ -24,6 +23,26 @@ var noticeList = [];
|
|
|
24
23
|
// jcc.json 中保存最后一次手动输入 Token 的内部字段名
|
|
25
24
|
var LAST_MANUAL_AUTH_TOKEN_KEY = '__lastManualAuthToken';
|
|
26
25
|
|
|
26
|
+
/**
|
|
27
|
+
* 使用 Node.js 18 内置 fetch 读取远程文本,并提供超时控制
|
|
28
|
+
* @param {string} url 请求地址
|
|
29
|
+
* @param {number} timeoutMs 超时时间(毫秒)
|
|
30
|
+
* @returns {Promise<{ ok: boolean, status: number, text: string }>}
|
|
31
|
+
*/
|
|
32
|
+
async function fetchRemoteText(url, timeoutMs) {
|
|
33
|
+
var controller = new AbortController();
|
|
34
|
+
var timer = setTimeout(function () {
|
|
35
|
+
controller.abort();
|
|
36
|
+
}, timeoutMs);
|
|
37
|
+
try {
|
|
38
|
+
var res = await fetch(url, { signal: controller.signal });
|
|
39
|
+
var text = await res.text();
|
|
40
|
+
return { ok: res.ok, status: res.status, text: text };
|
|
41
|
+
} finally {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
/**
|
|
28
47
|
* 从当前入口文件名推断正在运行的 CLI 命令名
|
|
29
48
|
* @returns {string} jcc、jcx 或空字符串
|
|
@@ -74,95 +93,91 @@ function rerunCurrentCli() {
|
|
|
74
93
|
* @param {object} buildCfg getBuildCfg 返回的配置对象
|
|
75
94
|
* @returns {Promise<void>}
|
|
76
95
|
*/
|
|
77
|
-
function fetchConfig(buildCfg) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} else {
|
|
146
|
-
console.log('[fetchConfig] 已跳过升级,继续使用当前版本');
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
console.warn('[fetchConfig] 服务端返回异常:', result.msg || JSON.stringify(result));
|
|
96
|
+
async function fetchConfig(buildCfg) {
|
|
97
|
+
var url = 'https://jai.jaskle.cn/hm/hm_pub/ai_cc_cfg?r=' + Math.random();
|
|
98
|
+
try {
|
|
99
|
+
var response = await fetchRemoteText(url, 8000);
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
console.warn('[fetchConfig] 请求失败: HTTP ' + response.status);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var result = JSON.parse(response.text);
|
|
106
|
+
if (result.code === 0 && result.data) {
|
|
107
|
+
// 将服务端返回的渠道列表填充到 API_CHANNELS
|
|
108
|
+
if (Array.isArray(result.data.channels)) {
|
|
109
|
+
API_CHANNELS.length = 0;
|
|
110
|
+
result.data.channels.forEach(function (ch) {
|
|
111
|
+
API_CHANNELS.push(ch);
|
|
112
|
+
});
|
|
113
|
+
console.log('[fetchConfig] 已拉取 ' + API_CHANNELS.length + ' 个渠道');
|
|
114
|
+
}
|
|
115
|
+
// 将服务端返回的 Codex 渠道列表填充到 CX_CHANNELS
|
|
116
|
+
if (Array.isArray(result.data.cxChannels)) {
|
|
117
|
+
CX_CHANNELS.length = 0;
|
|
118
|
+
result.data.cxChannels.forEach(function (ch) {
|
|
119
|
+
CX_CHANNELS.push(ch);
|
|
120
|
+
});
|
|
121
|
+
console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
|
|
122
|
+
}
|
|
123
|
+
// 解析公告列表
|
|
124
|
+
if (Array.isArray(result.data.noticeList)) {
|
|
125
|
+
noticeList.length = 0;
|
|
126
|
+
result.data.noticeList.forEach(function (item) {
|
|
127
|
+
noticeList.push(item);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// 检查 jcc 自身版本,若需要升级则询问用户确认后安装
|
|
131
|
+
var currentVer = '';
|
|
132
|
+
try { currentVer = require('../package.json').version; } catch (e) { }
|
|
133
|
+
var newVer = result.data.newVer || '';
|
|
134
|
+
// 比较语义化版本号,仅当服务端版本大于当前版本时才升级
|
|
135
|
+
function semverGt(a, b) {
|
|
136
|
+
var pa = a.split('.').map(Number);
|
|
137
|
+
var pb = b.split('.').map(Number);
|
|
138
|
+
for (var i = 0; i < 3; i++) {
|
|
139
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
140
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if (newVer && process.env.BASE_PARTS_AI_SKIP_SELF_UPDATE !== '1' && semverGt(newVer, currentVer)) {
|
|
145
|
+
console.log('[fetchConfig] 检测到新版本: ' + newVer + '(当前 ' + currentVer + ')');
|
|
146
|
+
var confirmed = "yes"
|
|
147
|
+
// confirmed = await select({
|
|
148
|
+
// message: '是否立即升级?',
|
|
149
|
+
// default: 'yes',
|
|
150
|
+
// choices: [
|
|
151
|
+
// { name: '立即升级', value: 'yes' },
|
|
152
|
+
// { name: '跳过', value: 'no' },
|
|
153
|
+
// ],
|
|
154
|
+
// });
|
|
155
|
+
if (confirmed === 'yes') {
|
|
156
|
+
console.log('[fetchConfig] 正在升级...');
|
|
157
|
+
// 用 select 上下选,默认高亮"立即升级"
|
|
158
|
+
try {
|
|
159
|
+
var tgzUrl = 'https://jdwfiles.oss-cn-hangzhou.aliyuncs.com/npm_pkg/base_parts_ai-' + newVer + '.tgz';
|
|
160
|
+
execSync('npm install -g ' + tgzUrl + ' --force --registry=https://registry.npmmirror.com', { stdio: 'inherit' });
|
|
161
|
+
rerunCurrentCli();
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.warn('[fetchConfig] 自动升级失败:', e.message);
|
|
151
164
|
}
|
|
152
|
-
}
|
|
153
|
-
console.
|
|
165
|
+
} else {
|
|
166
|
+
console.log('[fetchConfig] 已跳过升级,继续使用当前版本');
|
|
154
167
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
console.warn('[fetchConfig] 服务端返回异常:', result.msg || JSON.stringify(result));
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {
|
|
173
|
+
if (e && e.name === 'AbortError') {
|
|
161
174
|
console.warn('[fetchConfig] 请求超时');
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
175
|
+
} else if (e instanceof SyntaxError) {
|
|
176
|
+
console.warn('[fetchConfig] 解析响应失败:', e.message);
|
|
177
|
+
} else {
|
|
178
|
+
console.warn('[fetchConfig] 请求失败:', e.message);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
166
181
|
}
|
|
167
182
|
|
|
168
183
|
/**
|
|
@@ -251,21 +266,22 @@ function buildAiTipUrl(cli) {
|
|
|
251
266
|
* @param {string} url 请求地址
|
|
252
267
|
* @returns {Promise<string>}
|
|
253
268
|
*/
|
|
254
|
-
function fetchText(url) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
async function fetchText(url) {
|
|
270
|
+
try {
|
|
271
|
+
var response = await fetchRemoteText(url, 8000);
|
|
272
|
+
if (!response.ok) {
|
|
273
|
+
console.warn('[fetchText] 请求失败: HTTP ' + response.status);
|
|
274
|
+
return '';
|
|
275
|
+
}
|
|
276
|
+
return response.text;
|
|
277
|
+
} catch (e) {
|
|
278
|
+
if (e && e.name === 'AbortError') {
|
|
264
279
|
console.warn('[fetchText] 请求超时');
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
280
|
+
} else {
|
|
281
|
+
console.warn('[fetchText] 请求失败:', e.message);
|
|
282
|
+
}
|
|
283
|
+
return '';
|
|
284
|
+
}
|
|
269
285
|
}
|
|
270
286
|
|
|
271
287
|
/**
|
package/lib/setapi_cc.js
CHANGED
|
@@ -10,9 +10,37 @@
|
|
|
10
10
|
// inquirer@8 使用传统 prompt API
|
|
11
11
|
var inquirer = require('inquirer');
|
|
12
12
|
var utils = require('./ai_utils');
|
|
13
|
-
var
|
|
13
|
+
var childProcess = require('child_process');
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* 校验并安装渠道要求的 Claude Code 版本
|
|
17
|
+
* @param {string} requiredVer 渠道要求版本,latest 表示仅确保已安装
|
|
18
|
+
* @param {string} currentVer 当前检测到的 Claude 版本
|
|
19
|
+
*/
|
|
20
|
+
function ensureClaudeVersion(requiredVer, currentVer) {
|
|
21
|
+
// latest 表示只要求本机已安装 Claude,不强制覆盖用户已有的具体版本
|
|
22
|
+
if (requiredVer === 'latest' && currentVer) {
|
|
23
|
+
console.log('✅ Claude 已安装: ' + currentVer + ',渠道要求 latest,跳过重装');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (requiredVer && requiredVer !== currentVer) {
|
|
27
|
+
console.log('\n⚠️ Claude 版本不匹配:当前 ' + (currentVer || '未知') + ',渠道要求 ' + requiredVer);
|
|
28
|
+
// 使用 npmmirror 镜像加速安装指定版本的 claude-code 包
|
|
29
|
+
var pkg = '@anthropic-ai/claude-code@' + requiredVer;
|
|
30
|
+
console.log('🔄 正在安装 ' + pkg);
|
|
31
|
+
try {
|
|
32
|
+
// --registry 指定镜像源加速下载,--force 覆盖已有版本,stdio: 'inherit' 让 npm 进度实时打印到终端
|
|
33
|
+
childProcess.execSync('npm install -g ' + pkg + ' --registry=https://registry.npmmirror.com --force', { stdio: 'inherit' });
|
|
34
|
+
console.log('✅ Claude 已安装到 ' + requiredVer);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error('❌ 版本安装失败: ' + e.message);
|
|
37
|
+
}
|
|
38
|
+
} else if (requiredVer) {
|
|
39
|
+
console.log('✅ Claude 版本匹配: ' + currentVer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function setapiCc(cmd, buildCfg) {
|
|
16
44
|
var settings = utils.loadSettings(buildCfg.claudeSettingsPath);
|
|
17
45
|
var jccJson = utils.readJsonFile(buildCfg.jccJsonPath, {});
|
|
18
46
|
|
|
@@ -183,23 +211,9 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
183
211
|
requiredVer = ccVersionList[ctxAnswer.selectedCtx];
|
|
184
212
|
}
|
|
185
213
|
|
|
186
|
-
// 检测当前 Claude
|
|
214
|
+
// 检测当前 Claude 版本是否满足渠道要求,不满足则通过 npm registry 安装
|
|
187
215
|
var currentVer = buildCfg.claudeVersion || '';
|
|
188
|
-
|
|
189
|
-
console.log('\n⚠️ Claude 版本不匹配:当前 ' + (currentVer || '未知') + ',渠道要求 ' + requiredVer);
|
|
190
|
-
// 使用 npmmirror 镜像加速安装指定版本的 claude-code 包
|
|
191
|
-
var pkg = '@anthropic-ai/claude-code@' + requiredVer;
|
|
192
|
-
console.log('🔄 正在安装 ' + pkg);
|
|
193
|
-
try {
|
|
194
|
-
// --registry 指定镜像源加速下载,--force 覆盖已有版本,stdio: 'inherit' 让 npm 进度实时打印到终端
|
|
195
|
-
execSync('npm install -g ' + pkg + ' --registry=https://registry.npmmirror.com --force', { stdio: 'inherit' });
|
|
196
|
-
console.log('✅ Claude 已安装到 ' + requiredVer);
|
|
197
|
-
} catch (e) {
|
|
198
|
-
console.error('❌ 版本安装失败: ' + e.message);
|
|
199
|
-
}
|
|
200
|
-
} else if (requiredVer) {
|
|
201
|
-
console.log('✅ Claude 版本匹配: ' + currentVer);
|
|
202
|
-
}
|
|
216
|
+
ensureClaudeVersion(requiredVer, currentVer);
|
|
203
217
|
|
|
204
218
|
// 如果是22版本,清除大小标记
|
|
205
219
|
if (settings.model && requiredVer === '2.1.22') {
|
|
@@ -208,4 +222,10 @@ module.exports = async function (cmd, buildCfg) {
|
|
|
208
222
|
|
|
209
223
|
// 写入 settings.json
|
|
210
224
|
utils.writeJsonFile(buildCfg.claudeSettingsPath, settings);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setapiCc._private = {
|
|
228
|
+
ensureClaudeVersion: ensureClaudeVersion,
|
|
211
229
|
};
|
|
230
|
+
|
|
231
|
+
module.exports = setapiCc;
|
package/lib/setapi_cx.js
CHANGED
|
@@ -294,6 +294,11 @@ async function resolveRequiredCodexVersion(selected) {
|
|
|
294
294
|
* @param {string} currentVer 当前检测到的 Codex 版本
|
|
295
295
|
*/
|
|
296
296
|
function ensureCodexVersion(requiredVer, currentVer) {
|
|
297
|
+
// latest 表示只要求本机已安装 Codex,不强制覆盖用户已有的具体版本
|
|
298
|
+
if (requiredVer === 'latest' && currentVer) {
|
|
299
|
+
console.log('✅ Codex 已安装: ' + currentVer + ',渠道要求 latest,跳过重装');
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
297
302
|
if (requiredVer && requiredVer !== currentVer) {
|
|
298
303
|
console.log('\n⚠️ Codex 版本不匹配:当前 ' + (currentVer || '未知') + ',渠道要求 ' + requiredVer);
|
|
299
304
|
var pkg = '@openai/codex@' + requiredVer;
|
package/package.json
CHANGED
package/test_jcc.js
CHANGED
|
@@ -28,6 +28,7 @@ Object.keys(require.cache).forEach(function (k) { delete require.cache[k]; });
|
|
|
28
28
|
var utils = require('./lib/ai_utils');
|
|
29
29
|
var inquirer = require('inquirer');
|
|
30
30
|
var setapiCc = require('./lib/setapi_cc');
|
|
31
|
+
var childProcess = require('child_process');
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* 临时替换 inquirer.prompt,按队列返回交互结果
|
|
@@ -510,6 +511,32 @@ await (async function () {
|
|
|
510
511
|
if (!ok) { process.exit(1); }
|
|
511
512
|
})();
|
|
512
513
|
|
|
514
|
+
// ============================================================
|
|
515
|
+
// 测试 13:Claude latest 已安装时不重装,未安装时安装 latest
|
|
516
|
+
// ============================================================
|
|
517
|
+
console.log('\n======= Test 13: latest 仅在未安装时安装 =======');
|
|
518
|
+
(function () {
|
|
519
|
+
var oldExecSync = childProcess.execSync;
|
|
520
|
+
var commands = [];
|
|
521
|
+
childProcess.execSync = function (command) {
|
|
522
|
+
commands.push(command);
|
|
523
|
+
return '';
|
|
524
|
+
};
|
|
525
|
+
try {
|
|
526
|
+
setapiCc._private.ensureClaudeVersion('latest', '2.1.99');
|
|
527
|
+
setapiCc._private.ensureClaudeVersion('latest', '');
|
|
528
|
+
|
|
529
|
+
var ok = true;
|
|
530
|
+
ok = ok && commands.length === 1;
|
|
531
|
+
ok = ok && commands[0].indexOf('npm install -g @anthropic-ai/claude-code@latest') !== -1;
|
|
532
|
+
ok = ok && commands[0].indexOf('--registry=https://registry.npmmirror.com --force') !== -1;
|
|
533
|
+
console.log('✅ Test 13 通过:', ok);
|
|
534
|
+
if (!ok) { process.exit(1); }
|
|
535
|
+
} finally {
|
|
536
|
+
childProcess.execSync = oldExecSync;
|
|
537
|
+
}
|
|
538
|
+
})();
|
|
539
|
+
|
|
513
540
|
console.log('\n======= 所有测试完成 =======');
|
|
514
541
|
|
|
515
542
|
} // end runTests
|
package/test_jcx.js
CHANGED
|
@@ -454,6 +454,32 @@ async function runTests() {
|
|
|
454
454
|
}
|
|
455
455
|
});
|
|
456
456
|
|
|
457
|
+
// ============================================================
|
|
458
|
+
// 测试 8.1:Codex latest 已安装时不重装,未安装时安装 latest
|
|
459
|
+
// ============================================================
|
|
460
|
+
console.log('\n======= jcx Test 8.1: latest 仅在未安装时安装 =======');
|
|
461
|
+
(function () {
|
|
462
|
+
var oldExecSync = childProcess.execSync;
|
|
463
|
+
var commands = [];
|
|
464
|
+
childProcess.execSync = function (command) {
|
|
465
|
+
commands.push(command);
|
|
466
|
+
return '';
|
|
467
|
+
};
|
|
468
|
+
try {
|
|
469
|
+
privateApi.ensureCodexVersion('latest', '0.9.0');
|
|
470
|
+
privateApi.ensureCodexVersion('latest', '');
|
|
471
|
+
|
|
472
|
+
var ok = true;
|
|
473
|
+
ok = ok && commands.length === 1;
|
|
474
|
+
ok = ok && commands[0].indexOf('npm install -g @openai/codex@latest') !== -1;
|
|
475
|
+
ok = ok && commands[0].indexOf('--registry=https://registry.npmmirror.com --force') !== -1;
|
|
476
|
+
console.log('✅ Test 8.1 通过:', ok);
|
|
477
|
+
if (!ok) { process.exit(1); }
|
|
478
|
+
} finally {
|
|
479
|
+
childProcess.execSync = oldExecSync;
|
|
480
|
+
}
|
|
481
|
+
})();
|
|
482
|
+
|
|
457
483
|
console.log('\n======= jcx 所有测试完成 =======');
|
|
458
484
|
}
|
|
459
485
|
|