imtoagent 0.3.4 → 0.3.6

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.
Files changed (39) hide show
  1. package/README.md +97 -97
  2. package/bin/imtoagent-real +197 -153
  3. package/bin/imtoagent.cjs +13 -5
  4. package/index.ts +106 -106
  5. package/modules/agent/claude-adapter.ts +6 -6
  6. package/modules/agent/claude.ts +6 -6
  7. package/modules/agent/codex-adapter.ts +13 -13
  8. package/modules/agent/codex-exec-server.ts +11 -11
  9. package/modules/agent/codex.ts +29 -29
  10. package/modules/agent/opencode-adapter.ts +17 -17
  11. package/modules/agent/opencode.ts +10 -10
  12. package/modules/capabilities.ts +33 -33
  13. package/modules/cli/setup.ts +164 -164
  14. package/modules/core/config.ts +5 -5
  15. package/modules/core/error.ts +8 -8
  16. package/modules/core/runtime.ts +10 -10
  17. package/modules/core/session.ts +4 -4
  18. package/modules/core/stats.ts +14 -14
  19. package/modules/core/types.ts +7 -7
  20. package/modules/im/feishu.ts +56 -56
  21. package/modules/im/telegram.ts +23 -23
  22. package/modules/im/wechat.ts +54 -54
  23. package/modules/im/wecom.ts +50 -50
  24. package/modules/media/feishu-inbound-adapter.ts +4 -4
  25. package/modules/media/resolver.ts +11 -11
  26. package/modules/media/telegram-inbound-adapter.ts +8 -8
  27. package/modules/prompt-builder.ts +12 -12
  28. package/modules/proxy/anthropic-proxy.ts +31 -31
  29. package/modules/proxy/codex-proxy.ts +18 -18
  30. package/modules/utils/backend-check.ts +12 -12
  31. package/modules/utils/paths.ts +8 -8
  32. package/package.json +1 -1
  33. package/scripts/postinstall.cjs +10 -10
  34. package/scripts/postinstall.ts +13 -13
  35. package/templates/soul.template/identity.md +5 -5
  36. package/templates/soul.template/profile.md +7 -7
  37. package/templates/soul.template/rules.md +5 -5
  38. package/templates/soul.template/skills.md +2 -2
  39. package/templates/soul.template/workspace.md +3 -3
@@ -154,12 +154,12 @@ async function ilinkPost(endpoint: string, body: any, token?: string): Promise<a
154
154
  try {
155
155
  resolve(JSON.parse(data));
156
156
  } catch {
157
- reject(new Error(`解析响应失败: ${data.slice(0, 200)}`));
157
+ reject(new Error(`Failed to parse response: ${data.slice(0, 200)}`));
158
158
  }
159
159
  });
160
160
  });
161
161
  req.on('error', reject);
162
- req.on('timeout', () => { req.destroy(); reject(new Error('请求超时')); });
162
+ req.on('timeout', () => { req.destroy(); reject(new Error('request timeout')); });
163
163
  req.write(bodyStr);
164
164
  req.end();
165
165
  });
@@ -188,7 +188,7 @@ async function ilinkGet(endpoint: string, token?: string): Promise<any> {
188
188
  try {
189
189
  resolve(JSON.parse(data));
190
190
  } catch {
191
- reject(new Error(`解析响应失败: ${data.slice(0, 200)}`));
191
+ reject(new Error(`Failed to parse response: ${data.slice(0, 200)}`));
192
192
  }
193
193
  });
194
194
  }).on('error', reject).on('timeout', reject);
@@ -287,7 +287,7 @@ async function getBotQrcode(localTokenList: string[], token?: string): Promise<{
287
287
  local_token_list: localTokenList,
288
288
  }, token);
289
289
  if (result.ret !== 0 || !result.qrcode) {
290
- throw new Error(`获取二维码失败: ${JSON.stringify(result).slice(0, 200)}`);
290
+ throw new Error(`Failed to get QR code: ${JSON.stringify(result).slice(0, 200)}`);
291
291
  }
