base_parts_ai 1.0.44 → 1.0.46
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/README.md +1 -1
- package/lib/setapi_cx.js +132 -149
- package/package.json +4 -3
- package/test_home/.claude/settings.json +1 -1
- package/test_home/.codex/AGENTS.md +1 -1
- package/test_home/.codex/config.toml +4 -1
- package/test_jcc.js +1 -1
- package/test_jcx.js +36 -1
package/README.md
CHANGED
package/lib/setapi_cx.js
CHANGED
|
@@ -10,6 +10,7 @@ var fs = require('fs');
|
|
|
10
10
|
var inquirer = require('inquirer');
|
|
11
11
|
var utils = require('./ai_utils');
|
|
12
12
|
var childProcess = require('child_process');
|
|
13
|
+
var TOML = require('smol-toml');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* 读取文本文件,文件不存在时返回空字符串
|
|
@@ -47,27 +48,63 @@ function getEnvString(channel, key) {
|
|
|
47
48
|
return typeof value === 'string' ? value.trim() : '';
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* 解析 Codex TOML 配置,格式错误时阻止继续覆盖用户配置
|
|
53
|
+
* @param {string} toml TOML 内容
|
|
54
|
+
* @returns {object}
|
|
55
|
+
*/
|
|
56
|
+
function parseTomlConfig(toml) {
|
|
57
|
+
if (!toml || !String(toml).trim()) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return TOML.parse(toml);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
throw new Error('Codex config.toml 格式错误,已停止写入以避免破坏配置: ' + e.message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 将 Codex TOML 配置对象序列化为文本
|
|
69
|
+
* @param {object} config TOML 配置对象
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
function stringifyTomlConfig(config) {
|
|
73
|
+
var content = TOML.stringify(config || {});
|
|
74
|
+
return content ? content.replace(/\s+$/, '') + '\n' : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 确保对象字段是可写 TOML table
|
|
79
|
+
* @param {object} parent 父级对象
|
|
80
|
+
* @param {string} key 字段名
|
|
81
|
+
* @returns {object}
|
|
82
|
+
*/
|
|
83
|
+
function ensureTable(parent, key) {
|
|
84
|
+
if (!parent[key] || typeof parent[key] !== 'object' || Array.isArray(parent[key])) {
|
|
85
|
+
// 字段不是 table 时用对象替换,避免生成非法的嵌套配置
|
|
86
|
+
parent[key] = {};
|
|
87
|
+
}
|
|
88
|
+
return parent[key];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 判断对象是否没有自有字段
|
|
93
|
+
* @param {object} value 待检查对象
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
function isEmptyObject(value) {
|
|
97
|
+
return value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
50
100
|
/**
|
|
51
101
|
* 从 TOML 文本中提取顶层 model_provider
|
|
52
102
|
* @param {string} toml TOML 内容
|
|
53
103
|
* @returns {string}
|
|
54
104
|
*/
|
|
55
105
|
function getTopLevelModelProvider(toml) {
|
|
56
|
-
var
|
|
57
|
-
|
|
58
|
-
for (var i = 0; i < lines.length; i++) {
|
|
59
|
-
var line = lines[i];
|
|
60
|
-
if (/^\s*\[/.test(line)) {
|
|
61
|
-
inSection = true;
|
|
62
|
-
}
|
|
63
|
-
if (!inSection) {
|
|
64
|
-
var match = line.match(/^\s*model_provider\s*=\s*["']([^"']+)["']/);
|
|
65
|
-
if (match) {
|
|
66
|
-
return match[1];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return '';
|
|
106
|
+
var config = parseTomlConfig(toml);
|
|
107
|
+
return typeof config.model_provider === 'string' ? config.model_provider : '';
|
|
71
108
|
}
|
|
72
109
|
|
|
73
110
|
/**
|
|
@@ -76,26 +113,13 @@ function getTopLevelModelProvider(toml) {
|
|
|
76
113
|
* @returns {string}
|
|
77
114
|
*/
|
|
78
115
|
function getJcodexBaseUrl(toml) {
|
|
79
|
-
var
|
|
80
|
-
var
|
|
81
|
-
|
|
82
|
-
var line = lines[i];
|
|
83
|
-
if (/^\s*\[/.test(line)) {
|
|
84
|
-
inJcodex = /^\s*\[model_providers\.jcodex\]\s*$/.test(line);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (inJcodex) {
|
|
88
|
-
var match = line.match(/^\s*base_url\s*=\s*["']([^"']*)["']/);
|
|
89
|
-
if (match) {
|
|
90
|
-
return match[1];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return '';
|
|
116
|
+
var config = parseTomlConfig(toml);
|
|
117
|
+
var provider = config.model_providers && config.model_providers.jcodex;
|
|
118
|
+
return provider && typeof provider.base_url === 'string' ? provider.base_url : '';
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
/**
|
|
98
|
-
* 转义 TOML
|
|
122
|
+
* 转义 TOML 字符串值,保留给测试和旧调用方使用
|
|
99
123
|
* @param {string} value 原始字符串
|
|
100
124
|
* @returns {string}
|
|
101
125
|
*/
|
|
@@ -103,25 +127,53 @@ function escapeTomlString(value) {
|
|
|
103
127
|
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
104
128
|
}
|
|
105
129
|
|
|
130
|
+
/**
|
|
131
|
+
* 判断 TOML 顶层是否已经存在指定配置项
|
|
132
|
+
* @param {string} toml TOML 内容
|
|
133
|
+
* @param {string} key 顶层配置项名称
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
function hasTopLevelKey(toml, key) {
|
|
137
|
+
var config = parseTomlConfig(toml);
|
|
138
|
+
return Object.prototype.hasOwnProperty.call(config, key);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 确保 Codex 配置包含默认 sandbox_mode,缺失时写入顶层配置
|
|
143
|
+
* @param {string} toml TOML 内容
|
|
144
|
+
* @returns {string}
|
|
145
|
+
*/
|
|
146
|
+
function ensureSandboxMode(toml) {
|
|
147
|
+
var config = parseTomlConfig(toml);
|
|
148
|
+
if (!Object.prototype.hasOwnProperty.call(config, 'sandbox_mode')) {
|
|
149
|
+
config.sandbox_mode = 'danger-full-access';
|
|
150
|
+
}
|
|
151
|
+
return stringifyTomlConfig(config);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 强制写入 Codex TUI 编辑器换行快捷键,已有值会被覆盖为 alt-enter
|
|
156
|
+
* @param {string} toml TOML 内容
|
|
157
|
+
* @returns {string}
|
|
158
|
+
*/
|
|
159
|
+
function ensureEditorInsertNewlineKeymap(toml) {
|
|
160
|
+
var config = parseTomlConfig(toml);
|
|
161
|
+
var tui = ensureTable(config, 'tui');
|
|
162
|
+
var keymap = ensureTable(tui, 'keymap');
|
|
163
|
+
var editor = ensureTable(keymap, 'editor');
|
|
164
|
+
editor.insert_newline = 'alt-enter';
|
|
165
|
+
return stringifyTomlConfig(config);
|
|
166
|
+
}
|
|
167
|
+
|
|
106
168
|
/**
|
|
107
169
|
* 删除顶层 model_provider 字段,保留其它配置段落
|
|
108
170
|
* @param {string} toml TOML 内容
|
|
109
171
|
* @returns {string}
|
|
110
172
|
*/
|
|
111
173
|
function removeTopLevelModelProvider(toml) {
|
|
112
|
-
var
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
lines.forEach(function (line) {
|
|
116
|
-
if (/^\s*\[/.test(line)) {
|
|
117
|
-
inSection = true;
|
|
118
|
-
}
|
|
119
|
-
if (!inSection && /^\s*model_provider\s*=/.test(line)) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
kept.push(line);
|
|
123
|
-
});
|
|
124
|
-
return kept.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '') + '\n';
|
|
174
|
+
var config = parseTomlConfig(toml);
|
|
175
|
+
delete config.model_provider;
|
|
176
|
+
return stringifyTomlConfig(config);
|
|
125
177
|
}
|
|
126
178
|
|
|
127
179
|
/**
|
|
@@ -130,22 +182,14 @@ function removeTopLevelModelProvider(toml) {
|
|
|
130
182
|
* @returns {string}
|
|
131
183
|
*/
|
|
132
184
|
function removeJcodexProviderBlock(toml) {
|
|
133
|
-
var
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
skipping = true;
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (skipping && /^\s*\[/.test(line)) {
|
|
142
|
-
skipping = false;
|
|
185
|
+
var config = parseTomlConfig(toml);
|
|
186
|
+
if (config.model_providers && typeof config.model_providers === 'object' && !Array.isArray(config.model_providers)) {
|
|
187
|
+
delete config.model_providers.jcodex;
|
|
188
|
+
if (isEmptyObject(config.model_providers)) {
|
|
189
|
+
delete config.model_providers;
|
|
143
190
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
return kept.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '');
|
|
191
|
+
}
|
|
192
|
+
return stringifyTomlConfig(config);
|
|
149
193
|
}
|
|
150
194
|
|
|
151
195
|
/**
|
|
@@ -156,44 +200,18 @@ function removeJcodexProviderBlock(toml) {
|
|
|
156
200
|
* @returns {string}
|
|
157
201
|
*/
|
|
158
202
|
function setJcodexProvider(toml, baseUrl, wireApi) {
|
|
159
|
-
var
|
|
160
|
-
var
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
result.push('model_provider = "jcodex"');
|
|
167
|
-
inserted = true;
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (!inserted && /^\s*\[/.test(line)) {
|
|
171
|
-
result.push('model_provider = "jcodex"');
|
|
172
|
-
result.push('');
|
|
173
|
-
inserted = true;
|
|
174
|
-
}
|
|
175
|
-
result.push(line);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
if (!inserted) {
|
|
179
|
-
if (result.length && result[result.length - 1].trim() !== '') {
|
|
180
|
-
result.push('');
|
|
181
|
-
}
|
|
182
|
-
result.unshift('model_provider = "jcodex"');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
var body = result.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '');
|
|
186
|
-
if (body) {
|
|
187
|
-
body += '\n\n';
|
|
188
|
-
}
|
|
189
|
-
body += '[model_providers.jcodex]\n';
|
|
190
|
-
body += 'name = "jcodex"\n';
|
|
191
|
-
body += 'base_url = "' + escapeTomlString(baseUrl) + '"\n';
|
|
203
|
+
var config = parseTomlConfig(toml);
|
|
204
|
+
var providers = ensureTable(config, 'model_providers');
|
|
205
|
+
config.model_provider = 'jcodex';
|
|
206
|
+
providers.jcodex = {
|
|
207
|
+
name: 'jcodex',
|
|
208
|
+
base_url: baseUrl,
|
|
209
|
+
};
|
|
192
210
|
// wire_api 是 Codex provider 私有字段,非空时强制写入到 model_providers.jcodex 下
|
|
193
211
|
if (wireApi) {
|
|
194
|
-
|
|
212
|
+
providers.jcodex.wire_api = wireApi;
|
|
195
213
|
}
|
|
196
|
-
return
|
|
214
|
+
return stringifyTomlConfig(config);
|
|
197
215
|
}
|
|
198
216
|
|
|
199
217
|
/**
|
|
@@ -202,20 +220,13 @@ function setJcodexProvider(toml, baseUrl, wireApi) {
|
|
|
202
220
|
* @returns {string}
|
|
203
221
|
*/
|
|
204
222
|
function removeJcodexWireApi(toml) {
|
|
205
|
-
var
|
|
206
|
-
var
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// wire_api 为空时只删除 jcodex provider 下的私有字段,不影响其它段落
|
|
213
|
-
if (inJcodex && /^\s*wire_api\s*=/.test(line)) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
kept.push(line);
|
|
217
|
-
});
|
|
218
|
-
return kept.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '') + '\n';
|
|
223
|
+
var config = parseTomlConfig(toml);
|
|
224
|
+
var provider = config.model_providers && config.model_providers.jcodex;
|
|
225
|
+
// wire_api 为空时只删除 jcodex provider 下的私有字段,不影响其它段落
|
|
226
|
+
if (provider && typeof provider === 'object' && !Array.isArray(provider)) {
|
|
227
|
+
delete provider.wire_api;
|
|
228
|
+
}
|
|
229
|
+
return stringifyTomlConfig(config);
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
/**
|
|
@@ -225,47 +236,14 @@ function removeJcodexWireApi(toml) {
|
|
|
225
236
|
* @returns {string}
|
|
226
237
|
*/
|
|
227
238
|
function setJcodexWireApi(toml, wireApi) {
|
|
228
|
-
var
|
|
229
|
-
var
|
|
230
|
-
var
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
function appendWireApiLine() {
|
|
234
|
-
// 写入前去掉段尾空行,确保 wire_api 留在 jcodex 段内部
|
|
235
|
-
while (kept.length && kept[kept.length - 1].trim() === '') {
|
|
236
|
-
kept.pop();
|
|
237
|
-
}
|
|
238
|
-
kept.push('wire_api = "' + escapeTomlString(wireApi) + '"');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
lines.forEach(function (line) {
|
|
242
|
-
if (/^\s*\[/.test(line)) {
|
|
243
|
-
if (inJcodex && !inserted) {
|
|
244
|
-
appendWireApiLine();
|
|
245
|
-
kept.push('');
|
|
246
|
-
inserted = true;
|
|
247
|
-
}
|
|
248
|
-
inJcodex = /^\s*\[model_providers\.jcodex\]\s*$/.test(line);
|
|
249
|
-
foundJcodex = foundJcodex || inJcodex;
|
|
250
|
-
}
|
|
251
|
-
kept.push(line);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
if (foundJcodex && !inserted) {
|
|
255
|
-
appendWireApiLine();
|
|
256
|
-
inserted = true;
|
|
257
|
-
}
|
|
258
|
-
if (!foundJcodex) {
|
|
259
|
-
var body = kept.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s+$/, '');
|
|
260
|
-
if (body) {
|
|
261
|
-
body += '\n\n';
|
|
262
|
-
}
|
|
263
|
-
body += '[model_providers.jcodex]\n';
|
|
264
|
-
body += 'name = "jcodex"\n';
|
|
265
|
-
body += 'wire_api = "' + escapeTomlString(wireApi) + '"\n';
|
|
266
|
-
return body;
|
|
239
|
+
var config = parseTomlConfig(toml);
|
|
240
|
+
var providers = ensureTable(config, 'model_providers');
|
|
241
|
+
var provider = ensureTable(providers, 'jcodex');
|
|
242
|
+
if (!provider.name) {
|
|
243
|
+
provider.name = 'jcodex';
|
|
267
244
|
}
|
|
268
|
-
|
|
245
|
+
provider.wire_api = wireApi;
|
|
246
|
+
return stringifyTomlConfig(config);
|
|
269
247
|
}
|
|
270
248
|
|
|
271
249
|
/**
|
|
@@ -453,6 +431,8 @@ async function setapiCx(cmd, buildCfg) {
|
|
|
453
431
|
toml = removeTopLevelModelProvider(toml);
|
|
454
432
|
toml = wireApi ? setJcodexWireApi(toml, wireApi) : removeJcodexWireApi(toml);
|
|
455
433
|
}
|
|
434
|
+
toml = ensureSandboxMode(toml);
|
|
435
|
+
toml = ensureEditorInsertNewlineKeymap(toml);
|
|
456
436
|
writeTextFile(buildCfg.codexConfigPath, toml);
|
|
457
437
|
|
|
458
438
|
await refreshAgentsFile(buildCfg.codexAgentsPath);
|
|
@@ -470,6 +450,9 @@ setapiCx._private = {
|
|
|
470
450
|
getTopLevelModelProvider: getTopLevelModelProvider,
|
|
471
451
|
getJcodexBaseUrl: getJcodexBaseUrl,
|
|
472
452
|
escapeTomlString: escapeTomlString,
|
|
453
|
+
hasTopLevelKey: hasTopLevelKey,
|
|
454
|
+
ensureSandboxMode: ensureSandboxMode,
|
|
455
|
+
ensureEditorInsertNewlineKeymap: ensureEditorInsertNewlineKeymap,
|
|
473
456
|
removeTopLevelModelProvider: removeTopLevelModelProvider,
|
|
474
457
|
removeJcodexProviderBlock: removeJcodexProviderBlock,
|
|
475
458
|
removeJcodexWireApi: removeJcodexWireApi,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "base_parts_ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "jaskle base_parts_ai",
|
|
5
5
|
"main": "./main.js",
|
|
6
6
|
"registry": true,
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
"jcx": "./bin/jcx.js"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
19
|
+
"node": ">=18"
|
|
20
20
|
},
|
|
21
21
|
"author": "jaskle",
|
|
22
22
|
"license": "ISC",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"commander": "^14.0.3",
|
|
25
|
-
"inquirer": "^8.2.7"
|
|
25
|
+
"inquirer": "^8.2.7",
|
|
26
|
+
"smol-toml": "^1.6.1"
|
|
26
27
|
}
|
|
27
28
|
}
|
package/test_jcc.js
CHANGED
|
@@ -206,7 +206,7 @@ console.log('\n======= Test 3: setapi 写入渠道配置(envObj 合并)=====
|
|
|
206
206
|
ok = ok && (s.language === '简体中文');
|
|
207
207
|
ok = ok && (s.autoUpdatesChannel === 'stable');
|
|
208
208
|
ok = ok && (typeof s.hooks === 'object');
|
|
209
|
-
ok = ok && (aiTipCommand.indexOf('
|
|
209
|
+
ok = ok && (aiTipCommand.indexOf('cli=cc-' + utils.getAiTipOsParam()) !== -1);
|
|
210
210
|
ok = ok && (aiTipCommand.indexOf('%OS%') === -1);
|
|
211
211
|
ok = ok && (j.keys[selected.id] === newKey);
|
|
212
212
|
ok = ok && (j.keys.__lastManualAuthToken === newKey);
|
package/test_jcx.js
CHANGED
|
@@ -157,6 +157,38 @@ async function runTests() {
|
|
|
157
157
|
if (!ok) { process.exit(1); }
|
|
158
158
|
})();
|
|
159
159
|
|
|
160
|
+
// ============================================================
|
|
161
|
+
// 测试 2.1:缺失 sandbox_mode 时写入默认顶层配置
|
|
162
|
+
// ============================================================
|
|
163
|
+
console.log('\n======= jcx Test 2.1: 缺失 sandbox_mode 自动补齐 =======');
|
|
164
|
+
(function () {
|
|
165
|
+
var original = 'model_provider = "jcodex"\n\n[tui]\ntheme = "dark"\n';
|
|
166
|
+
var updated = privateApi.ensureSandboxMode(original);
|
|
167
|
+
var ok = true;
|
|
168
|
+
ok = ok && updated.indexOf('sandbox_mode = "danger-full-access"') !== -1;
|
|
169
|
+
ok = ok && updated.indexOf('sandbox_mode = "danger-full-access"') < updated.indexOf('[tui]');
|
|
170
|
+
ok = ok && privateApi.ensureSandboxMode(updated).match(/sandbox_mode\s*=/g).length === 1;
|
|
171
|
+
console.log('✅ Test 2.1 通过:', ok);
|
|
172
|
+
if (!ok) { process.exit(1); }
|
|
173
|
+
})();
|
|
174
|
+
|
|
175
|
+
// ============================================================
|
|
176
|
+
// 测试 2.2:强制写入 TUI 编辑器换行快捷键
|
|
177
|
+
// ============================================================
|
|
178
|
+
console.log('\n======= jcx Test 2.2: 强制写入 TUI keymap =======');
|
|
179
|
+
(function () {
|
|
180
|
+
var original = '[tui]\ntheme = "dark"\n\n[tui.keymap.editor]\ninsert_newline = "enter"\n';
|
|
181
|
+
var updated = privateApi.ensureEditorInsertNewlineKeymap(original);
|
|
182
|
+
var ok = true;
|
|
183
|
+
ok = ok && updated.indexOf('[tui]') !== -1;
|
|
184
|
+
ok = ok && updated.indexOf('theme = "dark"') !== -1;
|
|
185
|
+
ok = ok && updated.indexOf('[tui.keymap.editor]') !== -1;
|
|
186
|
+
ok = ok && updated.indexOf('insert_newline = "alt-enter"') !== -1;
|
|
187
|
+
ok = ok && updated.indexOf('insert_newline = "enter"') === -1;
|
|
188
|
+
console.log('✅ Test 2.2 通过:', ok);
|
|
189
|
+
if (!ok) { process.exit(1); }
|
|
190
|
+
})();
|
|
191
|
+
|
|
160
192
|
// ============================================================
|
|
161
193
|
// 测试 3:空 base_url 删除顶层 model_provider,保留 provider 段
|
|
162
194
|
// ============================================================
|
|
@@ -200,10 +232,13 @@ async function runTests() {
|
|
|
200
232
|
ok = ok && auth.__jid === 'forced_channel';
|
|
201
233
|
ok = ok && auth.OPENAI_API_KEY === 'sk-forced-codex-key';
|
|
202
234
|
ok = ok && toml.indexOf('model_provider = "jcodex"') !== -1;
|
|
235
|
+
ok = ok && toml.indexOf('sandbox_mode = "danger-full-access"') !== -1;
|
|
203
236
|
ok = ok && toml.indexOf('base_url = "https://forced.example.com/v1"') !== -1;
|
|
204
237
|
ok = ok && toml.indexOf('wire_api = "wire-forced-provider-key"') !== -1;
|
|
238
|
+
ok = ok && toml.indexOf('[tui.keymap.editor]') !== -1;
|
|
239
|
+
ok = ok && toml.indexOf('insert_newline = "alt-enter"') !== -1;
|
|
205
240
|
ok = ok && agents.indexOf('cli=cx') !== -1;
|
|
206
|
-
ok = ok && agents.indexOf('
|
|
241
|
+
ok = ok && agents.indexOf('cli=cx-' + utils.getAiTipOsParam()) !== -1;
|
|
207
242
|
console.log('✅ Test 4 通过:', ok);
|
|
208
243
|
if (!ok) { process.exit(1); }
|
|
209
244
|
});
|