base_parts_ai 1.0.53 → 1.0.54
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 +3 -3
- package/lib/setapi_cc.js +66 -25
- package/lib/setapi_cx.js +76 -13
- package/package.json +1 -1
package/lib/ai_utils.js
CHANGED
|
@@ -117,7 +117,7 @@ async function fetchConfig(buildCfg) {
|
|
|
117
117
|
result.data.channels.forEach(function (ch) {
|
|
118
118
|
API_CHANNELS.push(ch);
|
|
119
119
|
});
|
|
120
|
-
console.log('[fetchConfig] 已拉取 ' + API_CHANNELS.length + ' 个渠道');
|
|
120
|
+
// console.log('[fetchConfig] 已拉取 ' + API_CHANNELS.length + ' 个渠道');
|
|
121
121
|
}
|
|
122
122
|
// 将服务端返回的 Codex 渠道列表填充到 CX_CHANNELS
|
|
123
123
|
if (Array.isArray(result.data.cxChannels)) {
|
|
@@ -125,7 +125,7 @@ async function fetchConfig(buildCfg) {
|
|
|
125
125
|
result.data.cxChannels.forEach(function (ch) {
|
|
126
126
|
CX_CHANNELS.push(ch);
|
|
127
127
|
});
|
|
128
|
-
console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
|
|
128
|
+
// console.log('[fetchConfig] 已拉取 ' + CX_CHANNELS.length + ' 个 Codex 渠道');
|
|
129
129
|
}
|
|
130
130
|
// 将服务端返回的 weds 渠道列表填充到 WEDS_CHANNELS
|
|
131
131
|
if (Array.isArray(result.data.wedsChannels)) {
|
|
@@ -133,7 +133,7 @@ async function fetchConfig(buildCfg) {
|
|
|
133
133
|
result.data.wedsChannels.forEach(function (ch) {
|
|
134
134
|
WEDS_CHANNELS.push(ch);
|
|
135
135
|
});
|
|
136
|
-
console.log('[fetchConfig] 已拉取 ' + WEDS_CHANNELS.length + ' 个 weds 渠道');
|
|
136
|
+
// console.log('[fetchConfig] 已拉取 ' + WEDS_CHANNELS.length + ' 个 weds 渠道');
|
|
137
137
|
}
|
|
138
138
|
// 解析公告列表
|
|
139
139
|
if (Array.isArray(result.data.noticeList)) {
|
package/lib/setapi_cc.js
CHANGED
|
@@ -9,9 +9,36 @@
|
|
|
9
9
|
|
|
10
10
|
// inquirer@8 使用传统 prompt API
|
|
11
11
|
var inquirer = require('inquirer');
|
|
12
|
+
var fs = require('fs');
|
|
12
13
|
var utils = require('./ai_utils');
|
|
13
14
|
var childProcess = require('child_process');
|
|
14
15
|
|
|
16
|
+
// Claude 配置清除选项使用固定内部值,避免与服务端渠道 id 冲突。
|
|
17
|
+
var CLEAR_CLAUDE_CONFIG = '__clear_claude_config__';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 校验手动输入的 URL 或 Key,禁止再通过空白输入触发删除操作
|
|
21
|
+
* @param {string} value 用户输入值
|
|
22
|
+
* @returns {boolean|string}
|
|
23
|
+
*/
|
|
24
|
+
function validateRequiredInput(value) {
|
|
25
|
+
return String(value || '').trim().length > 0 ? true : '配置不能为空,请输入有效内容';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 删除 Claude 的两份生效配置文件,恢复客户端官方默认配置
|
|
30
|
+
* @param {object} buildCfg Claude 配置路径
|
|
31
|
+
*/
|
|
32
|
+
function clearClaudeConfig(buildCfg) {
|
|
33
|
+
[buildCfg.claudeJsonPath, buildCfg.claudeSettingsPath].forEach(function (filePath) {
|
|
34
|
+
try {
|
|
35
|
+
fs.rmSync(filePath, { force: true });
|
|
36
|
+
} catch (e) {
|
|
37
|
+
throw new Error('清除 Claude 配置失败(' + filePath + '): ' + e.message);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
/**
|
|
16
43
|
* 校验并安装渠道要求的 Claude Code 版本
|
|
17
44
|
* @param {string} requiredVer 渠道要求版本,latest 表示仅确保已安装
|
|
@@ -50,10 +77,6 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
50
77
|
// 显示当前配置状态
|
|
51
78
|
utils.printCurrentInfo(settings, channels);
|
|
52
79
|
|
|
53
|
-
if (!channels.length) {
|
|
54
|
-
throw new Error('Claude 渠道列表为空,请检查网络或服务端配置');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
80
|
// 构建渠道选择列表
|
|
58
81
|
var choices = channels.map(function (c) {
|
|
59
82
|
return {
|
|
@@ -61,6 +84,11 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
61
84
|
value: c.id,
|
|
62
85
|
};
|
|
63
86
|
});
|
|
87
|
+
// 清除入口固定放在渠道列表最后,即使服务端渠道为空也可恢复官方配置。
|
|
88
|
+
choices.push({
|
|
89
|
+
name: '>>>>清除Claude配置<<<< — 删除并恢复官方默认配置',
|
|
90
|
+
value: CLEAR_CLAUDE_CONFIG,
|
|
91
|
+
});
|
|
64
92
|
|
|
65
93
|
// 弹出渠道选择列表,默认选中当前渠道
|
|
66
94
|
var answers = await inquirer.prompt([{
|
|
@@ -72,6 +100,26 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
72
100
|
}]);
|
|
73
101
|
var channelId = answers.channelId;
|
|
74
102
|
|
|
103
|
+
if (channelId === CLEAR_CLAUDE_CONFIG) {
|
|
104
|
+
var clearAnswer = await inquirer.prompt([{
|
|
105
|
+
type: 'confirm',
|
|
106
|
+
name: 'confirmed',
|
|
107
|
+
message: '确认删除 Claude 配置文件并恢复官方默认配置吗?',
|
|
108
|
+
default: false,
|
|
109
|
+
}]);
|
|
110
|
+
if (!clearAnswer.confirmed) {
|
|
111
|
+
console.log('已取消清除 Claude 配置。');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
clearClaudeConfig(buildCfg);
|
|
115
|
+
console.log('✅ 已清除 Claude 配置,客户端将使用官方默认配置。');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!channels.length) {
|
|
120
|
+
throw new Error('Claude 渠道列表为空,请检查网络或服务端配置');
|
|
121
|
+
}
|
|
122
|
+
|
|
75
123
|
// 找到选中的渠道配置
|
|
76
124
|
var selected = channels.find(function (c) { return c.id === channelId; });
|
|
77
125
|
|
|
@@ -121,20 +169,16 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
121
169
|
var baseUrlAnswer = await inquirer.prompt([{
|
|
122
170
|
type: 'input',
|
|
123
171
|
name: 'value',
|
|
124
|
-
message: '请输入 ANTHROPIC_BASE_URL
|
|
172
|
+
message: '请输入 ANTHROPIC_BASE_URL:',
|
|
125
173
|
default: defaultBaseUrl,
|
|
174
|
+
validate: validateRequiredInput,
|
|
126
175
|
}]);
|
|
127
176
|
var inputBaseUrl = baseUrlAnswer.value.trim();
|
|
128
177
|
|
|
129
|
-
//
|
|
178
|
+
// 手动 URL 通过非空校验后写入配置及渠道缓存。
|
|
130
179
|
if (!jccJson.keys) { jccJson.keys = {}; }
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
jccJson.keys[selected.id + '_baseUrl'] = inputBaseUrl;
|
|
134
|
-
} else {
|
|
135
|
-
delete settings.env.ANTHROPIC_BASE_URL;
|
|
136
|
-
delete jccJson.keys[selected.id + '_baseUrl'];
|
|
137
|
-
}
|
|
180
|
+
settings.env.ANTHROPIC_BASE_URL = inputBaseUrl;
|
|
181
|
+
jccJson.keys[selected.id + '_baseUrl'] = inputBaseUrl;
|
|
138
182
|
|
|
139
183
|
if (forcedAuthToken) {
|
|
140
184
|
// 服务端强制 Token 不写入手动 Token 缓存,避免污染后续默认值
|
|
@@ -146,18 +190,14 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
146
190
|
var tokenAnswer = await inquirer.prompt([{
|
|
147
191
|
type: 'input',
|
|
148
192
|
name: 'value',
|
|
149
|
-
message: '请输入 ANTHROPIC_AUTH_TOKEN
|
|
193
|
+
message: '请输入 ANTHROPIC_AUTH_TOKEN:',
|
|
150
194
|
default: defaultToken,
|
|
195
|
+
validate: validateRequiredInput,
|
|
151
196
|
}]);
|
|
152
197
|
var inputToken = tokenAnswer.value.trim();
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
utils.saveLastManualAuthToken(jccJson, inputToken);
|
|
157
|
-
} else {
|
|
158
|
-
delete settings.env.ANTHROPIC_AUTH_TOKEN;
|
|
159
|
-
delete jccJson.keys[selected.id];
|
|
160
|
-
}
|
|
198
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = inputToken;
|
|
199
|
+
jccJson.keys[selected.id] = inputToken;
|
|
200
|
+
utils.saveLastManualAuthToken(jccJson, inputToken);
|
|
161
201
|
}
|
|
162
202
|
utils.writeJsonFile(cachePath, jccJson);
|
|
163
203
|
|
|
@@ -178,9 +218,7 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
178
218
|
name: 'value',
|
|
179
219
|
message: '请输入 ANTHROPIC_AUTH_TOKEN:',
|
|
180
220
|
default: savedKey,
|
|
181
|
-
validate:
|
|
182
|
-
return v.trim().length > 0 ? true : 'Key 不能为空';
|
|
183
|
-
},
|
|
221
|
+
validate: validateRequiredInput,
|
|
184
222
|
}]);
|
|
185
223
|
var newKey = keyAnswer.value.trim();
|
|
186
224
|
settings.env.ANTHROPIC_AUTH_TOKEN = newKey;
|
|
@@ -235,6 +273,9 @@ async function setapiCc(cmd, buildCfg) {
|
|
|
235
273
|
|
|
236
274
|
setapiCc._private = {
|
|
237
275
|
ensureClaudeVersion: ensureClaudeVersion,
|
|
276
|
+
validateRequiredInput: validateRequiredInput,
|
|
277
|
+
clearClaudeConfig: clearClaudeConfig,
|
|
278
|
+
CLEAR_CLAUDE_CONFIG: CLEAR_CLAUDE_CONFIG,
|
|
238
279
|
};
|
|
239
280
|
|
|
240
281
|
module.exports = setapiCc;
|
package/lib/setapi_cx.js
CHANGED
|
@@ -12,6 +12,32 @@ var utils = require('./ai_utils');
|
|
|
12
12
|
var childProcess = require('child_process');
|
|
13
13
|
var TOML = require('smol-toml');
|
|
14
14
|
|
|
15
|
+
// Codex 配置清除选项使用固定内部值,避免与服务端渠道 id 冲突。
|
|
16
|
+
var CLEAR_CODEX_CONFIG = '__clear_codex_config__';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 校验手动输入的 URL 或 Key,禁止再通过空白输入触发删除操作
|
|
20
|
+
* @param {string} value 用户输入值
|
|
21
|
+
* @returns {boolean|string}
|
|
22
|
+
*/
|
|
23
|
+
function validateRequiredInput(value) {
|
|
24
|
+
return String(value || '').trim().length > 0 ? true : '配置不能为空,请输入有效内容';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 删除 Codex 的两份生效配置文件,恢复客户端官方默认配置
|
|
29
|
+
* @param {object} buildCfg Codex 配置路径
|
|
30
|
+
*/
|
|
31
|
+
function clearCodexConfig(buildCfg) {
|
|
32
|
+
[buildCfg.codexAuthPath, buildCfg.codexConfigPath].forEach(function (filePath) {
|
|
33
|
+
try {
|
|
34
|
+
fs.rmSync(filePath, { force: true });
|
|
35
|
+
} catch (e) {
|
|
36
|
+
throw new Error('清除 Codex 配置失败(' + filePath + '): ' + e.message);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
/**
|
|
16
42
|
* 读取文本文件,文件不存在时返回空字符串
|
|
17
43
|
* @param {string} filePath 文件路径
|
|
@@ -71,6 +97,11 @@ function parseTomlConfig(toml) {
|
|
|
71
97
|
*/
|
|
72
98
|
function stringifyTomlConfig(config) {
|
|
73
99
|
var content = TOML.stringify(config || {});
|
|
100
|
+
// smol-toml 默认把嵌套对象输出为子表;此固定请求头需按 Codex 支持的内联表格式写入。
|
|
101
|
+
content = content.replace(
|
|
102
|
+
/\n\[model_providers\.jcodex\.http_headers\]\n(?:"?x-openai-actor-authorization"?) = "local-image-extension"\n/g,
|
|
103
|
+
'\nhttp_headers = { "x-openai-actor-authorization" = "local-image-extension" }\n'
|
|
104
|
+
);
|
|
74
105
|
return content ? content.replace(/\s+$/, '') + '\n' : '';
|
|
75
106
|
}
|
|
76
107
|
|
|
@@ -219,6 +250,11 @@ function setJcodexProvider(toml, baseUrl, wireApi) {
|
|
|
219
250
|
providers.jcodex = {
|
|
220
251
|
name: 'jcodex',
|
|
221
252
|
base_url: baseUrl,
|
|
253
|
+
// jcodex 使用本地图片扩展身份,不要求 Codex 注入 OpenAI 鉴权。
|
|
254
|
+
requires_openai_auth: false,
|
|
255
|
+
http_headers: {
|
|
256
|
+
'x-openai-actor-authorization': 'local-image-extension',
|
|
257
|
+
},
|
|
222
258
|
};
|
|
223
259
|
// wire_api 是 Codex provider 私有字段,非空时强制写入到 model_providers.jcodex 下
|
|
224
260
|
if (wireApi) {
|
|
@@ -255,6 +291,11 @@ function setJcodexWireApi(toml, wireApi) {
|
|
|
255
291
|
if (!provider.name) {
|
|
256
292
|
provider.name = 'jcodex';
|
|
257
293
|
}
|
|
294
|
+
// 即使渠道只配置 wire_api,也要补齐 jcodex 的固定鉴权参数。
|
|
295
|
+
provider.requires_openai_auth = false;
|
|
296
|
+
provider.http_headers = {
|
|
297
|
+
'x-openai-actor-authorization': 'local-image-extension',
|
|
298
|
+
};
|
|
258
299
|
provider.wire_api = wireApi;
|
|
259
300
|
return stringifyTomlConfig(config);
|
|
260
301
|
}
|
|
@@ -427,16 +468,17 @@ async function setapiCx(cmd, buildCfg) {
|
|
|
427
468
|
|
|
428
469
|
printCurrentCodexInfo(auth, toml);
|
|
429
470
|
|
|
430
|
-
if (!utils.CX_CHANNELS.length) {
|
|
431
|
-
throw new Error('Codex 渠道列表为空,请检查网络或服务端 cxChannels 配置');
|
|
432
|
-
}
|
|
433
|
-
|
|
434
471
|
var choices = utils.CX_CHANNELS.map(function (c) {
|
|
435
472
|
return {
|
|
436
473
|
name: c.name + ' — ' + (c.description || ''),
|
|
437
474
|
value: c.id,
|
|
438
475
|
};
|
|
439
476
|
});
|
|
477
|
+
// 清除入口固定放在渠道列表最后,即使服务端渠道为空也可恢复官方配置。
|
|
478
|
+
choices.push({
|
|
479
|
+
name: '>>>>清除Codex配置<<<< — 删除并恢复官方默认配置',
|
|
480
|
+
value: CLEAR_CODEX_CONFIG,
|
|
481
|
+
});
|
|
440
482
|
|
|
441
483
|
var answers = await inquirer.prompt([{
|
|
442
484
|
type: 'list',
|
|
@@ -445,19 +487,40 @@ async function setapiCx(cmd, buildCfg) {
|
|
|
445
487
|
choices: choices,
|
|
446
488
|
default: getCurrentCodexChannelId(auth, toml) || undefined,
|
|
447
489
|
}]);
|
|
490
|
+
if (answers.channelId === CLEAR_CODEX_CONFIG) {
|
|
491
|
+
var clearAnswer = await inquirer.prompt([{
|
|
492
|
+
type: 'confirm',
|
|
493
|
+
name: 'confirmed',
|
|
494
|
+
message: '确认删除 Codex 配置文件并恢复官方默认配置吗?',
|
|
495
|
+
default: false,
|
|
496
|
+
}]);
|
|
497
|
+
if (!clearAnswer.confirmed) {
|
|
498
|
+
console.log('已取消清除 Codex 配置。');
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
clearCodexConfig(buildCfg);
|
|
502
|
+
console.log('✅ 已清除 Codex 配置,客户端将使用官方默认配置。');
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (!utils.CX_CHANNELS.length) {
|
|
507
|
+
throw new Error('Codex 渠道列表为空,请检查网络或服务端 cxChannels 配置');
|
|
508
|
+
}
|
|
509
|
+
|
|
448
510
|
var selected = utils.CX_CHANNELS.find(function (c) { return c.id === answers.channelId; });
|
|
449
511
|
var forcedKey = getEnvString(selected, 'OPENAI_API_KEY');
|
|
450
512
|
var wireApi = getEnvString(selected, 'wire_api');
|
|
451
513
|
var baseUrl = getEnvString(selected, 'base_url');
|
|
452
514
|
|
|
453
|
-
// useSettingsFile 或自定义渠道允许手动指定 base_url
|
|
515
|
+
// useSettingsFile 或自定义渠道允许手动指定 base_url,空白输入由校验阻止。
|
|
454
516
|
var allowManualBaseUrl = selected.useSettingsFile || String(selected.id || '').indexOf('custom') !== -1;
|
|
455
517
|
if (allowManualBaseUrl) {
|
|
456
518
|
var baseAnswer = await inquirer.prompt([{
|
|
457
519
|
type: 'input',
|
|
458
520
|
name: 'value',
|
|
459
|
-
message: '请输入 Codex base_url
|
|
521
|
+
message: '请输入 Codex base_url:',
|
|
460
522
|
default: baseUrl || getJcodexBaseUrl(toml) || '',
|
|
523
|
+
validate: validateRequiredInput,
|
|
461
524
|
}]);
|
|
462
525
|
baseUrl = baseAnswer.value.trim();
|
|
463
526
|
}
|
|
@@ -466,8 +529,9 @@ async function setapiCx(cmd, buildCfg) {
|
|
|
466
529
|
var keyAnswer = await inquirer.prompt([{
|
|
467
530
|
type: 'input',
|
|
468
531
|
name: 'value',
|
|
469
|
-
message: '请输入 OPENAI_API_KEY
|
|
532
|
+
message: '请输入 OPENAI_API_KEY:',
|
|
470
533
|
default: getManualOpenAiKeyDefault(auth, selected.id),
|
|
534
|
+
validate: validateRequiredInput,
|
|
471
535
|
}]);
|
|
472
536
|
forcedKey = keyAnswer.value.trim();
|
|
473
537
|
saveManualOpenAiKey(auth, selected.id, forcedKey);
|
|
@@ -478,12 +542,8 @@ async function setapiCx(cmd, buildCfg) {
|
|
|
478
542
|
// 将选中渠道 id 持久化到 auth.__jid,作为 Codex 当前渠道标识
|
|
479
543
|
auth.__jid = selected.id;
|
|
480
544
|
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
auth.OPENAI_API_KEY = forcedKey;
|
|
484
|
-
} else {
|
|
485
|
-
delete auth.OPENAI_API_KEY;
|
|
486
|
-
}
|
|
545
|
+
// Key 来自渠道强制配置或已通过非空校验的手动输入,直接写入认证文件。
|
|
546
|
+
auth.OPENAI_API_KEY = forcedKey;
|
|
487
547
|
utils.writeJsonFile(buildCfg.codexAuthPath, auth);
|
|
488
548
|
|
|
489
549
|
if (baseUrl) {
|
|
@@ -525,6 +585,9 @@ setapiCx._private = {
|
|
|
525
585
|
saveManualOpenAiKey: saveManualOpenAiKey,
|
|
526
586
|
resolveRequiredCodexVersion: resolveRequiredCodexVersion,
|
|
527
587
|
ensureCodexVersion: ensureCodexVersion,
|
|
588
|
+
validateRequiredInput: validateRequiredInput,
|
|
589
|
+
clearCodexConfig: clearCodexConfig,
|
|
590
|
+
CLEAR_CODEX_CONFIG: CLEAR_CODEX_CONFIG,
|
|
528
591
|
};
|
|
529
592
|
|
|
530
593
|
module.exports = setapiCx;
|