292
292
  return { qrcode: result.qrcode, qrcode_img_content: result.qrcode_img_content };
293
293
  }
@@ -326,8 +326,8 @@ async function renderQR(qrContent: string): Promise<void> {
326
326
  * 返回 ilink_bot_id, bot_token, ilink_user_id
327
327
  */
328
328
  export async function bindWechatQR(): Promise<{ botId: string; botToken: string; ilinkUserId: string }> {
329
- console.log('\n📱 微信扫码绑定');
330
- console.log('正在获取二维码...');
329
+ console.log('\n📱 WeChat QR Code Binding');
330
+ console.log('Fetching QR code...');
331
331
 
332
332
  let refreshCount = 0;
333
333
  let currentToken: string | undefined;
@@ -335,9 +335,9 @@ export async function bindWechatQR(): Promise<{ botId: string; botToken: string;
335
335
  while (refreshCount < MAX_QR_REFRESH) {
336
336
  const { qrcode, qrcode_img_content } = await getBotQrcode([], currentToken);
337
337
 
338
- console.log('请使用微信扫描以下二维码:');
338
+ console.log('Please scan the following QR code with WeChat:');
339
339
  await renderQR(qrcode_img_content || qrcode);
340
- console.log('等待扫码中...');
340
+ console.log('Waiting for scan...');
341
341
 
342
342
  const start = Date.now();
343
343
  while (Date.now() - start < QR_POLL_TIMEOUT_MS) {
@@ -345,7 +345,7 @@ export async function bindWechatQR(): Promise<{ botId: string; botToken: string;
345
345
 
346
346
  switch (statusResult.status) {
347
347
  case 'confirmed':
348
- console.log('\n✅ 扫码成功!');
348
+ console.log('\n✅ QR scan successful!');
349
349
  const creds: StoredCreds = {
350
350
  botId: statusResult.ilink_bot_id!,
351
351
  botToken: statusResult.bot_token!,
@@ -372,15 +372,15 @@ export async function bindWechatQR(): Promise<{ botId: string; botToken: string;
372
372
  break;
373
373
 
374
374
  case 'need_verifycode':
375
- console.log('\n⚠️ 需要输入配对码(手机微信显示的数字)');
376
- console.log('此功能暂不支持自动处理,请在手机上完成验证后重试');
377
- throw new Error('需要配对码验证');
375
+ console.log('\n⚠️ Pairing code required (number shown on phone WeChat)');
376
+ console.log('This feature does not support automatic handling yet, please verify on phone and retry');
377
+ throw new Error('Pairing code verification required');
378
378
 
379
379
  case 'verify_code_blocked':
380
- throw new Error('配对码多次错误,请重新扫码');
380
+ throw new Error('Pairing code entered incorrectly too many times, please re-scan');
381
381
 
382
382
  case 'expired':
383
- console.log('\n⏱ 二维码过期,刷新中...');
383
+ console.log('\n⏱ QR code expired, refreshing...');
384
384
  refreshCount++;
385
385
  break;
386
386
 
@@ -393,10 +393,10 @@ export async function bindWechatQR(): Promise<{ botId: string; botToken: string;
393
393
  }
394
394
 
395
395
  refreshCount++;
396
- console.log('\n⏱ 扫码超时,刷新二维码...');
396
+ console.log('\n⏱ QR scan timed out, refreshing QR code...');
397
397
  }
398
398
 
399
- console.log('\n❌ 二维码刷新次数超限,请重试');
399
+ console.log('\n❌ QR code refresh limit exceeded, please retry');
400
400
  process.exit(1);
401
401
  }
402
402
 
@@ -459,7 +459,7 @@ export class WeChatIMModule implements IMModule {
459
459
  getCapabilities(): IMCapabilities {
460
460
  return {
461
461
  text: true,
462
- codeBlock: false, // 微信不支持代码块
462
+ codeBlock: false, // WeChat doesn't support code blocks
463
463
  cardMessage: false,
464
464
  fileSend: true,
465
465
  imageSend: true,
@@ -473,7 +473,7 @@ export class WeChatIMModule implements IMModule {
473
473
 
474
474
  start(handler: MessageHandler): void {
475
475
  if (this.running) {
476
- console.warn('[WeChat] 已在运行中');
476
+ console.warn('[WeChat] Already running');
477
477
  return;
478
478
  }
479
479
  this.handler = handler;
@@ -486,7 +486,7 @@ export class WeChatIMModule implements IMModule {
486
486
  if (this.pollTimer) { clearTimeout(this.pollTimer); this.pollTimer = null; }
487
487
  this._notifyStop().catch(() => {});
488
488
  this.handler = null;
489
- console.log('[WeChat] 已断开');
489
+ console.log('[WeChat] Disconnected');
490
490
  }
491
491
 
492
492
  // ── 连接 & 认证 ──
@@ -503,12 +503,12 @@ export class WeChatIMModule implements IMModule {
503
503
  this.botId = stored.botId;
504
504
  this.botToken = stored.botToken;
505
505
  this.ilinkUserId = stored.ilinkUserId;
506
- console.log('[WeChat] 已加载本地凭证');
506
+ console.log('[WeChat] Loaded local credentials');
507
507
  }
508
508
  }
509
509
 
510
510
  if (!this.botId || !this.botToken) {
511
- console.log('[WeChat] 未找到凭证,启动扫码绑定...');
511
+ console.log('[WeChat] No credentials found, starting QR binding...');
512
512
  const bound = await bindWechatQR();
513
513
  this.botId = bound.botId;
514
514
  this.botToken = bound.botToken;
@@ -518,9 +518,9 @@ export class WeChatIMModule implements IMModule {
518
518
  // 2. 加载 context_token
519
519
  this.contextTokens = new Map(Object.entries(loadContextTokens()));
520
520
 
521
- console.log(`[WeChat] 已认证 (bot: ${this.botId.slice(0, 8)}...)`);
521
+ console.log(`[WeChat] Authenticated (bot: ${this.botId.slice(0, 8)}...)`);
522
522
 
523
- // 3. 通知上线
523
+ // 3. Notify online
524
524
  await this._notifyStart();
525
525
 
526
526
  // 4. 启动 long-poll
@@ -534,9 +534,9 @@ export class WeChatIMModule implements IMModule {
534
534
  await ilinkPost('ilink/bot/msg/notifystart', {
535
535
  base_info: { channel_version: CHANNEL_VERSION, bot_agent: 'IMtoAgent' },
536
536
  }, this.botToken);
537
- console.log('[WeChat] 已通知上线');
537
+ console.log('[WeChat] Notified online');
538
538
  } catch (e: any) {
539
- console.warn(`[WeChat] 通知上线失败: ${e.message}`);
539
+ console.warn(`[WeChat] Failed to notify online: ${e.message}`);
540
540
  }
541
541
  }
542
542
 
@@ -557,13 +557,13 @@ export class WeChatIMModule implements IMModule {
557
557
  // 检查 session 是否暂停(过期冷却)
558
558
  if (Date.now() < this.sessionPausedUntil) {
559
559
  const remaining = Math.ceil((this.sessionPausedUntil - Date.now()) / 1000);
560
- console.log(`[WeChat] Session 冷却中,剩余 ${remaining}s`);
560
+ console.log(`[WeChat] Session cooling down, ${remaining}s remaining`);
561
561
  this.pollTimer = setTimeout(() => this._pollLoop(), Math.min(remaining * 1000, 60000));
562
562
  return;
563
563
  }
564
564
 
565
565
  this._pollOnce().catch(e => {
566
- console.error(`[WeChat] 轮询错误: ${e.message}`);
566
+ console.error(`[WeChat] Poll error: ${e.message}`);
567
567
  if (this.running) {
568
568
  this.pollTimer = setTimeout(() => this._pollLoop(), LONGPOLL_RETRY_DELAY_MS);
569
569
  }
@@ -579,15 +579,15 @@ export class WeChatIMModule implements IMModule {
579
579
  }, this.botToken);
580
580
 
581
581
  if (result.ret === -14) {
582
- // Session 过期,暂停 1 小时
583
- console.error('[WeChat] ⚠️ Session 过期,暂停 1 小时后重试');
582
+ // Session expired, pause for 1 hour
583
+ console.error('[WeChat] ⚠️ Session expired, pausing for 1 hour');
584
584
  this.sessionPausedUntil = Date.now() + SESSION_PAUSE_MS;
585
585
  this.pollTimer = setTimeout(() => this._pollLoop(), SESSION_PAUSE_MS);
586
586
  return;
587
587
  }
588
588
 
589
589
  if (result.ret !== 0) {
590
- throw new Error(`getupdates 错误: ret=${result.ret} ${result.errmsg || ''}`);
590
+ throw new Error(`getupdates error: ret=${result.ret} ${result.errmsg || ''}`);
591
591
  }
592
592
 
593
593
  // 保存续传 buf
@@ -601,7 +601,7 @@ export class WeChatIMModule implements IMModule {
601
601
  try {
602
602
  await this._handleMessage(msg);
603
603
  } catch (e: any) {
604
- console.error(`[WeChat] 消息处理异常: ${e.message}`);
604
+ console.error(`[WeChat] Message processing error: ${e.message}`);
605
605
  }
606
606
  }
607
607
  }
@@ -638,7 +638,7 @@ export class WeChatIMModule implements IMModule {
638
638
  break;
639
639
 
640
640
  case MessageItemType.IMAGE:
641
- text += text ? ' [图片]' : '[图片]';
641
+ text += text ? ' [Image]' : '[Image]';
642
642
  if (item.image_item?.media) {
643
643
  const localPath = await this._downloadMedia(item.image_item.media, item.image_item.aeskey, 'image.png');
644
644
  if (localPath) {
@@ -648,7 +648,7 @@ export class WeChatIMModule implements IMModule {
648
648
  break;
649
649
 
650
650
  case MessageItemType.VOICE:
651
- text += text ? ' [语音]' : '[语音]';
651
+ text += text ? ' [Voice]' : '[Voice]';
652
652
  if (item.voice_item?.media) {
653
653
  const localPath = await this._downloadMedia(item.voice_item.media, item.voice_item.aeskey, 'voice.silk');
654
654
  if (localPath) {
@@ -658,7 +658,7 @@ export class WeChatIMModule implements IMModule {
658
658
  break;
659
659
 
660
660
  case MessageItemType.FILE:
661
- text += text ? ' [文件]' : '[文件]';
661
+ text += text ? ' [File]' : '[File]';
662
662
  if (item.file_item?.media) {
663
663
  const localPath = await this._downloadMedia(item.file_item.media, item.file_item.aeskey, 'file');
664
664
  if (localPath) {
@@ -668,7 +668,7 @@ export class WeChatIMModule implements IMModule {
668
668
  break;
669
669
 
670
670
  case MessageItemType.VIDEO:
671
- text += text ? ' [视频]' : '[视频]';
671
+ text += text ? ' [Video]' : '[Video]';
672
672
  if (item.video_item?.media) {
673
673
  const localPath = await this._downloadMedia(item.video_item.media, item.video_item.aeskey, 'video.mp4');
674
674
  if (localPath) {
@@ -680,7 +680,7 @@ export class WeChatIMModule implements IMModule {
680
680
  }
681
681
 
682
682
  const preview = text.length > 80 ? text.slice(0, 80) + '...' : text;
683
- console.log(`[WeChat] 私聊 ${userId.slice(0, 12)}...: ${preview}`);
683
+ console.log(`[WeChat] DM ${userId.slice(0, 12)}...: ${preview}`);
684
684
 
685
685
  // 保存 frame 用于被动回复
686
686
  this.pendingFrames.set(chatId, msg);
@@ -699,7 +699,7 @@ export class WeChatIMModule implements IMModule {
699
699
 
700
700
  async reply(chatId: string, text: string, maxLen?: number): Promise<void> {
701
701
  const max = maxLen || TEXT_MAX;
702
- const safe = text.length > max ? text.slice(0, max) + '\n…截断' : text;
702
+ const safe = text.length > max ? text.slice(0, max) + '\n…truncated' : text;
703
703
  await this._sendMessage(chatId, [
704
704
  { type: MessageItemType.TEXT, text_item: { text: safe } },
705
705
  ]);
@@ -724,7 +724,7 @@ export class WeChatIMModule implements IMModule {
724
724
  // 发送 thinking 阶段结束信号
725
725
  await this._sendStreamSignal(state.streamTicket, 'thinking', true);
726
726
  } catch (e: any) {
727
- console.error(`[WeChat] 流式初始化失败,降级为普通回复: ${e.message}`);
727
+ console.error(`[WeChat] Stream init failed, falling back to normal reply: ${e.message}`);
728
728
  await this.reply(chatId, content);
729
729
  return;
730
730
  }
@@ -785,7 +785,7 @@ export class WeChatIMModule implements IMModule {
785
785
  try {
786
786
  await this._sendImageFromSource(chatId, b.url, b.title || 'image.png');
787
787
  } catch (e: any) {
788
- console.error(`[WeChat] 图片发送失败: ${e.message}`);
788
+ console.error(`[WeChat] Image send failed: ${e.message}`);
789
789
  }
790
790
  }
791
791
  break;
@@ -794,7 +794,7 @@ export class WeChatIMModule implements IMModule {
794
794
  try {
795
795
  await this._sendFileFromSource(chatId, b.url, b.title || b.filename || 'file');
796
796
  } catch (e: any) {
797
- console.error(`[WeChat] 文件发送失败: ${e.message}`);
797
+ console.error(`[WeChat] File send failed: ${e.message}`);
798
798
  }
799
799
  }
800
800
  break;
@@ -807,7 +807,7 @@ export class WeChatIMModule implements IMModule {
807
807
  try {
808
808
  await this._sendImageFromSource(chatId, imageKey, this._basename(imageKey));
809
809
  } catch (e: any) {
810
- console.error(`[WeChat] 图片发送失败: ${e.message}`);
810
+ console.error(`[WeChat] Image send failed: ${e.message}`);
811
811
  }
812
812
  }
813
813
 
@@ -815,7 +815,7 @@ export class WeChatIMModule implements IMModule {
815
815
  try {
816
816
  await this._sendFileFromSource(chatId, fileKey, fileName);
817
817
  } catch (e: any) {
818
- console.error(`[WeChat] 文件发送失败: ${e.message}`);
818
+ console.error(`[WeChat] File send failed: ${e.message}`);
819
819
  }
820
820
  }
821
821
 
@@ -839,18 +839,18 @@ export class WeChatIMModule implements IMModule {
839
839
  try {
840
840
  await ilinkPost('ilink/bot/sendmessage', body, this.botToken);
841
841
  } catch (e: any) {
842
- console.error(`[WeChat] 发送失败: ${e.message}`);
842
+ console.error(`[WeChat] Send failed: ${e.message}`);
843
843
  }
844
844
  }
845
845
 
846
- // ── 流式相关 ──
846
+ // ── Streaming ──
847
847
 
848
848
  private async _initStream(): Promise<string> {
849
849
  const result = await ilinkPost('ilink/bot/init_stream', {
850
850
  base_info: { channel_version: CHANNEL_VERSION, bot_agent: 'IMtoAgent' },
851
851
  }, this.botToken);
852
852
  if (result.ret !== 0 || !result.stream_ticket) {
853
- throw new Error(`initStream 失败: ${JSON.stringify(result).slice(0, 200)}`);
853
+ throw new Error(`initStream failed: ${JSON.stringify(result).slice(0, 200)}`);
854
854
  }
855
855
  return result.stream_ticket;
856
856
  }
@@ -886,7 +886,7 @@ export class WeChatIMModule implements IMModule {
886
886
  } else if (media.encrypt_query_param) {
887
887
  downloadUrl = `${CDN_BASE}?${media.encrypt_query_param}`;
888
888
  } else {
889
- console.error('[WeChat] 媒体下载:无可用 URL');
889
+ console.error('[WeChat] Media download: no URL available');
890
890
  return null;
891
891
  }
892
892
 
@@ -916,10 +916,10 @@ export class WeChatIMModule implements IMModule {
916
916
  fs.mkdirSync(MEDIA_DIR, { recursive: true });
917
917
  const filePath = path.join(MEDIA_DIR, `${Date.now()}_${fallbackName}`);
918
918
  fs.writeFileSync(filePath, decrypted);
919
- console.log(`[WeChat] 媒体下载完成: ${filePath}`);
919
+ console.log(`[WeChat] Media downloaded: ${filePath}`);
920
920
  return filePath;
921
921
  } catch (e: any) {
922
- console.error(`[WeChat] 媒体下载失败: ${e.message}`);
922
+ console.error(`[WeChat] Media download failed: ${e.message}`);
923
923
  return null;
924
924
  }
925
925
  }
@@ -945,12 +945,12 @@ export class WeChatIMModule implements IMModule {
945
945
  }, this.botToken);
946
946
 
947
947
  if (uploadResult.ret !== 0) {
948
- throw new Error(`getUploadUrl 失败: ${JSON.stringify(uploadResult).slice(0, 200)}`);
948
+ throw new Error(`getUploadUrl failed: ${JSON.stringify(uploadResult).slice(0, 200)}`);
949
949
  }
950
950
 
951
951
  const uploadUrl = uploadResult.upload_full_url || uploadResult.upload_url;
952
952
  if (!uploadUrl) {
953
- throw new Error('未获取到上传 URL');
953
+ throw new Error('Failed to get upload URL');
954
954
  }
955
955
 
956
956
  // 3. 上传加密文件到 CDN
@@ -960,7 +960,7 @@ export class WeChatIMModule implements IMModule {
960
960
  const encryptQuery = uploadResult.encrypt_query_param || uploadResult.encrypt_query;
961
961
  return { encryptQuery, aesKey, fileKey };
962
962
  } catch (e: any) {
963
- console.error(`[WeChat] 媒体上传失败: ${e.message}`);
963
+ console.error(`[WeChat] Media upload failed: ${e.message}`);
964
964
  return null;
965
965
  }
966
966
  }
@@ -984,12 +984,12 @@ export class WeChatIMModule implements IMModule {
984
984
  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
985
985
  resolve();
986
986
  } else {
987
- reject(new Error(`CDN 上传失败: HTTP ${res.statusCode} ${data.slice(0, 100)}`));
987
+ reject(new Error(`CDN upload failed: HTTP ${res.statusCode} ${data.slice(0, 100)}`));
988
988
  }
989
989
  });
990
990
  });
991
991
  req.on('error', reject);
992
- req.on('timeout', () => { req.destroy(); reject(new Error('CDN 上传超时')); });
992
+ req.on('timeout', () => { req.destroy(); reject(new Error('CDN upload timed out')); });
993
993
  req.write(buffer);
994
994
  req.end();
995
995
  });
@@ -1058,7 +1058,7 @@ export class WeChatIMModule implements IMModule {
1058
1058
  return fs.readFileSync(source);
1059
1059
  }
1060
1060
 
1061
- console.error(`[WeChat] 文件不存在: ${source}`);
1061
+ console.error(`[WeChat] File not found: ${source}`);
1062
1062
  return null;
1063
1063
  }
1064
1064