duojie-helper 0.2.2 → 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 CHANGED
@@ -11,7 +11,7 @@ const program = new Command();
11
11
  program
12
12
  .name('duojie')
13
13
  .description('🎮 Duojie API 一键配置助手 - 支持主流 AI 编程工具')
14
- .version('0.2.0');
14
+ .version('0.2.3');
15
15
 
16
16
  // 默认命令 - 交互式配置
17
17
  program
@@ -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.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,43 +108,49 @@ const toolConfigurators = {
107
108
  codex: configureCodex,
108
109
  continue: configureContinue,
109
110
  opencode: configureOpenCode,
111
+ droid: configureDroid,
110
112
  };
111
113
 
112
114
  // 工具信息
113
115
  const toolInfo = {
114
116
  claude: {
115
117
  name: 'Claude Code',
116
- description: 'Anthropic 官方 CLI 工具 (自动配置)',
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
- description: '多平台 AI 助手网关 (自动配置)',
128
+ description: '多平台 AI 助手网关(自动配置)',
122
129
  configPath: '~/.openclaw/openclaw.json',
123
130
  },
124
131
  opencode: {
125
132
  name: 'OpenCode',
126
- description: '终端 AI 编码助手 (自动配置)',
133
+ description: '终端 AI 编码助手(自动配置)',
127
134
  configPath: '~/.config/opencode/opencode.json',
128
135
  },
129
136
  continue: {
130
137
  name: 'Continue',
131
- description: 'VS Code/JetBrains AI 插件 (自动配置)',
138
+ description: 'VS Code/JetBrains AI 插件(自动配置)',
132
139
  configPath: '~/.continue/config.json',
133
140
  },
134
141
  codex: {
135
142
  name: 'Codex CLI',
136
- description: 'OpenAI Codex 命令行工具 (自动配置)',
143
+ description: 'OpenAI Codex 命令行工具(自动配置)',
137
144
  configPath: '~/.codex/config.toml',
138
145
  },
139
146
  cursor: {
140
147
  name: 'Cursor',
141
- description: 'AI 代码编辑器 (需手动配置,需 Pro 订阅)',
148
+ description: 'AI 代码编辑器(需手动配置,需 Pro 订阅)',
142
149
  configPath: 'Cursor Settings → Models',
143
150
  },
144
151
  cline: {
145
152
  name: 'Cline',
146
- description: 'VS Code AI 编程插件 (需手动配置)',
153
+ description: 'VS Code AI 编程插件(需手动配置)',
147
154
  configPath: 'Cline 插件设置',
148
155
  },
149
156
  };
@@ -151,7 +151,7 @@ configureClaudeCode.checkStatus = async function() {
151
151
  } else if (settings.env?.ANTHROPIC_API_KEY || settings.env?.ANTHROPIC_AUTH_TOKEN) {
152
152
  return {
153
153
  configured: true,
154
- message: '已配置 (非 Duojie)',
154
+ message: '已配置(非 Duojie',
155
155
  };
156
156
  }
