duojie-helper 0.2.17 → 0.2.19

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
@@ -48,8 +48,9 @@ program
48
48
  type: 'input',
49
49
  name: 'apiKey',
50
50
  message: '请输入你的 Duojie API Key (Ctrl+C 退出):',
51
+ filter: (input) => input.trim(),
51
52
  validate: (input) => {
52
- if (!input || input.trim().length < 10) {
53
+ if (!input || input.length < 10) {
53
54
  return 'API Key 不能为空且长度至少 10 位';
54
55
  }
55
56
  return true;
@@ -217,18 +218,21 @@ program
217
218
  type: 'input',
218
219
  name: 'apiKey',
219
220
  message: '请输入你的 Duojie API Key:',
221
+ filter: (input) => input.trim(),
220
222
  }
221
223
  ]);
222
224
  apiKey = answer.apiKey;
225
+ } else {
226
+ apiKey = apiKey.trim();
223
227
  }
224
228
 
225
229
  // 获取模型列表
226
230
  const spinner = ora('正在验证 API Key...').start();
227
- await fetchModels(apiKey.trim());
231
+ await fetchModels(apiKey);
228
232
  spinner.succeed('验证成功');
229
233
 
230
234
  const configSpinner = ora(`正在配置 ${tool}...`).start();
231
- const result = await configureTool(tool, apiKey.trim());
235
+ const result = await configureTool(tool, apiKey);
232
236
 
