@tokenaut/opentoken 1.3.5 → 1.4.0
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 +85 -71
- package/bin/opentoken.js +5 -4
- package/package.json +22 -10
- package/src/atomcode-config.js +94 -0
- package/src/cli.js +207 -147
- package/src/codex-config.js +44 -22
- package/src/config.js +29 -28
- package/src/fetch-models.js +228 -0
- package/src/hermes-config.js +157 -0
- package/src/logo.js +62 -0
- package/src/models.js +61 -0
- package/src/openclaw-config.js +174 -0
- package/src/opencode-config.js +166 -0
- package/src/screen.js +9 -0
- package/src/tools/atomcode.js +253 -0
- package/src/tools/claude.js +302 -0
- package/src/tools/codex.js +327 -0
- package/src/tools/hermes.js +274 -0
- package/src/tools/openclaw.js +244 -0
- package/src/tools/opencode.js +258 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { DEFAULT_BASE_URL, HERMES_OPENTOKEN_BASE_URL } = require('../models');
|
|
6
|
+
const { fetchModelsForBaseUrl } = require('../fetch-models');
|
|
7
|
+
const {
|
|
8
|
+
CONFIG_YAML_PATH,
|
|
9
|
+
ENV_PATH,
|
|
10
|
+
getCurrentHermesConfig,
|
|
11
|
+
applyHermesConfig,
|
|
12
|
+
} = require('../hermes-config');
|
|
13
|
+
const { printLogo } = require('../logo');
|
|
14
|
+
const { clearScreen } = require('../screen');
|
|
15
|
+
|
|
16
|
+
let pendingConfig = { apiKey: null, model: null };
|
|
17
|
+
|
|
18
|
+
function resetPending() {
|
|
19
|
+
pendingConfig = { apiKey: null, model: null };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function maskApiKey(key) {
|
|
23
|
+
if (!key || key.length < 8) return key || '';
|
|
24
|
+
return key.slice(0, 6) + '****' + key.slice(-4);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getEffectiveHermesEnv() {
|
|
28
|
+
const disk = getCurrentHermesConfig();
|
|
29
|
+
return {
|
|
30
|
+
apiKey: pendingConfig.apiKey !== null ? pendingConfig.apiKey : disk.apiKey,
|
|
31
|
+
model: pendingConfig.model !== null ? pendingConfig.model : disk.model,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function grayParen(text) {
|
|
36
|
+
return chalk.gray('(' + text + ')');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildHermesMenuChoices() {
|
|
40
|
+
const ev = getEffectiveHermesEnv();
|
|
41
|
+
const keyShow = ev.apiKey ? maskApiKey(ev.apiKey) : '未配置';
|
|
42
|
+
const modelShow = ev.model ? ev.model : '未配置';
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
new inquirer.Separator(
|
|
46
|
+
chalk.gray(
|
|
47
|
+
' Opentoken(custom provider): ' +
|
|
48
|
+
HERMES_OPENTOKEN_BASE_URL +
|
|
49
|
+
' · Opentoken API KEY → ' +
|
|
50
|
+
ENV_PATH,
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
{ name: '1. 配置 API Key' + grayParen(keyShow), value: 'apiKey' },
|
|
54
|
+
{ name: '2. 配置模型' + grayParen(modelShow), value: 'model' },
|
|
55
|
+
new inquirer.Separator(),
|
|
56
|
+
{ name: '3. 应用配置并保存', value: 'apply' },
|
|
57
|
+
new inquirer.Separator(),
|
|
58
|
+
{ name: '← 返回上级菜单', value: 'back' },
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function handleConfigApiKey() {
|
|
63
|
+
const { apiKey } = await inquirer.prompt([
|
|
64
|
+
{
|
|
65
|
+
type: 'password',
|
|
66
|
+
name: 'apiKey',
|
|
67
|
+
prefix: '',
|
|
68
|
+
message: '请输入 Opentoken API KEY(写入 ~/.hermes/.env):',
|
|
69
|
+
mask: '*',
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
if (apiKey && apiKey.trim()) {
|
|
74
|
+
pendingConfig.apiKey = apiKey.trim();
|
|
75
|
+
console.log(chalk.green(' ✓ Opentoken API KEY 已更新(待应用后写入 .env)'));
|
|
76
|
+
} else {
|
|
77
|
+
console.log(chalk.gray(' (留空,保持不变)'));
|
|
78
|
+
}
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function handleConfigModel() {
|
|
83
|
+
const { model: existing, apiKey } = getEffectiveHermesEnv();
|
|
84
|
+
const current = existing || '';
|
|
85
|
+
const listBase = DEFAULT_BASE_URL;
|
|
86
|
+
|
|
87
|
+
if (!apiKey) {
|
|
88
|
+
console.log(chalk.yellow(' 请先配置 API Key,否则无法拉取模型列表。'));
|
|
89
|
+
const { modelId } = await inquirer.prompt([
|
|
90
|
+
{
|
|
91
|
+
type: 'input',
|
|
92
|
+
name: 'modelId',
|
|
93
|
+
prefix: '',
|
|
94
|
+
message: '或直接输入模型 ID:',
|
|
95
|
+
default: current,
|
|
96
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
pendingConfig.model = modelId.trim();
|
|
100
|
+
applyHermesConfig({ model: pendingConfig.model });
|
|
101
|
+
console.log(chalk.green(' ✓ 模型已更新:' + pendingConfig.model));
|
|
102
|
+
console.log(
|
|
103
|
+
chalk.gray(
|
|
104
|
+
' (已写入 ' +
|
|
105
|
+
CONFIG_YAML_PATH +
|
|
106
|
+
',provider=custom,base_url=' +
|
|
107
|
+
HERMES_OPENTOKEN_BASE_URL +
|
|
108
|
+
')',
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
console.log('');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const fetched = await fetchModelsForBaseUrl(listBase, apiKey);
|
|
116
|
+
|
|
117
|
+
if (fetched.ok && fetched.models.length > 0) {
|
|
118
|
+
let ids = [...fetched.models];
|
|
119
|
+
if (current && !ids.includes(current)) {
|
|
120
|
+
ids = [current, ...ids];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const modelChoices = ids.map((id) => ({
|
|
124
|
+
name: current === id ? chalk.green('► ' + id) : ' ' + id,
|
|
125
|
+
value: id,
|
|
126
|
+
}));
|
|
127
|
+
modelChoices.push(new inquirer.Separator());
|
|
128
|
+
modelChoices.push({ name: ' 手动输入其他模型 ID…', value: '__manual__' });
|
|
129
|
+
|
|
130
|
+
const defaultValue = current && ids.includes(current) ? current : ids[0];
|
|
131
|
+
|
|
132
|
+
const { model } = await inquirer.prompt([
|
|
133
|
+
{
|
|
134
|
+
type: 'list',
|
|
135
|
+
name: 'model',
|
|
136
|
+
prefix: '',
|
|
137
|
+
message: '请选择模型:',
|
|
138
|
+
choices: modelChoices,
|
|
139
|
+
default: defaultValue,
|
|
140
|
+
pageSize: 15,
|
|
141
|
+
},
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
if (model === '__manual__') {
|
|
145
|
+
const { modelId } = await inquirer.prompt([
|
|
146
|
+
{
|
|
147
|
+
type: 'input',
|
|
148
|
+
name: 'modelId',
|
|
149
|
+
prefix: '',
|
|
150
|
+
message: '请输入模型 ID:',
|
|
151
|
+
default: current || '',
|
|
152
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
pendingConfig.model = modelId.trim();
|
|
156
|
+
} else {
|
|
157
|
+
pendingConfig.model = model;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
console.log(chalk.yellow(' 无法拉取模型列表:' + (fetched.error || '未知错误')));
|
|
161
|
+
console.log(chalk.gray(' 请检查网络或 API Key 后重试,或直接输入模型 ID。'));
|
|
162
|
+
const { modelId } = await inquirer.prompt([
|
|
163
|
+
{
|
|
164
|
+
type: 'input',
|
|
165
|
+
name: 'modelId',
|
|
166
|
+
prefix: '',
|
|
167
|
+
message: '请输入模型 ID:',
|
|
168
|
+
default: current,
|
|
169
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
170
|
+
},
|
|
171
|
+
]);
|
|
172
|
+
pendingConfig.model = modelId.trim();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
applyHermesConfig({ model: pendingConfig.model });
|
|
176
|
+
console.log(chalk.green(' ✓ 模型已更新:' + pendingConfig.model));
|
|
177
|
+
console.log(
|
|
178
|
+
chalk.gray(
|
|
179
|
+
' (已写入 ' +
|
|
180
|
+
CONFIG_YAML_PATH +
|
|
181
|
+
',provider=custom,base_url=' +
|
|
182
|
+
HERMES_OPENTOKEN_BASE_URL +
|
|
183
|
+
')',
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function doApplyConfig() {
|
|
190
|
+
const toWrite = {};
|
|
191
|
+
if (pendingConfig.apiKey !== null) toWrite.apiKey = pendingConfig.apiKey;
|
|
192
|
+
if (pendingConfig.model !== null) toWrite.model = pendingConfig.model;
|
|
193
|
+
|
|
194
|
+
console.log('');
|
|
195
|
+
if (Object.keys(toWrite).length > 0) {
|
|
196
|
+
applyHermesConfig(toWrite);
|
|
197
|
+
console.log(chalk.green(' ✓ 配置已成功写入'));
|
|
198
|
+
console.log(chalk.gray(' ' + CONFIG_YAML_PATH));
|
|
199
|
+
console.log(chalk.gray(' ' + ENV_PATH));
|
|
200
|
+
} else {
|
|
201
|
+
applyHermesConfig({});
|
|
202
|
+
console.log(
|
|
203
|
+
chalk.gray(
|
|
204
|
+
' (无 Key / 模型变更;已确保 provider=custom 与 Opentoken base_url)',
|
|
205
|
+
),
|
|
206
|
+
);
|
|
207
|
+
console.log(chalk.gray(' ' + CONFIG_YAML_PATH));
|
|
208
|
+
console.log(chalk.gray(' ' + ENV_PATH));
|
|
209
|
+
}
|
|
210
|
+
console.log('');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function paintScreen() {
|
|
214
|
+
clearScreen();
|
|
215
|
+
printLogo();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function run() {
|
|
219
|
+
resetPending();
|
|
220
|
+
|
|
221
|
+
while (true) {
|
|
222
|
+
paintScreen();
|
|
223
|
+
|
|
224
|
+
const { action } = await inquirer.prompt([
|
|
225
|
+
{
|
|
226
|
+
type: 'list',
|
|
227
|
+
name: 'action',
|
|
228
|
+
prefix: '',
|
|
229
|
+
message: 'Hermes 配置(~/.hermes/config.yaml · ~/.hermes/.env):',
|
|
230
|
+
choices: buildHermesMenuChoices(),
|
|
231
|
+
pageSize: 14,
|
|
232
|
+
},
|
|
233
|
+
]);
|
|
234
|
+
|
|
235
|
+
switch (action) {
|
|
236
|
+
case 'apiKey':
|
|
237
|
+
paintScreen();
|
|
238
|
+
await handleConfigApiKey();
|
|
239
|
+
break;
|
|
240
|
+
case 'model':
|
|
241
|
+
paintScreen();
|
|
242
|
+
await handleConfigModel();
|
|
243
|
+
break;
|
|
244
|
+
case 'apply':
|
|
245
|
+
doApplyConfig();
|
|
246
|
+
return;
|
|
247
|
+
case 'back':
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function quickSet({ key, model }) {
|
|
254
|
+
const toWrite = {};
|
|
255
|
+
if (key) toWrite.apiKey = key;
|
|
256
|
+
if (model) toWrite.model = model;
|
|
257
|
+
|
|
258
|
+
if (Object.keys(toWrite).length === 0) return false;
|
|
259
|
+
|
|
260
|
+
applyHermesConfig(toWrite);
|
|
261
|
+
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(chalk.bold(chalk.cyan('Hermes 配置已更新:')));
|
|
264
|
+
if (key) console.log(chalk.green(' ✓ Opentoken API KEY(.env): ') + maskApiKey(key));
|
|
265
|
+
if (model) console.log(chalk.green(' ✓ model.default : ') + model);
|
|
266
|
+
console.log(chalk.green(' ✓ provider / base_url : ') + 'custom · ' + HERMES_OPENTOKEN_BASE_URL);
|
|
267
|
+
console.log(chalk.gray(' ' + CONFIG_YAML_PATH));
|
|
268
|
+
console.log(chalk.gray(' ' + ENV_PATH));
|
|
269
|
+
console.log('');
|
|
270
|
+
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = { run, quickSet };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { DEFAULT_BASE_URL, OPENCLAW_PROVIDER_KEY } = require('../models');
|
|
6
|
+
const { fetchModelsForBaseUrl } = require('../fetch-models');
|
|
7
|
+
const {
|
|
8
|
+
OPENCLAW_JSON_PATH,
|
|
9
|
+
getCurrentOpenclawOpentokenConfig,
|
|
10
|
+
applyOpenclawConfig,
|
|
11
|
+
} = require('../openclaw-config');
|
|
12
|
+
const { printLogo } = require('../logo');
|
|
13
|
+
const { clearScreen } = require('../screen');
|
|
14
|
+
|
|
15
|
+
let pendingConfig = { apiKey: null, model: null };
|
|
16
|
+
|
|
17
|
+
function resetPending() {
|
|
18
|
+
pendingConfig = { apiKey: null, model: null };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function maskApiKey(key) {
|
|
22
|
+
if (!key || key.length < 8) return key || '';
|
|
23
|
+
return key.slice(0, 6) + '****' + key.slice(-4);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getEffectiveOpenclawEnv() {
|
|
27
|
+
const disk = getCurrentOpenclawOpentokenConfig();
|
|
28
|
+
return {
|
|
29
|
+
apiKey: pendingConfig.apiKey !== null ? pendingConfig.apiKey : disk.apiKey,
|
|
30
|
+
model: pendingConfig.model !== null ? pendingConfig.model : disk.model,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function grayParen(text) {
|
|
35
|
+
return chalk.gray('(' + text + ')');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildOpenclawMenuChoices() {
|
|
39
|
+
const ev = getEffectiveOpenclawEnv();
|
|
40
|
+
const keyShow = ev.apiKey ? maskApiKey(ev.apiKey) : '未配置';
|
|
41
|
+
const modelShow = ev.model ? OPENCLAW_PROVIDER_KEY + '/' + ev.model : '未配置';
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
new inquirer.Separator(chalk.gray(' ' + OPENCLAW_JSON_PATH)),
|
|
45
|
+
{ name: '1. 配置 API Key' + grayParen(keyShow), value: 'apiKey' },
|
|
46
|
+
{ name: '2. 配置模型' + grayParen(modelShow), value: 'model' },
|
|
47
|
+
new inquirer.Separator(),
|
|
48
|
+
{ name: '3. 应用配置并保存', value: 'apply' },
|
|
49
|
+
new inquirer.Separator(),
|
|
50
|
+
{ name: '← 返回上级菜单', value: 'back' },
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function handleConfigApiKey() {
|
|
55
|
+
const { apiKey } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: 'password',
|
|
58
|
+
name: 'apiKey',
|
|
59
|
+
prefix: '',
|
|
60
|
+
message: '请输入 Opentoken API KEY:',
|
|
61
|
+
mask: '*',
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
if (apiKey && apiKey.trim()) {
|
|
66
|
+
pendingConfig.apiKey = apiKey.trim();
|
|
67
|
+
console.log(chalk.green(' ✓ Opentoken API KEY 已更新(待应用后写入)'));
|
|
68
|
+
} else {
|
|
69
|
+
console.log(chalk.gray(' (留空,保持不变)'));
|
|
70
|
+
}
|
|
71
|
+
console.log('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function handleConfigModel() {
|
|
75
|
+
const { model: existing, apiKey } = getEffectiveOpenclawEnv();
|
|
76
|
+
const current = existing || '';
|
|
77
|
+
const listBase = DEFAULT_BASE_URL;
|
|
78
|
+
|
|
79
|
+
if (!apiKey) {
|
|
80
|
+
console.log(chalk.yellow(' 请先配置 API Key,否则无法拉取模型列表。'));
|
|
81
|
+
const { modelId } = await inquirer.prompt([
|
|
82
|
+
{
|
|
83
|
+
type: 'input',
|
|
84
|
+
name: 'modelId',
|
|
85
|
+
prefix: '',
|
|
86
|
+
message: '或直接输入模型 ID:',
|
|
87
|
+
default: current,
|
|
88
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
pendingConfig.model = modelId.trim();
|
|
92
|
+
applyOpenclawConfig({ model: pendingConfig.model });
|
|
93
|
+
console.log(chalk.green(' ✓ 模型已更新:' + OPENCLAW_PROVIDER_KEY + '/' + pendingConfig.model));
|
|
94
|
+
console.log(chalk.gray(' (已写入 ' + OPENCLAW_JSON_PATH + ')'));
|
|
95
|
+
console.log('');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const fetched = await fetchModelsForBaseUrl(listBase, apiKey);
|
|
100
|
+
|
|
101
|
+
if (fetched.ok && fetched.models.length > 0) {
|
|
102
|
+
let ids = [...fetched.models];
|
|
103
|
+
if (current && !ids.includes(current)) {
|
|
104
|
+
ids = [current, ...ids];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const modelChoices = ids.map((id) => ({
|
|
108
|
+
name: current === id ? chalk.green('► ' + id) : ' ' + id,
|
|
109
|
+
value: id,
|
|
110
|
+
}));
|
|
111
|
+
modelChoices.push(new inquirer.Separator());
|
|
112
|
+
modelChoices.push({ name: ' 手动输入其他模型 ID…', value: '__manual__' });
|
|
113
|
+
|
|
114
|
+
const defaultValue = current && ids.includes(current) ? current : ids[0];
|
|
115
|
+
|
|
116
|
+
const { model } = await inquirer.prompt([
|
|
117
|
+
{
|
|
118
|
+
type: 'list',
|
|
119
|
+
name: 'model',
|
|
120
|
+
prefix: '',
|
|
121
|
+
message: '请选择模型:',
|
|
122
|
+
choices: modelChoices,
|
|
123
|
+
default: defaultValue,
|
|
124
|
+
pageSize: 15,
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
if (model === '__manual__') {
|
|
129
|
+
const { modelId } = await inquirer.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: 'input',
|
|
132
|
+
name: 'modelId',
|
|
133
|
+
prefix: '',
|
|
134
|
+
message: '请输入模型 ID:',
|
|
135
|
+
default: current || '',
|
|
136
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
137
|
+
},
|
|
138
|
+
]);
|
|
139
|
+
pendingConfig.model = modelId.trim();
|
|
140
|
+
} else {
|
|
141
|
+
pendingConfig.model = model;
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
console.log(chalk.yellow(' 无法拉取模型列表:' + (fetched.error || '未知错误')));
|
|
145
|
+
console.log(chalk.gray(' 请检查网络或 API Key 后重试,或直接输入模型 ID。'));
|
|
146
|
+
const { modelId } = await inquirer.prompt([
|
|
147
|
+
{
|
|
148
|
+
type: 'input',
|
|
149
|
+
name: 'modelId',
|
|
150
|
+
prefix: '',
|
|
151
|
+
message: '请输入模型 ID:',
|
|
152
|
+
default: current,
|
|
153
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
154
|
+
},
|
|
155
|
+
]);
|
|
156
|
+
pendingConfig.model = modelId.trim();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
applyOpenclawConfig({ model: pendingConfig.model });
|
|
160
|
+
console.log(chalk.green(' ✓ 模型已更新:' + OPENCLAW_PROVIDER_KEY + '/' + pendingConfig.model));
|
|
161
|
+
console.log(chalk.gray(' (已写入 ' + OPENCLAW_JSON_PATH + ')'));
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function doApplyConfig() {
|
|
166
|
+
const toWrite = {};
|
|
167
|
+
if (pendingConfig.apiKey !== null) toWrite.apiKey = pendingConfig.apiKey;
|
|
168
|
+
if (pendingConfig.model !== null) toWrite.model = pendingConfig.model;
|
|
169
|
+
|
|
170
|
+
console.log('');
|
|
171
|
+
if (Object.keys(toWrite).length > 0) {
|
|
172
|
+
applyOpenclawConfig(toWrite);
|
|
173
|
+
console.log(chalk.green(' ✓ 配置已成功写入'));
|
|
174
|
+
console.log(chalk.gray(' ' + OPENCLAW_JSON_PATH));
|
|
175
|
+
} else {
|
|
176
|
+
applyOpenclawConfig({});
|
|
177
|
+
console.log(chalk.gray(' (无 Key / 模型变更)'));
|
|
178
|
+
console.log(chalk.gray(' ' + OPENCLAW_JSON_PATH));
|
|
179
|
+
}
|
|
180
|
+
console.log('');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function paintScreen() {
|
|
184
|
+
clearScreen();
|
|
185
|
+
printLogo();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function run() {
|
|
189
|
+
resetPending();
|
|
190
|
+
|
|
191
|
+
while (true) {
|
|
192
|
+
paintScreen();
|
|
193
|
+
|
|
194
|
+
const { action } = await inquirer.prompt([
|
|
195
|
+
{
|
|
196
|
+
type: 'list',
|
|
197
|
+
name: 'action',
|
|
198
|
+
prefix: '',
|
|
199
|
+
message: 'OpenClaw 配置(' + OPENCLAW_JSON_PATH + '):',
|
|
200
|
+
choices: buildOpenclawMenuChoices(),
|
|
201
|
+
pageSize: 14,
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
switch (action) {
|
|
206
|
+
case 'apiKey':
|
|
207
|
+
paintScreen();
|
|
208
|
+
await handleConfigApiKey();
|
|
209
|
+
break;
|
|
210
|
+
case 'model':
|
|
211
|
+
paintScreen();
|
|
212
|
+
await handleConfigModel();
|
|
213
|
+
break;
|
|
214
|
+
case 'apply':
|
|
215
|
+
doApplyConfig();
|
|
216
|
+
return;
|
|
217
|
+
case 'back':
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function quickSet({ key, model }) {
|
|
224
|
+
const toWrite = {};
|
|
225
|
+
if (key) toWrite.apiKey = key;
|
|
226
|
+
if (model) toWrite.model = model;
|
|
227
|
+
|
|
228
|
+
if (Object.keys(toWrite).length === 0) return false;
|
|
229
|
+
|
|
230
|
+
applyOpenclawConfig(toWrite);
|
|
231
|
+
|
|
232
|
+
console.log('');
|
|
233
|
+
console.log(chalk.bold(chalk.cyan('OpenClaw 配置已更新')));
|
|
234
|
+
if (key) console.log(chalk.green(' ✓ API Key: ') + maskApiKey(key));
|
|
235
|
+
if (model) {
|
|
236
|
+
console.log(chalk.green(' ✓ 模型: ') + OPENCLAW_PROVIDER_KEY + '/' + model);
|
|
237
|
+
}
|
|
238
|
+
console.log(chalk.gray(' ' + OPENCLAW_JSON_PATH));
|
|
239
|
+
console.log('');
|
|
240
|
+
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
module.exports = { run, quickSet };
|