imtoagent 0.3.1 → 0.3.3

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.
@@ -158,7 +158,7 @@ async function cmdStart() {
158
158
  const pkgDir = path.resolve(import.meta.dirname, '..');
159
159
  const indexFile = path.join(pkgDir, 'index.ts');
160
160
 
161
- const child = Bun.spawn(['bun', 'run', indexFile], {
161
+ const child = Bun.spawn([process.execPath, 'run', indexFile], {
162
162
  cwd: dataDir,
163
163
  env: { ...process.env, IMTOAGENT_HOME: dataDir },
164
164
  stdout: 'pipe',
@@ -384,7 +384,7 @@ async function cmdDaemon(): Promise<void> {
384
384
 
385
385
  const logStream = fs.createWriteStream(logFile, { flags: 'a' });
386
386
 
387
- const child = Bun.spawn(['bun', 'run', indexFile], {
387
+ const child = Bun.spawn([process.execPath, 'run', indexFile], {
388
388
  cwd: dataDir,
389
389
  env: { ...process.env, IMTOAGENT_HOME: dataDir },
390
390
  stdout: 'pipe',
@@ -107,7 +107,7 @@ async function promptText(label: string, defaultValue = ''): Promise<string> {
107
107
  while (true) {
108
108
  const key = await readKey();
109
109
 
110
- if (key === KEY.ENTER) {
110
+ if (key === KEY.ENTER || key === '\n') {
111
111
  break;
112
112
  } else if (key === KEY.ESC) {
113
113
  process.stdout.write('\x1B[0K\n');
@@ -119,13 +119,13 @@ async function promptText(label: string, defaultValue = ''): Promise<string> {
119
119
  }
120
120
  } else if (key === KEY.UP || key === KEY.DOWN) {
121
121
  // 忽略方向键
122
- } else if (key.length === 1 && key !== KEY.SPACE) {
123
- // 普通字符(空格单独处理)
124
- buf.push(key);
125
- process.stdout.write(key);
126
122
  } else if (key === KEY.SPACE) {
127
123
  buf.push(' ');
128
124
  process.stdout.write(' ');
125
+ } else if (key.length >= 1 && !key.startsWith('\x1b')) {
126
+ // 普通字符 / 粘贴的多字符块(不含转义序列的文本)
127
+ buf.push(key);
128
+ process.stdout.write(key);
129
129
  }
130
130
  }
131
131
  } finally {
@@ -171,6 +171,91 @@ async function confirm(label: string, defaultYes = true): Promise<boolean | -1>
171
171
  }
172
172
  }
173
173
 
174
+ // ================================================================
175
+ // 供应商预设
176
+ // ================================================================
177
+
178
+ interface ProviderPreset {
179
+ name: string;
180
+ baseUrl: string;
181
+ format: 'openai' | 'anthropic';
182
+ models: string[];
183
+ hint?: string; // 额外说明
184
+ }
185
+
186
+ const PROVIDER_PRESETS: ProviderPreset[] = [
187
+ {
188
+ name: 'DashScope(阿里百炼)',
189
+ baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
190
+ format: 'openai',
191
+ models: ['qwen-max', 'qwen-plus', 'qwen-turbo'],
192
+ },
193
+ {
194
+ name: 'DeepSeek',
195
+ baseUrl: 'https://api.deepseek.com/v1',
196
+ format: 'openai',
197
+ models: ['deepseek-chat', 'deepseek-reasoner'],
198
+ },
199
+ {
200
+ name: '智谱 AI(Zhipu)',
201
+ baseUrl: 'https://open.bigmodel.cn/api/paas/v4',
202
+ format: 'openai',
203
+ models: ['glm-4-plus', 'glm-4-flash', 'glm-4'],
204
+ },
205
+ {
206
+ name: 'MiniMax',
207
+ baseUrl: 'https://api.minimax.io/v1',
208
+ format: 'openai',
209
+ models: ['MiniMax-M2.5', 'MiniMax-M1'],
210
+ },
211
+ {
212
+ name: '硅基流动(SiliconFlow)',
213
+ baseUrl: 'https://api.siliconflow.cn/v1',
214
+ format: 'openai',
215
+ models: ['Qwen/Qwen2.5-72B-Instruct', 'deepseek-ai/DeepSeek-V3'],
216
+ },
217
+ {
218
+ name: 'Moonshot(月之暗面)',
219
+ baseUrl: 'https://api.moonshot.cn/v1',
220
+ format: 'openai',
221
+ models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],
222
+ },
223
+ {
224
+ name: 'OpenAI',
225
+ baseUrl: 'https://api.openai.com/v1',
226
+ format: 'openai',
227
+ models: ['gpt-4o', 'gpt-4o-mini', 'o3', 'o4-mini'],
228
+ hint: '需要代理才能访问',
229
+ },
230
+ {
231
+ name: 'Anthropic',
232
+ baseUrl: 'https://api.anthropic.com',
233
+ format: 'anthropic',
234
+ models: ['claude-sonnet-4-20250514', 'claude-haiku-4-20250514', 'claude-opus-4-20250514'],
235
+ hint: '需要代理才能访问',
236
+ },
237
+ {
238
+ name: 'Gemini(Google)',
239
+ baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',
240
+ format: 'openai',
241
+ models: ['gemini-2.5-pro', 'gemini-2.5-flash'],
242
+ hint: '需要代理才能访问',
243
+ },
244
+ {
245
+ name: 'xAI(Grok)',
246
+ baseUrl: 'https://api.x.ai/v1',
247
+ format: 'openai',
248
+ models: ['grok-3', 'grok-3-mini'],
249
+ hint: '需要代理才能访问',
250
+ },
251
+ {
252
+ name: 'Ollama(本地)',
253
+ baseUrl: 'http://localhost:11434/v1',
254
+ format: 'openai',
255
+ models: ['qwen2.5', 'llama3.2', 'deepseek-r1'],
256
+ },
257
+ ];
258
+
174
259
  // ================================================================
175
260
  // IM 平台配置定义
176
261
  // ================================================================
@@ -367,10 +452,10 @@ export async function runSetupWizard(): Promise<void> {
367
452
  const existingIdx = bots.findIndex(b => b.name === botName);
368
453
  if (existingIdx >= 0) {
369
454
  bots[existingIdx] = bot;
370
- console.log(`✅ 已替换: ${name}`);
455
+ console.log(`✅ 已替换: ${botName}`);
371
456
  } else {
372
457
  bots.push(bot);
373
- console.log(`✅ 已添加: ${name}`);
458
+ console.log(`✅ 已添加: ${botName}`);
374
459
  }
375
460
 
376
461
  // 是否继续添加
@@ -399,26 +484,76 @@ export async function runSetupWizard(): Promise<void> {
399
484
  let addingProviders = true;
400
485
  while (addingProviders) {
401
486
  console.log('--- 添加新供应商 ---\n');
402
- const provName = await promptText('供应商名称 (如 deepseek, dashscope)');
403
- if ((provName as any) === -1) { addingProviders = false; continue; }
404
- if (!provName) { addingProviders = false; continue; }
405
487
 
406
- if (providers[provName]) {
407
- console.log(`⚠️ 供应商 "${provName}" 已存在,将覆盖\n`);
488
+ // 选择预设 or 自定义
489
+ const presetOptions = PROVIDER_PRESETS.map(p => {
490
+ const tag = p.hint ? ` ${p.hint}` : '';
491
+ return `${p.name}${tag}`;
492
+ });
493
+ presetOptions.push('自定义...');
494
+
495
+ const presetIdx = await selectMenu('选择供应商', presetOptions);
496
+ if (presetIdx === -1) { addingProviders = false; continue; }
497
+
498
+ let provName: string, baseUrl: string, format: 'openai' | 'anthropic', models: string[];
499
+
500
+ if (presetIdx < PROVIDER_PRESETS.length) {
501
+ // 使用预设
502
+ const preset = PROVIDER_PRESETS[presetIdx];
503
+ provName = preset.name.split('(')[0].trim().toLowerCase(); // 取简短名称
504
+ baseUrl = preset.baseUrl;
505
+ format = preset.format;
506
+ models = [...preset.models];
507
+
508
+ console.log(`\n✅ 预设已加载:`);
509
+ console.log(` 名称: ${provName}`);
510
+ console.log(` URL: ${preset.baseUrl}`);
511
+ console.log(` 格式: ${preset.format}`);
512
+ console.log(` 模型: ${preset.models.join(', ')}\n`);
513
+
514
+ // 确认/修改简短名称
515
+ const nameEdit = await promptText('供应商名称(留空确认)', provName);
516
+ if ((nameEdit as any) === -1) continue;
517
+ provName = nameEdit || provName;
518
+
519
+ // 确认/修改 Base URL
520
+ const urlEdit = await promptText('Base URL', baseUrl);
521
+ if ((urlEdit as any) === -1) continue;
522
+ baseUrl = urlEdit || baseUrl;
523
+
524
+ // 确认/修改模型列表
525
+ const modelsEdit = await promptText('模型列表(逗号分隔)', models.join(', '));
526
+ if ((modelsEdit as any) === -1) continue;
527
+ if (modelsEdit) models = modelsEdit.split(',').map(s => s.trim()).filter(Boolean);
528
+
529
+ if (providers[provName]) {
530
+ console.log(`⚠️ 供应商 "${provName}" 已存在,将覆盖\n`);
531
+ }
532
+ } else {
533
+ // 自定义
534
+ provName = await promptText('供应商名称 (如 deepseek, dashscope)');
535
+ if ((provName as any) === -1) { addingProviders = false; continue; }
536
+ if (!provName) { addingProviders = false; continue; }
537
+ if (providers[provName]) {
538
+ console.log(`⚠️ 供应商 "${provName}" 已存在,将覆盖\n`);
539
+ }
540
+
541
+ baseUrl = await promptText('Base URL (如 https://api.deepseek.com/v1)');
542
+ if ((baseUrl as any) === -1) continue;
543
+ const modelsStr = await promptText('模型列表 (逗号分隔)');
544
+ if ((modelsStr as any) === -1) continue;
545
+ models = (modelsStr || '').split(',').map(s => s.trim()).filter(Boolean);
546
+
547
+ const formatIdx = await selectMenu('API 格式', ['openai', 'anthropic']);
548
+ if (formatIdx === -1) continue;
549
+ format = ['openai', 'anthropic'][formatIdx];
408
550
  }
409
551
 
410
- const baseUrl = await promptText('Base URL (如 https://api.deepseek.com/v1)');
411
- if ((baseUrl as any) === -1) continue;
552
+ // API Key(所有路径都需要)
412
553
  const apiKey = await promptText('API Key');
413
554
  if ((apiKey as any) === -1) continue;
414
- const modelsStr = await promptText('模型列表 (逗号分隔,如 deepseek-v4-pro,deepseek-v4-flash)');
415
- if ((modelsStr as any) === -1) continue;
416
- const models = (modelsStr || '').split(',').map(s => s.trim()).filter(Boolean);
417
-
418
- const formatIdx = await selectMenu('API 格式', ['openai', 'anthropic']);
419
- if (formatIdx === -1) continue;
420
- const format = ['openai', 'anthropic'][formatIdx];
421
555
 
556
+ // 价格(可选)
422
557
  const priceInput = await promptText('价格 (入/出 每百万 Token,如 0.55,2.19,留空跳过)');
423
558
  if ((priceInput as any) === -1) continue;
424
559
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imtoagent",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "IM ↔ Agent 统一网关 — 飞书/Telegram/微信/企业微信对接 Claude Code/Codex/OpenCode",
5
5
  "type": "module",
6
6
  "bin": {