@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,258 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const { DEFAULT_BASE_URL, OPENCODE_PROVIDER_KEY } = require('../models');
|
|
6
|
+
const { fetchModelsForBaseUrl } = require('../fetch-models');
|
|
7
|
+
const {
|
|
8
|
+
OPENCODE_JSON_PATH,
|
|
9
|
+
getCurrentOpencodeOpentokenConfig,
|
|
10
|
+
applyOpencodeConfig,
|
|
11
|
+
} = require('../opencode-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 getEffectiveOpencodeEnv() {
|
|
27
|
+
const disk = getCurrentOpencodeOpentokenConfig();
|
|
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 buildOpencodeMenuChoices() {
|
|
39
|
+
const ev = getEffectiveOpencodeEnv();
|
|
40
|
+
const keyShow = ev.apiKey ? maskApiKey(ev.apiKey) : '未配置';
|
|
41
|
+
const modelShow = ev.model ? ev.model : '未配置';
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
new inquirer.Separator(chalk.gray(' ' + OPENCODE_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 } = getEffectiveOpencodeEnv();
|
|
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
|
+
applyOpencodeConfig({ model: pendingConfig.model });
|
|
93
|
+
console.log(chalk.green(' ✓ 模型已更新:' + pendingConfig.model));
|
|
94
|
+
console.log(chalk.gray(' (已写入 ' + OPENCODE_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
|
+
|
|
144
|
+
applyOpencodeConfig({ model: pendingConfig.model, models: ids });
|
|
145
|
+
console.log(chalk.green(' ✓ 模型已更新:' + pendingConfig.model));
|
|
146
|
+
console.log(chalk.gray(' (已写入 ' + OPENCODE_JSON_PATH + ',包含 ' + ids.length + ' 个可用模型)'));
|
|
147
|
+
console.log('');
|
|
148
|
+
} else {
|
|
149
|
+
console.log(chalk.yellow(' 无法拉取模型列表:' + (fetched.error || '未知错误')));
|
|
150
|
+
console.log(chalk.gray(' 请检查网络或 API Key 后重试,或直接输入模型 ID。'));
|
|
151
|
+
const { modelId } = await inquirer.prompt([
|
|
152
|
+
{
|
|
153
|
+
type: 'input',
|
|
154
|
+
name: 'modelId',
|
|
155
|
+
prefix: '',
|
|
156
|
+
message: '请输入模型 ID:',
|
|
157
|
+
default: current,
|
|
158
|
+
validate: (v) => (v && v.trim() ? true : '请输入模型 ID'),
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
pendingConfig.model = modelId.trim();
|
|
162
|
+
applyOpencodeConfig({ model: pendingConfig.model });
|
|
163
|
+
console.log(chalk.green(' ✓ 模型已更新:' + pendingConfig.model));
|
|
164
|
+
console.log(chalk.gray(' (已写入 ' + OPENCODE_JSON_PATH + ')'));
|
|
165
|
+
console.log('');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function doApplyConfig() {
|
|
170
|
+
const toWrite = {};
|
|
171
|
+
if (pendingConfig.apiKey !== null) toWrite.apiKey = pendingConfig.apiKey;
|
|
172
|
+
if (pendingConfig.model !== null) toWrite.model = pendingConfig.model;
|
|
173
|
+
|
|
174
|
+
console.log('');
|
|
175
|
+
if (Object.keys(toWrite).length > 0) {
|
|
176
|
+
applyOpencodeConfig(toWrite);
|
|
177
|
+
console.log(chalk.green(' ✓ 配置已成功写入'));
|
|
178
|
+
console.log(chalk.gray(' ' + OPENCODE_JSON_PATH));
|
|
179
|
+
} else {
|
|
180
|
+
applyOpencodeConfig({});
|
|
181
|
+
console.log(chalk.gray(' (无 Key / 模型变更)'));
|
|
182
|
+
console.log(chalk.gray(' ' + OPENCODE_JSON_PATH));
|
|
183
|
+
}
|
|
184
|
+
console.log('');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function paintScreen() {
|
|
188
|
+
clearScreen();
|
|
189
|
+
printLogo();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function run() {
|
|
193
|
+
resetPending();
|
|
194
|
+
|
|
195
|
+
while (true) {
|
|
196
|
+
paintScreen();
|
|
197
|
+
|
|
198
|
+
const { action } = await inquirer.prompt([
|
|
199
|
+
{
|
|
200
|
+
type: 'list',
|
|
201
|
+
name: 'action',
|
|
202
|
+
prefix: '',
|
|
203
|
+
message: 'OpenCode 配置(' + OPENCODE_JSON_PATH + '):',
|
|
204
|
+
choices: buildOpencodeMenuChoices(),
|
|
205
|
+
pageSize: 14,
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
switch (action) {
|
|
210
|
+
case 'apiKey':
|
|
211
|
+
paintScreen();
|
|
212
|
+
await handleConfigApiKey();
|
|
213
|
+
break;
|
|
214
|
+
case 'model':
|
|
215
|
+
paintScreen();
|
|
216
|
+
await handleConfigModel();
|
|
217
|
+
break;
|
|
218
|
+
case 'apply':
|
|
219
|
+
doApplyConfig();
|
|
220
|
+
return;
|
|
221
|
+
case 'back':
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function quickSet({ key, model }) {
|
|
228
|
+
const toWrite = {};
|
|
229
|
+
if (key) toWrite.apiKey = key;
|
|
230
|
+
if (model) toWrite.model = model;
|
|
231
|
+
|
|
232
|
+
if (Object.keys(toWrite).length === 0) return false;
|
|
233
|
+
|
|
234
|
+
if (key) {
|
|
235
|
+
const fetched = await fetchModelsForBaseUrl(DEFAULT_BASE_URL, key);
|
|
236
|
+
if (fetched.ok && fetched.models.length > 0) {
|
|
237
|
+
toWrite.models = fetched.models;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
applyOpencodeConfig(toWrite);
|
|
242
|
+
|
|
243
|
+
console.log('');
|
|
244
|
+
console.log(chalk.bold(chalk.cyan('OpenCode 配置已更新')));
|
|
245
|
+
if (key) console.log(chalk.green(' ✓ API Key: ') + maskApiKey(key));
|
|
246
|
+
if (model) {
|
|
247
|
+
console.log(chalk.green(' ✓ 模型: ') + model);
|
|
248
|
+
}
|
|
249
|
+
if (toWrite.models) {
|
|
250
|
+
console.log(chalk.gray(' (包含 ' + toWrite.models.length + ' 个可用模型)'));
|
|
251
|
+
}
|
|
252
|
+
console.log(chalk.gray(' ' + OPENCODE_JSON_PATH));
|
|
253
|
+
console.log('');
|
|
254
|
+
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = { run, quickSet };
|