157
157
  } catch {
@@ -53,7 +53,7 @@ ${chalk.cyan('步骤 4:')} 填入以下配置:
53
53
  ${chalk.cyan('步骤 5:')} 可选配置:
54
54
  • ${chalk.green('Max Tokens:')} 8192 (推荐)
55
55
  • Context Window Size: 200000
56
- • 取消勾选 "Support Images" (如果模型不支持)
56
+ • 取消勾选 "Support Images"(如果模型不支持)
57
57
 
58
58
  ${chalk.gray('提示: 配置完成后即可在 Cline 中使用 Duojie API')}
59
59
  `;
@@ -82,7 +82,7 @@ configureCline.checkStatus = async function() {
82
82
  if (await fs.pathExists(paths.globalStorageDir)) {
83
83
  return {
84
84
  configured: true,
85
- message: 'Cline 插件已安装 (配置状态需在 VS Code 中查看)',
85
+ message: 'Cline 插件已安装(配置状态需在 VS Code 中查看)',
86
86
  };
87
87
  }
88
88
 
@@ -228,7 +228,7 @@ configureCodex.checkStatus = async function() {
228
228
  if (content.includes('model_providers.duojie') || content.includes('duojie')) {
229
229
  return { configured: true, message: '已配置 Duojie API' };
230
230
  } else if (content.includes('model_provider')) {
231
- return { configured: true, message: '已配置 (非 Duojie)' };
231
+ return { configured: true, message: '已配置(非 Duojie' };
232
232
  }
233
233
  } catch {}
234
234
  }
@@ -124,7 +124,7 @@ configureContinue.checkStatus = async function() {
124
124
  if (config._duojie?.configured) {
125
125
  return { configured: true, message: '已配置 Duojie API' };
126
126
  } else if (config.models?.length > 0) {
127
- return { configured: true, message: '已配置 (非 Duojie)' };
127
+ return { configured: true, message: '已配置(非 Duojie' };
128
128
  }
129
129
  } catch {}
130
130
  }
@@ -46,14 +46,14 @@ ${chalk.cyan('步骤 3:')} 填入以下配置:
46
46
  ${chalk.green('Protocol:')} OpenAI
47
47
  ${chalk.green('OpenAI API Key:')} ${apiKey}
48
48
  ${chalk.green('Override OpenAI Base URL:')} ${baseUrl}
49
- ${chalk.green('Model Name:')} 输入模型名称 (注意大小写)
49
+ ${chalk.green('Model Name:')} 输入模型名称(注意大小写)
50
50
  例如: claude-sonnet-4-5-20250929
51
51
  或: gpt-4o
52
52
 
53
53
  ${chalk.cyan('步骤 4:')} 点击保存,然后在模型列表中选择刚添加的模型
54
54
 
55
55
  ${chalk.cyan('可选配置:')}
56
- ${chalk.green('Max Tokens:')} 8192 (推荐)
56
+ ${chalk.green('Max Tokens:')} 8192(推荐)
57
57
 
58
58
  ${chalk.gray('提示: 如果遇到 "The model does not work with your current plan" 错误,')}
59
59
  ${chalk.gray(' 说明您的 Cursor 订阅不支持自定义模型')}
@@ -62,7 +62,7 @@ ${chalk.gray(' 说明您的 Cursor 订阅不支持自定义模型')}
62
62
  return {
63
63
  success: true,
64
64
  manual: true,
65
- message: '需要手动配置 (需 Cursor Pro)',
65
+ message: '需要手动配置(需 Cursor Pro',
66
66
  instructions: instructions,
67
67
  configInfo: {
68
68
  protocol: 'OpenAI',
@@ -81,7 +81,7 @@ configureCursor.checkStatus = async function() {
81
81
  if (await fs.pathExists(paths.configDir)) {
82
82
  return {
83
83
  configured: true,
84
- message: 'Cursor 已安装 (配置状态需在 Cursor 中查看)',
84
+ message: 'Cursor 已安装(配置状态需在 Cursor 中查看)',
85
85
  };
86
86
  }
87
87
 
@@ -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;
@@ -232,7 +232,7 @@ configureOpenClaw.checkStatus = async function() {
232
232
  } else if (config.models?.providers) {
233
233
  return {
234
234
  configured: true,
235
- message: '已配置 (非 Duojie)',
235
+ message: '已配置(非 Duojie',
236
236
  };
237
237
  }
238
238
  } catch {
@@ -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 使用 XDG 规范
14
- let configDir, dataDir;
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: 8192,
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: 8192,
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/openai-compatible',
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: '重启 OpenCode 使配置生效',
146
+ hint: '请设置环境变量 ANTHROPIC_API_KEY 或在 OpenCode 中配置 API Key',
174
147
  };
175
148
  } catch (error) {
176
149
  return {
@@ -190,15 +163,16 @@ configureOpenCode.checkStatus = async function() {
190
163
  try {
191
164
  const config = await fs.readJson(paths.configFile);
192
165
 
193
- if (config._duojie?.configured || config.provider?.duojie) {
166
+ if (config.provider?.duojie) {
167
+ const modelCount = Object.keys(config.provider.duojie.models || {}).length;
194
168
  return {
195
169
  configured: true,
196
- message: '已配置 Duojie API',
170
+ message: `已配置 ${modelCount} 个 Duojie 模型`,
197
171
  };
198
172
  } else if (config.provider && Object.keys(config.provider).length > 0) {
199
173
  return {
200
174
  configured: true,
201
- message: '已配置 (非 Duojie)',
175
+ message: '已配置(非 Duojie',
202
176
  };
203
177
  }
204
178
  } catch {
@@ -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;