duojie-helper 0.2.3 → 0.2.4
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/bin/cli.js +6 -1
- package/package.json +5 -2
- package/src/index.js +7 -0
- package/src/tools/droid.js +252 -0
- package/src/tools/opencode.js +28 -70
package/bin/cli.js
CHANGED
|
@@ -87,6 +87,7 @@ program
|
|
|
87
87
|
loop: false,
|
|
88
88
|
choices: [
|
|
89
89
|
{ name: '🤖 Claude Code - 自动配置', value: 'claude' },
|
|
90
|
+
{ name: '🏭 Droid - 自动配置', value: 'droid' },
|
|
90
91
|
{ name: '🦞 OpenClaw / Moltbot - 自动配置', value: 'openclaw' },
|
|
91
92
|
{ name: '🖥️ OpenCode - 自动配置', value: 'opencode' },
|
|
92
93
|
{ name: '💻 Continue - 自动配置', value: 'continue' },
|
|
@@ -166,6 +167,9 @@ program
|
|
|
166
167
|
if (tools.includes('claude')) {
|
|
167
168
|
console.log(chalk.gray(' • Claude Code: 重新打开终端窗口'));
|
|
168
169
|
}
|
|
170
|
+
if (tools.includes('droid')) {
|
|
171
|
+
console.log(chalk.gray(' • Droid: 重启 Factory'));
|
|
172
|
+
}
|
|
169
173
|
if (tools.includes('openclaw')) {
|
|
170
174
|
console.log(chalk.gray(' • OpenClaw: 运行 openclaw gateway restart'));
|
|
171
175
|
}
|
|
@@ -198,7 +202,7 @@ program
|
|
|
198
202
|
// 配置单个工具
|
|
199
203
|
program
|
|
200
204
|
.command('config <tool>')
|
|
201
|
-
.description('配置单个工具 (claude/openclaw/opencode/cursor/cline/codex/continue)')
|
|
205
|
+
.description('配置单个工具 (claude/droid/openclaw/opencode/cursor/cline/codex/continue)')
|
|
202
206
|
.option('-k, --key <apiKey>', 'API Key')
|
|
203
207
|
.action(async (tool, options) => {
|
|
204
208
|
try {
|
|
@@ -296,6 +300,7 @@ program
|
|
|
296
300
|
|
|
297
301
|
支持的工具 (自动配置):
|
|
298
302
|
• claude - Claude Code (Anthropic 官方 CLI)
|
|
303
|
+
• droid - Droid (Factory 的 AI 编程工具)
|
|
299
304
|
• openclaw - OpenClaw / Moltbot
|
|
300
305
|
• opencode - OpenCode (终端 AI 编码助手)
|
|
301
306
|
• continue - Continue (VS Code/JetBrains)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "duojie-helper",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Duojie API 一键配置助手 - 支持 Claude Code, OpenClaw, Cursor, Cline 等主流 AI 编程工具",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Duojie API 一键配置助手 - 支持 Claude Code, Droid, OpenClaw, Cursor, Cline 等主流 AI 编程工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"duojie": "./bin/cli.js",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"ai",
|
|
18
18
|
"api",
|
|
19
19
|
"claude",
|
|
20
|
+
"droid",
|
|
21
|
+
"factory",
|
|
20
22
|
"openclaw",
|
|
23
|
+
"opencode",
|
|
21
24
|
"cursor",
|
|
22
25
|
"cline",
|
|
23
26
|
"config",
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { configureCline } from './tools/cline.js';
|
|
|
7
7
|
import { configureCodex } from './tools/codex.js';
|
|
8
8
|
import { configureContinue } from './tools/continue.js';
|
|
9
9
|
import { configureOpenCode } from './tools/opencode.js';
|
|
10
|
+
import { configureDroid } from './tools/droid.js';
|
|
10
11
|
|
|
11
12
|
// API 配置
|
|
12
13
|
export const API_CONFIG = {
|
|
@@ -107,6 +108,7 @@ const toolConfigurators = {
|
|
|
107
108
|
codex: configureCodex,
|
|
108
109
|
continue: configureContinue,
|
|
109
110
|
opencode: configureOpenCode,
|
|
111
|
+
droid: configureDroid,
|
|
110
112
|
};
|
|
111
113
|
|
|
112
114
|
// 工具信息
|
|
@@ -116,6 +118,11 @@ const toolInfo = {
|
|
|
116
118
|
description: 'Anthropic 官方 CLI 工具(自动配置)',
|
|
117
119
|
configPath: '~/.claude/settings.json',
|
|
118
120
|
},
|
|
121
|
+
droid: {
|
|
122
|
+
name: 'Droid',
|
|
123
|
+
description: 'Factory 的 AI 编程工具(自动配置)',
|
|
124
|
+
configPath: '~/.factory/config.json',
|
|
125
|
+
},
|
|
119
126
|
openclaw: {
|
|
120
127
|
name: 'OpenClaw / Moltbot',
|
|
121
128
|
description: '多平台 AI 助手网关(自动配置)',
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { API_CONFIG, getModels } from '../index.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 获取 Droid (Factory) 配置路径
|
|
8
|
+
*/
|
|
9
|
+
function getDroidConfigPaths() {
|
|
10
|
+
const home = os.homedir();
|
|
11
|
+
return {
|
|
12
|
+
configDir: path.join(home, '.factory'),
|
|
13
|
+
configFile: path.join(home, '.factory', 'config.json'),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 生成友好的模型显示名称
|
|
19
|
+
* @param {string} modelId - 模型 ID
|
|
20
|
+
* @returns {string} 显示名称
|
|
21
|
+
*/
|
|
22
|
+
function generateDisplayName(modelId) {
|
|
23
|
+
// 将 model id 转换为友好名称
|
|
24
|
+
// claude-opus-4-5-kiro -> Opus 4.5 Kiro
|
|
25
|
+
// gpt-5.2-codex -> GPT 5.2 Codex
|
|
26
|
+
// gemini-3-pro-preview -> Gemini 3 Pro Preview
|
|
27
|
+
|
|
28
|
+
let name = modelId;
|
|
29
|
+
|
|
30
|
+
// 移除常见前缀
|
|
31
|
+
if (name.startsWith('claude-')) {
|
|
32
|
+
name = name.substring(7);
|
|
33
|
+
} else if (name.startsWith('gpt-')) {
|
|
34
|
+
name = 'GPT ' + name.substring(4);
|
|
35
|
+
} else if (name.startsWith('gemini-')) {
|
|
36
|
+
name = 'Gemini ' + name.substring(7);
|
|
37
|
+
} else if (name.startsWith('glm-')) {
|
|
38
|
+
name = 'GLM-' + name.substring(4);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 将连字符转换为空格,并首字母大写
|
|
42
|
+
name = name
|
|
43
|
+
.split('-')
|
|
44
|
+
.map(part => {
|
|
45
|
+
// 处理版本号(如 4-5 -> 4.5)
|
|
46
|
+
if (/^\d+$/.test(part)) {
|
|
47
|
+
return part;
|
|
48
|
+
}
|
|
49
|
+
// 首字母大写
|
|
50
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
51
|
+
})
|
|
52
|
+
.join(' ');
|
|
53
|
+
|
|
54
|
+
// 处理版本号格式(4 5 -> 4.5)
|
|
55
|
+
name = name.replace(/(\d+)\s+(\d+)(?=\s|$)/g, '$1.$2');
|
|
56
|
+
|
|
57
|
+
// 添加后缀标识
|
|
58
|
+
return `${name} [duojie.games]`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 根据模型 ID 确定 provider
|
|
63
|
+
* @param {string} modelId - 模型 ID
|
|
64
|
+
* @param {string[]} endpoints - 支持的 endpoint 类型
|
|
65
|
+
* @returns {string} provider 名称
|
|
66
|
+
*/
|
|
67
|
+
function determineProvider(modelId, endpoints = []) {
|
|
68
|
+
const id = modelId.toLowerCase();
|
|
69
|
+
|
|
70
|
+
if (id.startsWith('gpt')) {
|
|
71
|
+
// GPT 模型使用 openai-response
|
|
72
|
+
return 'openai-response';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Claude, Gemini, GLM 等使用 anthropic
|
|
76
|
+
if (id.startsWith('claude') || id.startsWith('gemini') || id.startsWith('glm')) {
|
|
77
|
+
return 'anthropic';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 其他模型根据 endpoints 判断
|
|
81
|
+
if (endpoints.includes('anthropic')) {
|
|
82
|
+
return 'anthropic';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 'openai-response';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 配置 Droid (Factory)
|
|
90
|
+
* 自动配置所有可用模型到 custom_models
|
|
91
|
+
*/
|
|
92
|
+
export async function configureDroid(apiKey) {
|
|
93
|
+
const paths = getDroidConfigPaths();
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// 确保目录存在
|
|
97
|
+
await fs.ensureDir(paths.configDir);
|
|
98
|
+
|
|
99
|
+
// 1. 读取现有配置(如果存在)
|
|
100
|
+
let existingConfig = {};
|
|
101
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
102
|
+
try {
|
|
103
|
+
existingConfig = await fs.readJson(paths.configFile);
|
|
104
|
+
} catch {
|
|
105
|
+
existingConfig = {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 2. 获取模型列表
|
|
110
|
+
const models = getModels();
|
|
111
|
+
const rawModels = API_CONFIG.rawModels || [];
|
|
112
|
+
|
|
113
|
+
// 创建模型 ID 到 endpoints 的映射
|
|
114
|
+
const endpointsMap = {};
|
|
115
|
+
for (const m of rawModels) {
|
|
116
|
+
endpointsMap[m.id] = m.supported_endpoint_types || [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 3. 构建 Duojie 的 custom_models
|
|
120
|
+
const duojieModels = [];
|
|
121
|
+
|
|
122
|
+
// 处理所有模型类别
|
|
123
|
+
const allModels = [
|
|
124
|
+
...(models.claude || []),
|
|
125
|
+
...(models.gpt || []),
|
|
126
|
+
...(models.gemini || []),
|
|
127
|
+
...(models.other || []),
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
for (const m of allModels) {
|
|
131
|
+
const endpoints = endpointsMap[m.id] || [];
|
|
132
|
+
const provider = determineProvider(m.id, endpoints);
|
|
133
|
+
|
|
134
|
+
duojieModels.push({
|
|
135
|
+
model_display_name: generateDisplayName(m.id),
|
|
136
|
+
model: m.id,
|
|
137
|
+
base_url: API_CONFIG.baseUrl,
|
|
138
|
+
api_key: apiKey,
|
|
139
|
+
provider: provider,
|
|
140
|
+
supports_vision: true,
|
|
141
|
+
max_tokens: 8192,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (duojieModels.length === 0) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
message: '没有可用的模型',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 4. 合并配置(保留用户其他自定义模型)
|
|
153
|
+
let existingCustomModels = existingConfig.custom_models || [];
|
|
154
|
+
|
|
155
|
+
// 过滤掉已有的 Duojie 模型(通过 base_url 或 display_name 判断)
|
|
156
|
+
existingCustomModels = existingCustomModels.filter(m => {
|
|
157
|
+
const isDuojie =
|
|
158
|
+
(m.base_url && m.base_url.includes('duojie')) ||
|
|
159
|
+
(m.model_display_name && m.model_display_name.includes('[duojie.games]'));
|
|
160
|
+
return !isDuojie;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// 5. 合并新旧模型
|
|
164
|
+
const newConfig = {
|
|
165
|
+
...existingConfig,
|
|
166
|
+
custom_models: [
|
|
167
|
+
...existingCustomModels,
|
|
168
|
+
...duojieModels,
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// 6. 写入配置文件
|
|
173
|
+
await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
success: true,
|
|
177
|
+
message: `已配置 ${duojieModels.length} 个模型 → ${paths.configFile}`,
|
|
178
|
+
configPath: paths.configFile,
|
|
179
|
+
hint: '重启 Factory 使配置生效',
|
|
180
|
+
};
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
message: `配置失败: ${error.message}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 检查 Droid 配置状态
|
|
191
|
+
*/
|
|
192
|
+
configureDroid.checkStatus = async function() {
|
|
193
|
+
const paths = getDroidConfigPaths();
|
|
194
|
+
|
|
195
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
196
|
+
try {
|
|
197
|
+
const config = await fs.readJson(paths.configFile);
|
|
198
|
+
|
|
199
|
+
// 检查是否有 Duojie 的自定义模型
|
|
200
|
+
const customModels = config.custom_models || [];
|
|
201
|
+
const duojieModels = customModels.filter(m =>
|
|
202
|
+
(m.base_url && m.base_url.includes('duojie')) ||
|
|
203
|
+
(m.model_display_name && m.model_display_name.includes('[duojie.games]'))
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (duojieModels.length > 0) {
|
|
207
|
+
return {
|
|
208
|
+
configured: true,
|
|
209
|
+
message: `已配置 ${duojieModels.length} 个 Duojie 模型`,
|
|
210
|
+
};
|
|
211
|
+
} else if (customModels.length > 0) {
|
|
212
|
+
return {
|
|
213
|
+
configured: true,
|
|
214
|
+
message: '已配置(非 Duojie)',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
// ignore
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { configured: false };
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 撤销 Droid 配置
|
|
227
|
+
*/
|
|
228
|
+
configureDroid.revoke = async function() {
|
|
229
|
+
const paths = getDroidConfigPaths();
|
|
230
|
+
|
|
231
|
+
if (await fs.pathExists(paths.configFile)) {
|
|
232
|
+
try {
|
|
233
|
+
const config = await fs.readJson(paths.configFile);
|
|
234
|
+
|
|
235
|
+
// 只删除 Duojie 的自定义模型
|
|
236
|
+
if (config.custom_models) {
|
|
237
|
+
config.custom_models = config.custom_models.filter(m => {
|
|
238
|
+
const isDuojie =
|
|
239
|
+
(m.base_url && m.base_url.includes('duojie')) ||
|
|
240
|
+
(m.model_display_name && m.model_display_name.includes('[duojie.games]'));
|
|
241
|
+
return !isDuojie;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
await fs.writeJson(paths.configFile, config, { spaces: 2 });
|
|
246
|
+
} catch {
|
|
247
|
+
// ignore
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export default configureDroid;
|
package/src/tools/opencode.js
CHANGED
|
@@ -8,26 +8,13 @@ import { API_CONFIG, getModels } from '../index.js';
|
|
|
8
8
|
*/
|
|
9
9
|
function getOpenCodeConfigPaths() {
|
|
10
10
|
const home = os.homedir();
|
|
11
|
-
const platform = os.platform();
|
|
12
11
|
|
|
13
|
-
// OpenCode
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (platform === 'win32') {
|
|
17
|
-
// Windows: 使用 APPDATA
|
|
18
|
-
configDir = path.join(process.env.APPDATA || home, 'opencode');
|
|
19
|
-
dataDir = path.join(process.env.LOCALAPPDATA || home, 'opencode');
|
|
20
|
-
} else {
|
|
21
|
-
// macOS/Linux: 使用 XDG 规范
|
|
22
|
-
configDir = path.join(process.env.XDG_CONFIG_HOME || path.join(home, '.config'), 'opencode');
|
|
23
|
-
dataDir = path.join(process.env.XDG_DATA_HOME || path.join(home, '.local', 'share'), 'opencode');
|
|
24
|
-
}
|
|
12
|
+
// OpenCode 统一使用 ~/.config/opencode 目录
|
|
13
|
+
const configDir = path.join(home, '.config', 'opencode');
|
|
25
14
|
|
|
26
15
|
return {
|
|
27
16
|
configDir,
|
|
28
17
|
configFile: path.join(configDir, 'opencode.json'),
|
|
29
|
-
dataDir,
|
|
30
|
-
authFile: path.join(dataDir, 'auth.json'),
|
|
31
18
|
};
|
|
32
19
|
}
|
|
33
20
|
|
|
@@ -41,7 +28,6 @@ export async function configureOpenCode(apiKey) {
|
|
|
41
28
|
try {
|
|
42
29
|
// 确保目录存在
|
|
43
30
|
await fs.ensureDir(paths.configDir);
|
|
44
|
-
await fs.ensureDir(paths.dataDir);
|
|
45
31
|
|
|
46
32
|
// 1. 读取现有配置(如果存在)
|
|
47
33
|
let existingConfig = {};
|
|
@@ -62,21 +48,27 @@ export async function configureOpenCode(apiKey) {
|
|
|
62
48
|
// Claude 模型
|
|
63
49
|
for (const m of (models.claude || [])) {
|
|
64
50
|
duojieModels[m.id] = {
|
|
65
|
-
name: m.name,
|
|
66
51
|
limit: {
|
|
67
52
|
context: 200000,
|
|
68
53
|
output: 8192,
|
|
69
54
|
},
|
|
55
|
+
modalities: {
|
|
56
|
+
input: ['text', 'image'],
|
|
57
|
+
output: ['text'],
|
|
58
|
+
},
|
|
70
59
|
};
|
|
71
60
|
}
|
|
72
61
|
|
|
73
62
|
// GPT 模型
|
|
74
63
|
for (const m of (models.gpt || [])) {
|
|
75
64
|
duojieModels[m.id] = {
|
|
76
|
-
name: m.name,
|
|
77
65
|
limit: {
|
|
78
66
|
context: 128000,
|
|
79
|
-
output:
|
|
67
|
+
output: 16384,
|
|
68
|
+
},
|
|
69
|
+
modalities: {
|
|
70
|
+
input: ['text', 'image'],
|
|
71
|
+
output: ['text'],
|
|
80
72
|
},
|
|
81
73
|
};
|
|
82
74
|
}
|
|
@@ -84,21 +76,27 @@ export async function configureOpenCode(apiKey) {
|
|
|
84
76
|
// Gemini 模型
|
|
85
77
|
for (const m of (models.gemini || [])) {
|
|
86
78
|
duojieModels[m.id] = {
|
|
87
|
-
name: m.name,
|
|
88
79
|
limit: {
|
|
89
80
|
context: 1000000,
|
|
90
81
|
output: 8192,
|
|
91
82
|
},
|
|
83
|
+
modalities: {
|
|
84
|
+
input: ['text', 'image'],
|
|
85
|
+
output: ['text'],
|
|
86
|
+
},
|
|
92
87
|
};
|
|
93
88
|
}
|
|
94
89
|
|
|
95
90
|
// 其他模型
|
|
96
91
|
for (const m of (models.other || [])) {
|
|
97
92
|
duojieModels[m.id] = {
|
|
98
|
-
name: m.name,
|
|
99
93
|
limit: {
|
|
100
94
|
context: 128000,
|
|
101
|
-
output:
|
|
95
|
+
output: 4096,
|
|
96
|
+
},
|
|
97
|
+
modalities: {
|
|
98
|
+
input: ['text', 'image'],
|
|
99
|
+
output: ['text'],
|
|
102
100
|
},
|
|
103
101
|
};
|
|
104
102
|
}
|
|
@@ -114,10 +112,10 @@ export async function configureOpenCode(apiKey) {
|
|
|
114
112
|
|
|
115
113
|
// 4. 构建 Duojie provider 配置
|
|
116
114
|
const duojieProvider = {
|
|
117
|
-
npm: '@ai-sdk/
|
|
115
|
+
npm: '@ai-sdk/anthropic',
|
|
118
116
|
name: 'Duojie API',
|
|
119
117
|
options: {
|
|
120
|
-
baseURL: API_CONFIG.baseUrl
|
|
118
|
+
baseURL: `${API_CONFIG.baseUrl}/v1`,
|
|
121
119
|
},
|
|
122
120
|
models: duojieModels,
|
|
123
121
|
};
|
|
@@ -129,48 +127,23 @@ export async function configureOpenCode(apiKey) {
|
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
const newConfig = {
|
|
132
|
-
$schema: 'https://opencode.ai/config.json',
|
|
133
130
|
...existingConfig,
|
|
131
|
+
$schema: existingConfig.$schema || 'https://opencode.ai/config.json',
|
|
134
132
|
provider: {
|
|
135
133
|
...existingConfig.provider,
|
|
136
134
|
duojie: duojieProvider,
|
|
137
135
|
},
|
|
138
|
-
// 标记配置来源
|
|
139
|
-
_duojie: {
|
|
140
|
-
configured: true,
|
|
141
|
-
configuredAt: new Date().toISOString(),
|
|
142
|
-
},
|
|
143
136
|
};
|
|
144
137
|
|
|
145
|
-
// 如果没有设置默认模型,设置为第一个 Claude 模型或 GPT 模型
|
|
146
|
-
if (!existingConfig.model) {
|
|
147
|
-
const firstModel = models.claude?.[0]?.id || models.gpt?.[0]?.id;
|
|
148
|
-
if (firstModel) {
|
|
149
|
-
newConfig.model = `duojie/${firstModel}`;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
138
|
// 6. 写入配置文件
|
|
154
139
|
await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
|
|
155
140
|
|
|
156
|
-
// 7.
|
|
157
|
-
let authConfig = {};
|
|
158
|
-
if (await fs.pathExists(paths.authFile)) {
|
|
159
|
-
try {
|
|
160
|
-
authConfig = await fs.readJson(paths.authFile);
|
|
161
|
-
} catch {
|
|
162
|
-
authConfig = {};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
authConfig.duojie = apiKey;
|
|
167
|
-
await fs.writeJson(paths.authFile, authConfig, { spaces: 2 });
|
|
168
|
-
|
|
141
|
+
// 7. 提示用户设置环境变量
|
|
169
142
|
return {
|
|
170
143
|
success: true,
|
|
171
144
|
message: `已配置 ${totalModels} 个模型 → ${paths.configFile}`,
|
|
172
145
|
configPath: paths.configFile,
|
|
173
|
-
hint: '
|
|
146
|
+
hint: '请设置环境变量 ANTHROPIC_API_KEY 或在 OpenCode 中配置 API Key',
|
|
174
147
|
};
|
|
175
148
|
} catch (error) {
|
|
176
149
|
return {
|
|
@@ -190,10 +163,11 @@ configureOpenCode.checkStatus = async function() {
|
|
|
190
163
|
try {
|
|
191
164
|
const config = await fs.readJson(paths.configFile);
|
|
192
165
|
|
|
193
|
-
if (config.
|
|
166
|
+
if (config.provider?.duojie) {
|
|
167
|
+
const modelCount = Object.keys(config.provider.duojie.models || {}).length;
|
|
194
168
|
return {
|
|
195
169
|
configured: true,
|
|
196
|
-
message:
|
|
170
|
+
message: `已配置 ${modelCount} 个 Duojie 模型`,
|
|
197
171
|
};
|
|
198
172
|
} else if (config.provider && Object.keys(config.provider).length > 0) {
|
|
199
173
|
return {
|
|
@@ -223,7 +197,6 @@ configureOpenCode.revoke = async function() {
|
|
|
223
197
|
if (config.provider?.duojie) {
|
|
224
198
|
delete config.provider.duojie;
|
|
225
199
|
}
|
|
226
|
-
delete config._duojie;
|
|
227
200
|
|
|
228
201
|
// 如果默认模型是 duojie 的,清除它
|
|
229
202
|
if (config.model?.startsWith('duojie/')) {
|
|
@@ -235,21 +208,6 @@ configureOpenCode.revoke = async function() {
|
|
|
235
208
|
// ignore
|
|
236
209
|
}
|
|
237
210
|
}
|
|
238
|
-
|
|
239
|
-
// 清理认证文件中的 duojie key
|
|
240
|
-
if (await fs.pathExists(paths.authFile)) {
|
|
241
|
-
try {
|
|
242
|
-
const authConfig = await fs.readJson(paths.authFile);
|
|
243
|
-
|
|
244
|
-
if (authConfig.duojie) {
|
|
245
|
-
delete authConfig.duojie;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
await fs.writeJson(paths.authFile, authConfig, { spaces: 2 });
|
|
249
|
-
} catch {
|
|
250
|
-
// ignore
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
211
|
};
|
|
254
212
|
|
|
255
213
|
export default configureOpenCode;
|