1314mc-helper 0.2.21
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 +160 -0
- package/bin/cli.js +328 -0
- package/package.json +56 -0
- package/src/index.js +359 -0
- package/src/tools/claude.js +200 -0
- package/src/tools/cline.js +103 -0
- package/src/tools/codex.js +154 -0
- package/src/tools/continue.js +149 -0
- package/src/tools/cursor.js +99 -0
- package/src/tools/droid.js +253 -0
- package/src/tools/openclaw.js +295 -0
- package/src/tools/opencode.js +227 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { getModels, getApiBaseForProtocol } from '../index.js';
|
|
5
|
+
|
|
6
|
+
const MC_PROVIDER_PREFIX = 'mc1314-';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 获取 OpenClaw 配置路径
|
|
10
|
+
* 支持 macOS / Linux / Windows(os.homedir() 跨平台处理)
|
|
11
|
+
*/
|
|
12
|
+
function getOpenClawConfigPaths() {
|
|
13
|
+
const home = os.homedir();
|
|
14
|
+
return {
|
|
15
|
+
configDir: path.join(home, '.openclaw'),
|
|
16
|
+
configFile: path.join(home, '.openclaw', 'openclaw.json'),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 读取并解析 openclaw.json(支持 JSON5 注释/尾逗号)
|
|
22
|
+
*/
|
|
23
|
+
async function readConfig(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
26
|
+
const clean = content
|
|
27
|
+
.replace(/\/\/.*$/gm, '')
|
|
28
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
29
|
+
.replace(/,(\s*[}\]])/g, '$1');
|
|
30
|
+
return JSON.parse(clean);
|
|
31
|
+
} catch {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 将模型对象转换为 OpenClaw models.providers 格式
|
|
38
|
+
*/
|
|
39
|
+
function buildModelEntry(m, contextWindow, maxTokens) {
|
|
40
|
+
return {
|
|
41
|
+
id: m.id,
|
|
42
|
+
name: m.name || m.id,
|
|
43
|
+
reasoning: false,
|
|
44
|
+
input: ['text', 'image'],
|
|
45
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
46
|
+
contextWindow,
|
|
47
|
+
maxTokens,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function canonicalizeApi(api) {
|
|
52
|
+
const normalized = `${api || ''}`.toLowerCase().trim();
|
|
53
|
+
if (!normalized) return 'openai-completions';
|
|
54
|
+
if (normalized === 'openai-response') return 'openai-responses';
|
|
55
|
+
if (normalized === 'openai-completion') return 'openai-completions';
|
|
56
|
+
if (normalized === 'openai') return 'openai-completions';
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function providerNameForApi(api) {
|
|
61
|
+
const normalized = canonicalizeApi(api);
|
|
62
|
+
if (normalized === 'anthropic-messages') return 'mc1314-anthropic';
|
|
63
|
+
if (normalized === 'openai-completions') return 'mc1314-openai';
|
|
64
|
+
if (normalized === 'openai-responses') return 'mc1314-openai-responses';
|
|
65
|
+
return `mc1314-${normalized.replace(/[^a-z0-9]+/g, '-')}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function stripMcKeysFromObject(obj) {
|
|
69
|
+
if (!obj || typeof obj !== 'object') return {};
|
|
70
|
+
const next = { ...obj };
|
|
71
|
+
for (const key of Object.keys(next)) {
|
|
72
|
+
if (`${key}`.startsWith(MC_PROVIDER_PREFIX) || `${key}`.startsWith('duojie-')) {
|
|
73
|
+
delete next[key];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return next;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 配置 OpenClaw
|
|
81
|
+
*
|
|
82
|
+
* 使用官方 models.providers 自定义 provider 格式:
|
|
83
|
+
* models.providers.<name>.baseUrl / apiKey / api / models[]
|
|
84
|
+
* agents.defaults.model.primary → "<provider>/<model-id>"
|
|
85
|
+
*
|
|
86
|
+
* 参考文档:https://docs.openclaw.ai/gateway/configuration-reference#custom-providers-and-base-urls
|
|
87
|
+
*/
|
|
88
|
+
export async function configureOpenClaw(apiKey) {
|
|
89
|
+
const paths = getOpenClawConfigPaths();
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await fs.ensureDir(paths.configDir);
|
|
93
|
+
|
|
94
|
+
const existingConfig = await fs.pathExists(paths.configFile)
|
|
95
|
+
? await readConfig(paths.configFile)
|
|
96
|
+
: {};
|
|
97
|
+
|
|
98
|
+
// OpenClaw 配置会做 schema 校验:根级未知字段可能导致校验失败
|
|
99
|
+
// 旧版本写入过 `_duojie`,这里显式剔除
|
|
100
|
+
const { _mc1314: _ignoredMc, _duojie: _ignoredDuojie, ...baseConfig } = existingConfig || {};
|
|
101
|
+
void _ignoredMc;
|
|
102
|
+
void _ignoredDuojie;
|
|
103
|
+
|
|
104
|
+
const models = getModels();
|
|
105
|
+
|
|
106
|
+
// 构建各类模型列表(apis 是协议数组,展开为多条记录)
|
|
107
|
+
function expandModels(modelList, contextWindow, maxTokens) {
|
|
108
|
+
const expanded = [];
|
|
109
|
+
for (const m of modelList) {
|
|
110
|
+
const apis = m.apis || ['anthropic-messages'];
|
|
111
|
+
for (const api of apis) {
|
|
112
|
+
expanded.push({
|
|
113
|
+
...m,
|
|
114
|
+
api: canonicalizeApi(api),
|
|
115
|
+
_entry: buildModelEntry(m, contextWindow, maxTokens),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return expanded;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const claudeModels = expandModels(models.claude || [], 200000, 64000);
|
|
123
|
+
const gptModels = expandModels(models.gpt || [], 128000, 16384);
|
|
124
|
+
const geminiModels = expandModels(models.gemini || [], 1000000, 65536);
|
|
125
|
+
const otherModels = expandModels(models.other || [], 128000, 16384);
|
|
126
|
+
|
|
127
|
+
const allModels = [...claudeModels, ...gptModels, ...geminiModels, ...otherModels];
|
|
128
|
+
|
|
129
|
+
// 构建 providers(按 api 协议分组,避免在同一个 provider 混用不同协议)
|
|
130
|
+
const providers = {};
|
|
131
|
+
const modelIdToProvider = new Map();
|
|
132
|
+
|
|
133
|
+
for (const m of allModels) {
|
|
134
|
+
const api = canonicalizeApi(m.api);
|
|
135
|
+
const providerName = providerNameForApi(api);
|
|
136
|
+
|
|
137
|
+
if (!providers[providerName]) {
|
|
138
|
+
providers[providerName] = {
|
|
139
|
+
baseUrl: getApiBaseForProtocol(api),
|
|
140
|
+
apiKey,
|
|
141
|
+
api,
|
|
142
|
+
models: [],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
providers[providerName].models.push(m._entry);
|
|
147
|
+
modelIdToProvider.set(m.id, providerName);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 默认模型:优先 gpt-5.4(如存在)
|
|
151
|
+
const gpt54 = gptModels.find(m => m.id === 'gpt-5.4')?.id || null;
|
|
152
|
+
let defaultProvider = 'mc1314-anthropic';
|
|
153
|
+
let defaultModelId = claudeModels[0]?.id || allModels[0]?.id || '';
|
|
154
|
+
|
|
155
|
+
if (gpt54) {
|
|
156
|
+
defaultProvider = modelIdToProvider.get(gpt54) || providerNameForApi('openai-completions');
|
|
157
|
+
defaultModelId = gpt54;
|
|
158
|
+
} else if (claudeModels.length > 0) {
|
|
159
|
+
defaultModelId = (
|
|
160
|
+
claudeModels.find(m => m.id.includes('opus-4-6')) ||
|
|
161
|
+
claudeModels.find(m => m.id.includes('opus')) ||
|
|
162
|
+
claudeModels.find(m => m.id.includes('sonnet')) ||
|
|
163
|
+
claudeModels[0]
|
|
164
|
+
).id;
|
|
165
|
+
defaultProvider = modelIdToProvider.get(defaultModelId) || 'mc1314-anthropic';
|
|
166
|
+
} else if (gptModels.length > 0) {
|
|
167
|
+
defaultModelId = gptModels[0].id;
|
|
168
|
+
defaultProvider = modelIdToProvider.get(defaultModelId) || providerNameForApi(gptModels[0].api);
|
|
169
|
+
} else if (geminiModels.length > 0) {
|
|
170
|
+
defaultModelId = geminiModels[0].id;
|
|
171
|
+
defaultProvider = modelIdToProvider.get(defaultModelId) || providerNameForApi(geminiModels[0].api);
|
|
172
|
+
} else if (otherModels.length > 0) {
|
|
173
|
+
defaultModelId = otherModels[0].id;
|
|
174
|
+
defaultProvider = modelIdToProvider.get(defaultModelId) || providerNameForApi(otherModels[0].api);
|
|
175
|
+
} else if (allModels.length > 0) {
|
|
176
|
+
defaultModelId = allModels[0].id;
|
|
177
|
+
defaultProvider = modelIdToProvider.get(defaultModelId) || providerNameForApi(allModels[0].api);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 构建模型 allowlist(用于 /model 切换)
|
|
181
|
+
const modelAllowList = {};
|
|
182
|
+
for (const m of allModels) {
|
|
183
|
+
const providerName = modelIdToProvider.get(m.id) || providerNameForApi(m.api);
|
|
184
|
+
modelAllowList[`${providerName}/${m.id}`] = { alias: m.name || m.id };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const totalModels = allModels.length;
|
|
188
|
+
|
|
189
|
+
// 合并配置,保留用户已有的非 Duojie 设置
|
|
190
|
+
const existingProviders = stripMcKeysFromObject(baseConfig.models?.providers);
|
|
191
|
+
const existingAllowList = stripMcKeysFromObject(baseConfig.agents?.defaults?.models);
|
|
192
|
+
|
|
193
|
+
const newConfig = {
|
|
194
|
+
...baseConfig,
|
|
195
|
+
models: {
|
|
196
|
+
...baseConfig.models,
|
|
197
|
+
mode: 'merge',
|
|
198
|
+
providers: {
|
|
199
|
+
...existingProviders,
|
|
200
|
+
...providers,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
agents: {
|
|
204
|
+
...baseConfig.agents,
|
|
205
|
+
defaults: {
|
|
206
|
+
...baseConfig.agents?.defaults,
|
|
207
|
+
model: {
|
|
208
|
+
...baseConfig.agents?.defaults?.model,
|
|
209
|
+
primary: `${defaultProvider}/${defaultModelId}`,
|
|
210
|
+
},
|
|
211
|
+
models: {
|
|
212
|
+
...existingAllowList,
|
|
213
|
+
...modelAllowList,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
success: true,
|
|
223
|
+
message: `已配置 ${totalModels} 个模型 → ${paths.configFile}`,
|
|
224
|
+
configPath: paths.configFile,
|
|
225
|
+
hint: '运行 openclaw gateway restart 使配置生效',
|
|
226
|
+
};
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return {
|
|
229
|
+
success: false,
|
|
230
|
+
message: `配置失败: ${error.message}`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 检查 OpenClaw 配置状态
|
|
237
|
+
*/
|
|
238
|
+
configureOpenClaw.checkStatus = async function () {
|
|
239
|
+
const paths = getOpenClawConfigPaths();
|
|
240
|
+
|
|
241
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
242
|
+
const config = await readConfig(paths.configFile);
|
|
243
|
+
const providerKeys = Object.keys(config.models?.providers || {});
|
|
244
|
+
if (providerKeys.some(k => `${k}`.startsWith(MC_PROVIDER_PREFIX) || `${k}`.startsWith('duojie-'))) {
|
|
245
|
+
return { configured: true, message: '已配置 1314mc API' };
|
|
246
|
+
}
|
|
247
|
+
if (providerKeys.length > 0) {
|
|
248
|
+
return { configured: true, message: '已配置(非 1314mc)' };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { configured: false };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 撤销 OpenClaw 配置
|
|
257
|
+
*/
|
|
258
|
+
configureOpenClaw.revoke = async function () {
|
|
259
|
+
const paths = getOpenClawConfigPaths();
|
|
260
|
+
|
|
261
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
262
|
+
const config = await readConfig(paths.configFile);
|
|
263
|
+
|
|
264
|
+
if (config.models?.providers) {
|
|
265
|
+
for (const key of Object.keys(config.models.providers)) {
|
|
266
|
+
if (`${key}`.startsWith(MC_PROVIDER_PREFIX) || `${key}`.startsWith('duojie-')) {
|
|
267
|
+
delete config.models.providers[key];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (config.agents?.defaults?.models) {
|
|
273
|
+
for (const key of Object.keys(config.agents.defaults.models)) {
|
|
274
|
+
if (`${key}`.startsWith(MC_PROVIDER_PREFIX) || `${key}`.startsWith('duojie-')) {
|
|
275
|
+
delete config.agents.defaults.models[key];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (config.agents?.defaults?.model?.primary?.startsWith?.(MC_PROVIDER_PREFIX) || config.agents?.defaults?.model?.primary?.startsWith?.('duojie-')) {
|
|
281
|
+
delete config.agents.defaults.model.primary;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (config._mc1314) {
|
|
285
|
+
delete config._mc1314;
|
|
286
|
+
}
|
|
287
|
+
if (config._duojie) {
|
|
288
|
+
delete config._duojie;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
await fs.writeJson(paths.configFile, config, { spaces: 2 });
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export default configureOpenClaw;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { API_CONFIG, getModels, getApiBaseForProtocol } from '../index.js';
|
|
5
|
+
|
|
6
|
+
const OPENAI_PROTOCOLS = new Set([
|
|
7
|
+
'openai', 'openai-response', 'openai-responses',
|
|
8
|
+
'openai-completion', 'openai-completions', 'responses', 'chat',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function isOpenaiModelApi(api) {
|
|
12
|
+
return OPENAI_PROTOCOLS.has(`${m.api || ''}`.toLowerCase());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 获取 OpenCode 配置路径
|
|
17
|
+
*/
|
|
18
|
+
function getOpenCodeConfigPaths() {
|
|
19
|
+
const home = os.homedir();
|
|
20
|
+
|
|
21
|
+
// OpenCode 统一使用 ~/.config/opencode 目录
|
|
22
|
+
const configDir = path.join(home, '.config', 'opencode');
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
configDir,
|
|
26
|
+
configFile: path.join(configDir, 'opencode.json'),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 配置 OpenCode
|
|
32
|
+
* 自动配置所有可用模型
|
|
33
|
+
*/
|
|
34
|
+
export async function configureOpenCode(apiKey) {
|
|
35
|
+
const paths = getOpenCodeConfigPaths();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// 确保目录存在
|
|
39
|
+
await fs.ensureDir(paths.configDir);
|
|
40
|
+
|
|
41
|
+
// 1. 读取现有配置(如果存在)
|
|
42
|
+
let existingConfig = {};
|
|
43
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
44
|
+
try {
|
|
45
|
+
existingConfig = await fs.readJson(paths.configFile);
|
|
46
|
+
} catch {
|
|
47
|
+
existingConfig = {};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 2. 获取模型列表
|
|
52
|
+
const models = getModels();
|
|
53
|
+
|
|
54
|
+
// 3. 按协议分组构建 provider 的模型配置(双协议模型同时放入两个分组)
|
|
55
|
+
const anthropicModels = {};
|
|
56
|
+
const openaiModels = {};
|
|
57
|
+
|
|
58
|
+
const allCategories = [
|
|
59
|
+
...(models.claude || []),
|
|
60
|
+
...(models.gpt || []),
|
|
61
|
+
...(models.gemini || []),
|
|
62
|
+
...(models.other || []),
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
for (const m of allCategories) {
|
|
66
|
+
const modelConfig = {
|
|
67
|
+
limit: {
|
|
68
|
+
context: 200000,
|
|
69
|
+
output: 16384,
|
|
70
|
+
},
|
|
71
|
+
modalities: {
|
|
72
|
+
input: ['text', 'image'],
|
|
73
|
+
output: ['text'],
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const apis = m.apis || ['anthropic-messages'];
|
|
78
|
+
for (const api of apis) {
|
|
79
|
+
if (isOpenaiModelApi(api)) {
|
|
80
|
+
openaiModels[m.id] = modelConfig;
|
|
81
|
+
} else {
|
|
82
|
+
anthropicModels[m.id] = modelConfig;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const totalAnthropic = Object.keys(anthropicModels).length;
|
|
88
|
+
const totalOpenai = Object.keys(openaiModels).length;
|
|
89
|
+
const totalModels = totalAnthropic + totalOpenai;
|
|
90
|
+
|
|
91
|
+
if (totalModels === 0) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
message: '没有可用的模型',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 4. 构建 providers(按协议拆分为 anthropic 和 openai 两个)
|
|
99
|
+
const { duojie: _old, ...existingProviders } = existingConfig.provider || {};
|
|
100
|
+
const newProviders = { ...existingProviders };
|
|
101
|
+
|
|
102
|
+
if (totalAnthropic > 0) {
|
|
103
|
+
newProviders['mc-anthropic'] = {
|
|
104
|
+
npm: '@ai-sdk/anthropic',
|
|
105
|
+
name: '1314mc Anthropic',
|
|
106
|
+
options: {
|
|
107
|
+
baseURL: getApiBaseForProtocol('anthropic-messages'),
|
|
108
|
+
apiKey: apiKey,
|
|
109
|
+
},
|
|
110
|
+
models: anthropicModels,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (totalOpenai > 0) {
|
|
115
|
+
newProviders['mc-openai'] = {
|
|
116
|
+
npm: '@ai-sdk/openai',
|
|
117
|
+
name: '1314mc OpenAI',
|
|
118
|
+
options: {
|
|
119
|
+
baseURL: getApiBaseForProtocol('openai-completions'),
|
|
120
|
+
apiKey: apiKey,
|
|
121
|
+
},
|
|
122
|
+
models: openaiModels,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 优先选择 gpt-5.4 作为默认模型
|
|
127
|
+
let preferredModel = null;
|
|
128
|
+
if (openaiModels['gpt-5.4']) {
|
|
129
|
+
preferredModel = 'mc-openai/gpt-5.4';
|
|
130
|
+
} else if (anthropicModels['claude-opus-4-6']) {
|
|
131
|
+
preferredModel = 'mc-anthropic/claude-opus-4-6';
|
|
132
|
+
} else if (totalOpenai > 0) {
|
|
133
|
+
preferredModel = `mc-openai/${Object.keys(openaiModels)[0]}`;
|
|
134
|
+
} else if (totalAnthropic > 0) {
|
|
135
|
+
preferredModel = `mc-anthropic/${Object.keys(anthropicModels)[0]}`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const newConfig = {
|
|
139
|
+
...existingConfig,
|
|
140
|
+
$schema: existingConfig.$schema || 'https://opencode.ai/config.json',
|
|
141
|
+
provider: newProviders,
|
|
142
|
+
...(preferredModel ? { model: preferredModel } : {}),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// 6. 写入配置文件
|
|
146
|
+
await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
|
|
147
|
+
|
|
148
|
+
// 7. 提示用户设置环境变量
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
message: `已配置 ${totalModels} 个模型 → ${paths.configFile}`,
|
|
152
|
+
configPath: paths.configFile,
|
|
153
|
+
hint: '重启 OpenCode 使配置生效',
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
message: `配置失败: ${error.message}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* 检查 OpenCode 配置状态
|
|
165
|
+
*/
|
|
166
|
+
configureOpenCode.checkStatus = async function() {
|
|
167
|
+
const paths = getOpenCodeConfigPaths();
|
|
168
|
+
|
|
169
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
170
|
+
try {
|
|
171
|
+
const config = await fs.readJson(paths.configFile);
|
|
172
|
+
|
|
173
|
+
if (config.provider?.['mc-anthropic'] || config.provider?.['mc-openai']) {
|
|
174
|
+
const mc = (config.provider['mc-anthropic']?.models || {});
|
|
175
|
+
const mo = (config.provider['mc-openai']?.models || {});
|
|
176
|
+
const modelCount = Object.keys(mc).length + Object.keys(mo).length;
|
|
177
|
+
return {
|
|
178
|
+
configured: true,
|
|
179
|
+
message: `已配置 ${modelCount} 个 1314mc 模型`,
|
|
180
|
+
};
|
|
181
|
+
} else if (config.provider && Object.keys(config.provider).length > 0) {
|
|
182
|
+
return {
|
|
183
|
+
configured: true,
|
|
184
|
+
message: '已配置(非 1314mc)',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// ignore
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { configured: false };
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 撤销 OpenCode 配置
|
|
197
|
+
*/
|
|
198
|
+
configureOpenCode.revoke = async function() {
|
|
199
|
+
const paths = getOpenCodeConfigPaths();
|
|
200
|
+
|
|
201
|
+
// 清理配置文件中的 duojie provider
|
|
202
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
203
|
+
try {
|
|
204
|
+
const config = await fs.readJson(paths.configFile);
|
|
205
|
+
|
|
206
|
+
if (config.provider?.['mc-anthropic']) {
|
|
207
|
+
delete config.provider['mc-anthropic'];
|
|
208
|
+
}
|
|
209
|
+
if (config.provider?.['mc-openai']) {
|
|
210
|
+
delete config.provider['mc-openai'];
|
|
211
|
+
}
|
|
212
|
+
if (config.provider?.duojie) {
|
|
213
|
+
delete config.provider.duojie;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (config.model?.startsWith('mc-') || config.model?.startsWith('duojie/')) {
|
|
217
|
+
delete config.model;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
await fs.writeJson(paths.configFile, config, { spaces: 2 });
|
|
221
|
+
} catch {
|
|
222
|
+
// ignore
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export default configureOpenCode;
|