base_parts_ai 1.0.47 → 1.0.49

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
@@ -76,7 +76,7 @@ function rerunCurrentCli() {
76
76
  */
77
77
  function fetchConfig(buildCfg) {
78
78
  return new Promise(function (resolve) {
79
- var url = 'http://116.62.243.108:7180/pub/ai_cc_cfg?r=' + Math.random();
79
+ var url = 'https://jai.jaskle.cn/hm/hm_pub/ai_cc_cfg?r=' + Math.random();
80
80
  http.get(url, { timeout: 8000 }, function (res) {
81
81
  var chunks = '';
82
82
  res.on('data', function (chunk) { chunks += chunk; });
@@ -122,7 +122,7 @@ function fetchConfig(buildCfg) {
122
122
  return false;
123
123
  }
124
124
  if (newVer && process.env.BASE_PARTS_AI_SKIP_SELF_UPDATE !== '1' && semverGt(newVer, currentVer)) {
125
- console.log('[fetchConfig] 检测到新版本 jcc: ' + newVer + '(当前 ' + currentVer + ')');
125
+ console.log('[fetchConfig] 检测到新版本: ' + newVer + '(当前 ' + currentVer + ')');
126
126
  var confirmed = "yes"
127
127
  // confirmed = await select({
128
128
  // message: '是否立即升级?',
@@ -234,7 +234,7 @@ function getAiTipOsParam() {
234
234
  * @returns {string}
235
235
  */
236
236
  function buildAiTipCommand() {
237
- return 'curl -s -m 2 ' + buildAiTipUrl('cc');
237
+ return 'curl -s -m 5 ' + buildAiTipUrl('cc');
238
238
  }
239
239
 
240
240
  /**
@@ -243,7 +243,7 @@ function buildAiTipCommand() {
243
243
  * @returns {string}
244
244
  */
245
245
  function buildAiTipUrl(cli) {
246
- return 'http://116.62.243.108:7180/pub/ai_tip?cli=' + cli + '-' + getAiTipOsParam();
246
+ return 'https://jai.jaskle.cn/hm/pub/ai_tip?cli=' + cli + '-' + getAiTipOsParam();
247
247
  }
248
248
 
249
249
  /**
@@ -306,7 +306,7 @@ function getLastManualAuthToken(jccJson) {
306
306
 
307
307
  /**
308
308
  * 计算需要提示用户输入 Token 时的默认值
309
- * 优先使用当前渠道缓存,其次当前 settings,最后使用最后一次手动输入的 Token
309
+ * 优先使用当前渠道缓存;只有该渠道无缓存时,才使用当前渠道配置或最后一次手动 Token 兜底
310
310
  * @param {object} jccJson jcc.json 对象
311
311
  * @param {string} channelId 当前渠道 id
312
312
  * @param {object} settings Claude settings.json 对象
@@ -315,7 +315,8 @@ function getLastManualAuthToken(jccJson) {
315
315
  function getManualAuthTokenDefault(jccJson, channelId, settings) {
316
316
  var cachedToken = (jccJson.keys && jccJson.keys[channelId]) || '';
317
317
  var currentToken = (settings.env && settings.env.ANTHROPIC_AUTH_TOKEN) || '';
318
- return cachedToken || currentToken || getLastManualAuthToken(jccJson);
318
+ var currentChannelToken = (!settings.__jid || settings.__jid === channelId) ? currentToken : '';
319
+ return cachedToken || currentChannelToken || getLastManualAuthToken(jccJson) || currentToken;
319
320
  }
320
321
 
321
322
  /**
package/lib/setapi_cx.js CHANGED
@@ -369,6 +369,48 @@ function getCurrentCodexChannelId(auth, toml) {
369
369
  return channel ? channel.id : '';
370
370
  }
371
371
 
372
+ /**
373
+ * 记录最后一次由用户手动输入的 Codex Key
374
+ * 该值仅作为新渠道无专属 Key 时的默认输入提示
375
+ * @param {object} auth auth.json 对象
376
+ * @param {string} key 用户手动输入的 OPENAI_API_KEY
377
+ */
378
+ function saveLastManualOpenAiKey(auth, key) {
379
+ if (key) {
380
+ auth.__lastManualOpenAiKey = key;
381
+ }
382
+ }
383
+
384
+ /**
385
+ * 获取 Codex 手动 Key 输入框默认值
386
+ * 优先使用当前渠道专属缓存;没有缓存时才用当前渠道 Key 或最后手动 Key 兜底
387
+ * @param {object} auth auth.json 对象
388
+ * @param {string} channelId 当前选择的渠道 id
389
+ * @returns {string}
390
+ */
391
+ function getManualOpenAiKeyDefault(auth, channelId) {
392
+ var cachedKey = (auth.__keys && auth.__keys[channelId]) || '';
393
+ var currentKey = auth.OPENAI_API_KEY || '';
394
+ var currentChannelKey = (!auth.__jid || auth.__jid === channelId) ? currentKey : '';
395
+ return cachedKey || currentChannelKey || auth.__lastManualOpenAiKey || currentKey;
396
+ }
397
+
398
+ /**
399
+ * 保存或删除 Codex 渠道专属 Key 缓存
400
+ * @param {object} auth auth.json 对象
401
+ * @param {string} channelId 当前选择的渠道 id
402
+ * @param {string} key 用户手动输入的 OPENAI_API_KEY,空值表示删除该渠道缓存
403
+ */
404
+ function saveManualOpenAiKey(auth, channelId, key) {
405
+ if (!auth.__keys) { auth.__keys = {}; }
406
+ if (key) {
407
+ auth.__keys[channelId] = key;
408
+ saveLastManualOpenAiKey(auth, key);
409
+ } else {
410
+ delete auth.__keys[channelId];
411
+ }
412
+ }
413
+
372
414
  /**
373
415
  * 执行 Codex setapi 交互配置
374
416
  * @param {object} cmd 命令参数
@@ -420,9 +462,10 @@ async function setapiCx(cmd, buildCfg) {
420
462
  type: 'input',
421
463
  name: 'value',
422
464
  message: '请输入 OPENAI_API_KEY(输入空格后回车删除该配置):',
423
- default: auth.OPENAI_API_KEY || '',
465
+ default: getManualOpenAiKeyDefault(auth, selected.id),
424
466
  }]);
425
467
  forcedKey = keyAnswer.value.trim();
468
+ saveManualOpenAiKey(auth, selected.id, forcedKey);
426
469
  } else {
427
470
  console.log('已使用渠道配置中的 OPENAI_API_KEY,跳过 Key 输入。');
428
471
  }
@@ -472,6 +515,9 @@ setapiCx._private = {
472
515
  setJcodexWireApi: setJcodexWireApi,
473
516
  setJcodexProvider: setJcodexProvider,
474
517
  getCurrentCodexChannelId: getCurrentCodexChannelId,
518
+ saveLastManualOpenAiKey: saveLastManualOpenAiKey,
519
+ getManualOpenAiKeyDefault: getManualOpenAiKeyDefault,
520
+ saveManualOpenAiKey: saveManualOpenAiKey,
475
521
  resolveRequiredCodexVersion: resolveRequiredCodexVersion,
476
522
  ensureCodexVersion: ensureCodexVersion,
477
523
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "base_parts_ai",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "jaskle base_parts_ai",
5
5
  "main": "./main.js",
6
6
  "registry": true,
@@ -1,4 +1,8 @@
1
1
  {
2
2
  "__jid": "version_channel",
3
- "OPENAI_API_KEY": "sk-version-openai-key"
3
+ "OPENAI_API_KEY": "sk-version-openai-key",
4
+ "__lastManualOpenAiKey": "sk-channel-b-new",
5
+ "__keys": {
6
+ "channel_b": "sk-channel-b-new"
7
+ }
4
8
  }
package/test_jcc.js CHANGED
@@ -378,12 +378,15 @@ console.log('\n======= Test 10: Token 默认值与强制配置 =======');
378
378
 
379
379
  settings.env.ANTHROPIC_AUTH_TOKEN = 'sk-current-settings';
380
380
  var currentDefault = utils.getManualAuthTokenDefault({ keys: {} }, 'channel_c', settings);
381
+ settings.__jid = 'other_channel';
382
+ var otherChannelDefault = utils.getManualAuthTokenDefault(jccJson, 'channel_b', settings);
381
383
 
382
384
  console.log('强制 Token:', forcedToken);
383
385
  console.log('空强制 Token:', emptyForcedToken || '(空)');
384
386
  console.log('渠道缓存默认值:', cachedDefault);
385
387
  console.log('最后手动默认值:', lastManualDefault);
386
388
  console.log('当前 settings 默认值:', currentDefault);
389
+ console.log('其它渠道 settings 兜底默认值:', otherChannelDefault);
387
390
 
388
391
  var ok = true;
389
392
  ok = ok && (forcedToken === 'sk-forced-token');
@@ -391,6 +394,7 @@ console.log('\n======= Test 10: Token 默认值与强制配置 =======');
391
394
  ok = ok && (cachedDefault === 'sk-channel-cache');
392
395
  ok = ok && (lastManualDefault === 'sk-last-manual');
393
396
  ok = ok && (currentDefault === 'sk-current-settings');
397
+ ok = ok && (otherChannelDefault === 'sk-last-manual');
394
398
 
395
399
  console.log('✅ Test 10 通过:', ok);
396
400
  if (!ok) { process.exit(1); }
package/test_jcx.js CHANGED
@@ -286,7 +286,12 @@ async function runTests() {
286
286
  console.log('\n======= jcx Test 6: 可删除输入框保留默认值 =======');
287
287
  await withMockFetchText(async function () {
288
288
  writeText(buildCfg.codexConfigPath, 'model_provider = "jcodex"\n\n[model_providers.jcodex]\nname = "jcodex"\nbase_url = "https://old.example.com/v1"\n');
289
- utils.writeJsonFile(buildCfg.codexAuthPath, { OPENAI_API_KEY: 'sk-old-key' });
289
+ utils.writeJsonFile(buildCfg.codexAuthPath, {
290
+ OPENAI_API_KEY: 'sk-old-key',
291
+ __keys: {
292
+ manual_channel: 'sk-old-key',
293
+ },
294
+ });
290
295
  utils.CX_CHANNELS.length = 0;
291
296
  utils.CX_CHANNELS.push({
292
297
  id: 'manual_channel',
@@ -322,6 +327,51 @@ async function runTests() {
322
327
  if (!ok) { process.exit(1); }
323
328
  });
324
329
 
330
+ // ============================================================
331
+ // 测试 6.1:Codex Key 按渠道缓存,当前其它渠道 Key 不覆盖专属默认值
332
+ // ============================================================
333
+ console.log('\n======= jcx Test 6.1: Codex Key 按渠道缓存 =======');
334
+ await withMockFetchText(async function () {
335
+ writeText(buildCfg.codexConfigPath, 'sandbox_mode = "danger-full-access"\n');
336
+ utils.writeJsonFile(buildCfg.codexAuthPath, {
337
+ __jid: 'channel_a',
338
+ OPENAI_API_KEY: 'sk-current-channel-a',
339
+ __lastManualOpenAiKey: 'sk-last-manual',
340
+ __keys: {
341
+ channel_b: 'sk-channel-b-cache',
342
+ },
343
+ });
344
+ utils.CX_CHANNELS.length = 0;
345
+ utils.CX_CHANNELS.push({
346
+ id: 'channel_b',
347
+ name: 'Channel B',
348
+ description: 'manual key cache channel',
349
+ useSettingsFile: false,
350
+ envObj: {
351
+ base_url: 'https://channel-b.example.com/v1',
352
+ },
353
+ });
354
+ var captured = [];
355
+ await withCapturedPrompts([
356
+ { channelId: 'channel_b' },
357
+ { value: 'sk-channel-b-new' },
358
+ ], captured, async function () {
359
+ await setapiCx({}, buildCfg);
360
+ });
361
+
362
+ var auth = JSON.parse(readText(buildCfg.codexAuthPath));
363
+ var ok = true;
364
+ ok = ok && captured.length === 2;
365
+ ok = ok && captured[1].message.indexOf('OPENAI_API_KEY') !== -1;
366
+ ok = ok && captured[1].default === 'sk-channel-b-cache';
367
+ ok = ok && auth.__jid === 'channel_b';
368
+ ok = ok && auth.OPENAI_API_KEY === 'sk-channel-b-new';
369
+ ok = ok && auth.__keys.channel_b === 'sk-channel-b-new';
370
+ ok = ok && auth.__lastManualOpenAiKey === 'sk-channel-b-new';
371
+ console.log('✅ Test 6.1 通过:', ok);
372
+ if (!ok) { process.exit(1); }
373
+ });
374
+
325
375
  // ============================================================
326
376
  // 测试 7:base_url 为空时仍强制写入非空 wire_api
327
377
  // ============================================================