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 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
- return new Promise(function (resolve) {
79
- var url = 'https://jai.jaskle.cn/hm/hm_pub/ai_cc_cfg?r=' + Math.random();
80
- http.get(url, { timeout: 8000 }, function (res) {
81
- var chunks = '';
82
- res.on('data', function (chunk) { chunks += chunk; });
83
- res.on('end', async function () {
84
- try {
85
- var result = JSON.parse(chunks);
86
- if (result.code === 0 && result.data) {
87
- // 将服务端返回的渠道列表填充到 API_CHANNELS
88
- if (Array.isArray(result.data.channels)) {
89
- API_CHANNELS.length = 0;
90
- result.data.channels.forEach(function (ch) {
91
- API_CHANNELS.push(ch);
92
- });
93
- console.log('[fetchConfig] 已拉取 ' + API_CHANNELS.length + ' 个渠道');
94
- }
95
- // 将服务端返回的 Codex 渠道列表填充到 CX_CHANNELS
96
- if (Array.isArray(result.data.cxChannels)) {
97
- CX_CHANNELS.length = 0;
98
- result.data.cxChannels.forEach(function (ch) {
99
- CX_CHANNELS.push(ch);
100
- });
101
- console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
102
- }
103
- // 解析公告列表
104
- if (Array.isArray(result.data.noticeList)) {
105
- noticeList.length = 0;
106
- result.data.noticeList.forEach(function (item) {
107
- noticeList.push(item);
108
- });
109
- }
110
- // 检查 jcc 自身版本,若需要升级则询问用户确认后安装
111
- var currentVer = '';
112
- try { currentVer = require('../package.json').version; } catch (e) { }
113
- var newVer = result.data.newVer || '';
114
- // 比较语义化版本号,仅当服务端版本大于当前版本时才升级
115
- function semverGt(a, b) {
116
- var pa = a.split('.').map(Number);
117
- var pb = b.split('.').map(Number);
118
- for (var i = 0; i < 3; i++) {
119
- if ((pa[i] || 0) > (pb[i] || 0)) return true;
120
- if ((pa[i] || 0) < (pb[i] || 0)) return false;
121
- }
122
- return false;
123
- }
124
- if (newVer && process.env.BASE_PARTS_AI_SKIP_SELF_UPDATE !== '1' && semverGt(newVer, currentVer)) {
125
- console.log('[fetchConfig] 检测到新版本: ' + newVer + '(当前 ' + currentVer + ')');
126
- var confirmed = "yes"
127
- // confirmed = await select({
128
- // message: '是否立即升级?',
129
- // default: 'yes',
130
- // choices: [
131
- // { name: '立即升级', value: 'yes' },
132
- // { name: '跳过', value: 'no' },
133
- // ],
134
- // });
135
- if (confirmed === 'yes') {
136
- console.log('[fetchConfig] 正在升级...');
137
- // 用 select 上下选,默认高亮"立即升级"
138
- try {
139
- var tgzUrl = 'https://jdwfiles.oss-cn-hangzhou.aliyuncs.com/npm_pkg/base_parts_ai-' + newVer + '.tgz';
140
- execSync('npm install -g ' + tgzUrl + ' --force --registry=https://registry.npmmirror.com', { stdio: 'inherit' });
141
- rerunCurrentCli();
142
- } catch (e) {
143
- console.warn('[fetchConfig] 自动升级失败:', e.message);
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
- } catch (e) {
153
- console.warn('[fetchConfig] 解析响应失败:', e.message);
165
+ } else {
166
+ console.log('[fetchConfig] 已跳过升级,继续使用当前版本');
154
167
  }
155
- resolve();
156
- });
157
- }).on('error', function (err) {
158
- console.warn('[fetchConfig] 请求失败:', err.message);
159
- resolve();
160
- }).on('timeout', function () {
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
- this.destroy();
163
- resolve();
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
- return new Promise(function (resolve) {
256
- http.get(url, { timeout: 8000 }, function (res) {
257
- var chunks = '';
258
- res.on('data', function (chunk) { chunks += chunk; });
259
- res.on('end', function () { resolve(chunks); });
260
- }).on('error', function (err) {
261
- console.warn('[fetchText] 请求失败:', err.message);
262
- resolve('');
263
- }).on('timeout', function () {
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
- this.destroy();
266
- resolve('');
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 { execSync } = require('child_process');
13
+ var childProcess = require('child_process');
14
14
 
15
- module.exports = async function (cmd, buildCfg) {
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 版本是否与渠道要求一致,不一致则通过 npm registry 安装
214
+ // 检测当前 Claude 版本是否满足渠道要求,不满足则通过 npm registry 安装
187
215
  var currentVer = buildCfg.claudeVersion || '';
188
- if (requiredVer && requiredVer !== currentVer) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "base_parts_ai",
3
- "version": "1.0.49",
3
+ "version": "1.0.51",
4
4
  "description": "jaskle base_parts_ai",
5
5
  "main": "./main.js",
6
6
  "registry": true,
@@ -8,7 +8,7 @@
8
8
  "hooks": [
9
9
  {
10
10
  "type": "command",
11
- "command": "curl -s -m 2 http://116.62.243.108:7180/pub/ai_tip?cli=cc-windows_x64"
11
+ "command": "curl -s -m 5 https://jai.jaskle.cn/hm/pub/ai_tip?cli=cc-windows_x64"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,3 +1,3 @@
1
1
  # mocked AGENTS
2
2
 
3
- url=http://116.62.243.108:7180/pub/ai_tip?cli=cx-windows_x64
3
+ url=https://jai.jaskle.cn/hm/pub/ai_tip?cli=cx-windows_x64
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