233
237
  if (result.success) {
234
238
  configSpinner.succeed(result.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "duojie-helper",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Duojie API 一键配置助手 - 支持 Claude Code, Droid, OpenClaw, Cursor, Cline 等主流 AI 编程工具",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -52,17 +52,41 @@ export function getApiBaseForProtocol(protocol = '') {
52
52
  return withoutV1(API_CONFIG.baseUrl);
53
53
  }
54
54
 
55
+ /**
56
+ * 从 pricing 页面获取公开模型 ID 集合
57
+ * 返回 null 表示获取失败,调用方应跳过过滤
58
+ */
59
+ async function fetchPublicModelIds(apiKey) {
60
+ try {
61
+ const response = await fetch(`${API_CONFIG.baseUrl}/pricing`, {
62
+ headers: { 'Authorization': `Bearer ${apiKey}` },
63
+ });
64
+ if (!response.ok) return null;
65
+ const html = await response.text();
66
+ const ids = new Set();
67
+ for (const m of (html.match(/###\s+([^\n\r]+)/g) || [])) {
68
+ ids.add(m.replace(/^###\s+/, '').trim());
69
+ }
70
+ return ids.size > 0 ? ids : null;
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+
55
76
  /**
56
77
  * 从 API 获取模型列表
57
78
  */
58
79
  export async function fetchModels(apiKey) {
59
80
  try {
60
- const response = await fetch(`${API_CONFIG.baseUrl}/v1/models`, {
61
- headers: {
62
- 'Authorization': `Bearer ${apiKey}`,
63
- 'Content-Type': 'application/json',
64
- },
65
- });
81
+ const [response, publicIds] = await Promise.all([
82
+ fetch(`${API_CONFIG.baseUrl}/v1/models`, {
83
+ headers: {
84
+ 'Authorization': `Bearer ${apiKey}`,
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ }),
88
+ fetchPublicModelIds(apiKey),
89
+ ]);
66
90
 
67
91
  if (!response.ok) {
68
92
  throw new Error(`HTTP ${response.status}`);
@@ -79,6 +103,8 @@ export async function fetchModels(apiKey) {
79
103
  if (data.data && Array.isArray(data.data)) {
80
104
  for (const model of data.data) {
81
105
  const id = model.id;
106
+ // 跳过未在 pricing 页面公开的隐藏模型
107
+ if (publicIds && !publicIds.has(id)) continue;
82
108
  const endpoints = model.supported_endpoint_types || [];
83
109
 
84
110
  // 判断最佳协议
@@ -48,7 +48,7 @@ ${chalk.cyan('步骤 4:')} 填入以下配置:
48
48
  ${chalk.green('Base URL:')} ${baseUrl}
49
49
  ${chalk.green('API Key:')} ${apiKey}
50
50
  ${chalk.green('Model:')} 选择 "Enter model ID" 并输入模型名称
51
- 例如: gpt-5.2 或 claude-opus-4-6-kiro
51
+ 例如: gpt-5.2 或 claude-sonnet-4-6
52
52
 
53
53
  ${chalk.cyan('步骤 5:')} 可选配置:
54
54
  • ${chalk.green('Max Tokens:')} 8192 (推荐)
@@ -5,7 +5,7 @@ import TOML from '@iarna/toml';
5
5
  import { getApiBaseForProtocol } from '../index.js';
6
6
 
7
7
  const DUOJIE_PROVIDER_ID = 'duojie';
8
- const DEFAULT_MODEL = 'gpt-5.2';
8
+ const DEFAULT_MODEL = 'gpt-5.4';
9
9
 
10
10
  /**
11
11
  * 获取 Codex CLI 配置路径
@@ -48,7 +48,7 @@ ${chalk.cyan('步骤 3:')} 填入以下配置:
48
48
  ${chalk.green('Override OpenAI Base URL:')} ${baseUrl}
49
49
  ${chalk.green('Model Name:')} 输入模型名称(注意大小写)
50
50
  例如: gpt-5.2
51
- 或: claude-opus-4-6-kiro
51
+ 或: claude-sonnet-4-6
52
52
 
53
53
  ${chalk.cyan('步骤 4:')} 点击保存,然后在模型列表中选择刚添加的模型
54
54
 
@@ -15,6 +15,7 @@ function getDroidConfigPaths() {
15
15
  return {
16
16
  configDir: path.join(home, '.factory'),
17
17
  configFile: path.join(home, '.factory', 'config.json'),
18
+ settingsFile: path.join(home, '.factory', 'settings.json'),
18
19
  };
19
20
  }
20
21
 
@@ -69,7 +70,8 @@ function generateDisplayName(modelId) {
69
70
  * @returns {string} provider 名称
70
71
  */
71
72
  function determineProvider(modelId, endpoints = []) {
72
- return 'anthropic';
73
+ if (modelId.startsWith('claude')) return 'anthropic';
74
+ return 'openai';
73
75
  }
74
76
 
75
77
  /**
@@ -166,6 +168,19 @@ export async function configureDroid(apiKey) {
166
168
  // 6. 写入配置文件
167
169
  await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
168
170
 
171
+ // 7. 清空 settings.json 中的 customModels(如文件和字段存在)
172
+ if (await fs.pathExists(paths.settingsFile)) {
173
+ try {
174
+ const settings = await fs.readJson(paths.settingsFile);
175
+ if (Array.isArray(settings.customModels) && settings.customModels.length > 0) {
176
+ settings.customModels = [];
177
+ await fs.writeJson(paths.settingsFile, settings, { spaces: 2 });
178
+ }
179
+ } catch {
180
+ // 忽略,不影响主流程
181
+ }
182
+ }
183
+
169
184
  return {
170
185
  success: true,
171
186
  message: `已配置 ${duojieModels.length} 个模型 → ${paths.configFile}`,
@@ -149,17 +149,15 @@ export async function configureOpenClaw(apiKey) {
149
149
 
150
150
  // 默认模型:优先 gpt-5.2(如存在)
151
151
  const gpt52 = gptModels.find(m => m.id === 'gpt-5.2')?.id || null;
152
- let defaultProvider = modelIdToProvider.get('claude-opus-4-6-kiro') || 'duojie-claude';
153
- let defaultModelId = 'claude-opus-4-6-kiro';
152
+ let defaultProvider = 'duojie-claude';
153
+ let defaultModelId = claudeModels[0]?.id || allModels[0]?.id || '';
154
154
 
155
155
  if (gpt52) {
156
156
  defaultProvider = modelIdToProvider.get(gpt52) || providerNameForApi('openai-completions');
157
157
  defaultModelId = gpt52;
158
158
  } else if (claudeModels.length > 0) {
159
159
  defaultModelId = (
160
- claudeModels.find(m => m.id.includes('opus-4-6') && m.id.includes('kiro')) ||
161
160
  claudeModels.find(m => m.id.includes('opus-4-6')) ||
162
- claudeModels.find(m => m.id.includes('opus') && m.id.includes('kiro')) ||
163
161
  claudeModels.find(m => m.id.includes('opus')) ||
164
162
  claudeModels.find(m => m.id.includes('sonnet')) ||
165
163
  claudeModels[0]