imtoagent 0.3.3 → 0.3.5

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 +96 -96
  3. package/bin/imtoagent.cjs +1 -1
  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 +171 -163
  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 +39 -28
  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
@@ -68,7 +68,7 @@ export class FileConfigManager implements ConfigManager {
68
68
  const raw = fs.readFileSync(configPath, 'utf-8');
69
69
  this.rawConfig = JSON.parse(raw);
70
70
  } catch (e: any) {
71
- console.error(`[Config] 加载 config.json 失败: ${e.message}`);
71
+ console.error(`[Config] Failed to load config.json: ${e.message}`);
72
72
  this.rawConfig = {} as RawConfig;
73
73
  }
74
74
 
@@ -87,7 +87,7 @@ export class FileConfigManager implements ConfigManager {
87
87
  });
88
88
  }
89
89
  } catch (e: any) {
90
- console.error(`[Config] 加载 providers.json 失败: ${e.message}`);
90
+ console.error(`[Config] Failed to load providers.json: ${e.message}`);
91
91
  }
92
92
 
93
93
  // 加载默认 providers
@@ -130,7 +130,7 @@ export class FileConfigManager implements ConfigManager {
130
130
  this.botConfigs.set(botKey, {});
131
131
  }
132
132
  } catch (e: any) {
133
- console.error(`[Config] 加载 bot ${botKey} 配置失败: ${e.message}`);
133
+ console.error(`[Config] Failed to load bot ${botKey} config: ${e.message}`);
134
134
  this.botConfigs.set(botKey, {});
135
135
  }
136
136
  }
@@ -147,7 +147,7 @@ export class FileConfigManager implements ConfigManager {
147
147
  this.botConfigs.set(botKey, config);
148
148
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
149
149
  } catch (e: any) {
150
- console.error(`[Config] 保存 bot ${botKey} 配置失败: ${e.message}`);
150
+ console.error(`[Config] Failed to save bot ${botKey} config: ${e.message}`);
151
151
  }
152
152
  }
153
153
 
@@ -261,7 +261,7 @@ export class FileConfigManager implements ConfigManager {
261
261
  const configPath = path.join(getDataDir(), 'config.json');
262
262
  fs.writeFileSync(configPath, JSON.stringify(this.rawConfig, null, 2) + '\n');
263
263
  } catch (e: any) {
264
- console.error(`[Config] 保存全局 activeModel 失败: ${e.message}`);
264
+ console.error(`[Config] Failed to save global activeModel: ${e.message}`);
265
265
  }
266
266
  }
267
267
  }
@@ -25,7 +25,7 @@ export class DefaultErrorHandler implements ErrorHandler {
25
25
  const errMsg = error.message || String(error);
26
26
  const backend = ctx.backend;
27
27
 
28
- console.error(`[Error] ${backend} 调用失败 (attempt ${ctx.attempt}): ${errMsg}`);
28
+ console.error(`[Error] ${backend} call failed (attempt ${ctx.attempt}): ${errMsg}`);
29
29
 
30
30
  // 提取 HTTP 状态码
31
31
  const statusCode = this.extractStatusCode(error);
@@ -35,7 +35,7 @@ export class DefaultErrorHandler implements ErrorHandler {
35
35
  if (ctx.attempt < 2) {
36
36
  // 网络超时、5xx → 重试
37
37
  if (isTimeout || statusCode >= 500) {
38
- console.log(`[Error] 将重试 ${backend} 调用 (${ctx.attempt + 1}/2)`);
38
+ console.log(`[Error] Will retry ${backend} call (${ctx.attempt + 1}/2)`);
39
39
  return { type: 'retry', maxAttempts: 2 };
40
40
  }
41
41
 
@@ -43,7 +43,7 @@ export class DefaultErrorHandler implements ErrorHandler {
43
43
  if (statusCode === 429) {
44
44
  const retryAfter = this.extractRetryAfter(error);
45
45
  if (retryAfter > 0) {
46
- console.log(`[Error] 429 限流,等待 ${retryAfter}ms 后重试`);
46
+ console.log(`[Error] 429 rate limited, waiting ${retryAfter}ms before retry`);
47
47
  await this.sleep(retryAfter);
48
48
  }
49
49
  return { type: 'retry', maxAttempts: 2 };
@@ -98,24 +98,24 @@ export class DefaultErrorHandler implements ErrorHandler {
98
98
  const statusCode = this.extractStatusCode(error);
99
99
 
100
100
  if (statusCode === 401 || statusCode === 403) {
101
- return `⚠️ ${backend} 后端认证失败,请联系管理员检查配置。`;
101
+ return `⚠️ ${backend} backend authentication failed. Please ask an admin to check the configuration.`;
102
102
  }
103
103
 
104
104
  if (statusCode === 429) {
105
- return `⚠️ 当前请求过于频繁,请稍后再试。`;
105
+ return `⚠️ Too many requests. Please try again later.`;
106
106
  }
107
107
 
108
108
  if (statusCode >= 500) {
109
- return `⚠️ ${backend} 服务端暂时不可用,请稍后重试。`;
109
+ return `⚠️ ${backend} server is temporarily unavailable. Please try again later.`;
110
110
  }
111
111
 
112
112
  if (this.isTimeoutError(error)) {
113
- return `⚠️ 请求超时,后端可能正在处理复杂任务。请稍后再试。`;
113
+ return `⚠️ Request timed out. The backend may be processing a complex task. Please try again later.`;
114
114
  }
115
115
 
116
116
  // 通用错误
117
117
  const shortMsg = error.message.slice(0, 100);
118
- return `⚠️ 处理消息时遇到错误:${shortMsg}`;
118
+ return `⚠️ Error processing message: ${shortMsg}`;
119
119
  }
120
120
 
121
121
  private sleep(ms: number): Promise<void> {
@@ -56,7 +56,7 @@ function checkRestartSignal(): { triggered: boolean; reason: string } {
56
56
  // 兼容纯文本格式
57
57
  signal = { reason: raw, timestamp: Date.now() };
58
58
  }
59
- return { triggered: true, reason: signal.reason || '未知原因' };
59
+ return { triggered: true, reason: signal.reason || 'Unknown reason' };
60
60
  } catch {
61
61
  return { triggered: false, reason: '' };
62
62
  }
@@ -92,7 +92,7 @@ export class AgentRuntime {
92
92
  */
93
93
  registerAdapter(backend: string, adapter: AgentAdapter): void {
94
94
  this.adapters.set(backend, adapter);
95
- console.log(`[Runtime] 注册适配器: ${backend} → ${adapter.name}`);
95
+ console.log(`[Runtime] Registered adapter: ${backend} → ${adapter.name}`);
96
96
  }
97
97
 
98
98
  /**
@@ -136,14 +136,14 @@ export class AgentRuntime {
136
136
  session.metadata = {};
137
137
  session.startFresh = false;
138
138
  session.running = false;
139
- console.log(`[Runtime] startFresh: 清除 ${ctx.chatId} 旧会话`);
139
+ console.log(`[Runtime] startFresh: cleared old session for ${ctx.chatId}`);
140
140
  }
141
141
 
142
142
  // 3. 重置统计
143
143
  this.config.statsTracker.resetForCall(session);
144
144
 
145
145
  // 4. 发送进度提示
146
- await ctx.sendProgress('💭 思考中...');
146
+ await ctx.sendProgress('💭 Thinking...');
147
147
 
148
148
  session.running = true;
149
149
 
@@ -203,10 +203,10 @@ export class AgentRuntime {
203
203
  const now = Date.now();
204
204
  if (now - lastRestartTime < RESTART_COOLDOWN_MS) {
205
205
  const remaining = Math.ceil((RESTART_COOLDOWN_MS - (now - lastRestartTime)) / 1000);
206
- console.log(`[Runtime] ⏳ Agent 请求重启被忽略(冷却中,${remaining}s 后重试): ${signal.reason}`);
206
+ console.log(`[Runtime] ⏳ Agent restart request ignored (cooldown, retry in ${remaining}s): ${signal.reason}`);
207
207
  } else {
208
208
  lastRestartTime = now;
209
- console.log(`[Runtime] 🔄 Agent 请求重启: ${signal.reason}`);
209
+ console.log(`[Runtime] 🔄 Agent requested restart: ${signal.reason}`);
210
210
  return { restart: true, reason: signal.reason };
211
211
  }
212
212
  }
@@ -214,7 +214,7 @@ export class AgentRuntime {
214
214
  return { restart: false };
215
215
 
216
216
  } catch (error: any) {
217
- console.error(`[Runtime] 处理消息失败 (attempt ${attempt}): ${error.message}`);
217
+ console.error(`[Runtime] Failed to process message (attempt ${attempt}): ${error.message}`);
218
218
 
219
219
  // 7. 错误处理
220
220
  const errorCtx: ErrorContext = {
@@ -226,12 +226,12 @@ export class AgentRuntime {
226
226
  const action = await this.config.errorHandler.handle(ctx.chatId, error, errorCtx);
227
227
 
228
228
  if (action.type === 'retry') {
229
- console.log(`[Runtime] 重试 ${adapter.name} 调用`);
229
+ console.log(`[Runtime] Retrying ${adapter.name} call`);
230
230
  continue;
231
231
  }
232
232
 
233
233
  if (action.type === 'fallback') {
234
- console.log(`[Runtime] 降级到 ${action.adapter}`);
234
+ console.log(`[Runtime] Fallback to ${action.adapter}`);
235
235
  // Phase 2 实现 fallback 逻辑
236
236
  const fallbackAdapter = this.adapters.get(action.adapter);
237
237
  if (fallbackAdapter) {
@@ -277,6 +277,6 @@ export class AgentRuntime {
277
277
  if (adapter?.healthCheck) {
278
278
  return adapter.healthCheck();
279
279
  }
280
- return true; // 默认认为健康
280
+ return true; // Assume healthy by default
281
281
  }
282
282
  }
@@ -139,7 +139,7 @@ export class FileSessionManager implements SessionManager {
139
139
  session = this.createNewSession(chatId, userId);
140
140
  }
141
141
  } catch (e: any) {
142
- console.error(`[Session] 加载 ${chatId} 失败: ${e.message},创建新会话`);
142
+ console.error(`[Session] Failed to load ${chatId}: ${e.message}, creating new session`);
143
143
  session = this.createNewSession(chatId, userId);
144
144
  }
145
145
  } else {
@@ -207,7 +207,7 @@ export class FileSessionManager implements SessionManager {
207
207
  try {
208
208
  fs.writeFileSync(filePath, JSON.stringify(output, null, 2));
209
209
  } catch (e: any) {
210
- console.error(`[Session] 持久化 ${session.chatId} 失败: ${e.message}`);
210
+ console.error(`[Session] Failed to persist ${session.chatId}: ${e.message}`);
211
211
  }
212
212
  }
213
213
 
@@ -225,7 +225,7 @@ export class FileSessionManager implements SessionManager {
225
225
  fs.unlinkSync(filePath);
226
226
  }
227
227
  } catch (e: any) {
228
- console.error(`[Session] 删除 ${chatId} 失败: ${e.message}`);
228
+ console.error(`[Session] Failed to delete ${chatId}: ${e.message}`);
229
229
  }
230
230
  }
231
231
 
@@ -244,7 +244,7 @@ export class FileSessionManager implements SessionManager {
244
244
 
245
245
  for (const chatId of toRemove) {
246
246
  botCache.delete(chatId);
247
- console.log(`[Session] 清理空闲会话: ${chatId}`);
247
+ console.log(`[Session] Cleaning up idle session: ${chatId}`);
248
248
  }
249
249
  }
250
250
 
@@ -1,7 +1,7 @@
1
1
  // ================================================================
2
- // StatsTracker — 调用统计追踪
2
+ // StatsTracker — call statistics tracking
3
3
  // ================================================================
4
- // 统一管理 token/cost/duration 统计,替代各 Agent 中的重复逻辑
4
+ // Unified token/cost/duration stats management across agents
5
5
  // ================================================================
6
6
 
7
7
  import type { Session, StatsTracker, CallStats } from './types';
@@ -12,14 +12,14 @@ import type { Session, StatsTracker, CallStats } from './types';
12
12
 
13
13
  export class DefaultStatsTracker implements StatsTracker {
14
14
  /**
15
- * 重置单次调用的统计(在调用开始时)
15
+ * Reset call stats (at call start)
16
16
  */
17
17
  resetForCall(session: Session): void {
18
18
  session.stats.calls += 1;
19
19
  }
20
20
 
21
21
  /**
22
- * 累加统计(在调用成功后)
22
+ * Accumulate stats (after call success)
23
23
  */
24
24
  accumulate(session: Session, usage: {
25
25
  inputTokens: number;
@@ -39,35 +39,35 @@ export class DefaultStatsTracker implements StatsTracker {
39
39
  }
40
40
 
41
41
  /**
42
- * 生成统计摘要字符串(用于发送给用户)
42
+ * Generate stats summary string (sent to user)
43
43
  */
44
44
  formatSummary(session: Session): string {
45
45
  const s = session.stats;
46
46
  const parts: string[] = [];
47
47
 
48
- // 调用次数
49
- parts.push(`📊 调用 ${s.calls} 次`);
48
+ // Call count
49
+ parts.push(`📊 ${s.calls} calls`);
50
50
 
51
- // Token 用量
51
+ // Token usage
52
52
  const totalTokens = s.totalInputTokens + s.totalOutputTokens;
53
53
  if (totalTokens > 0) {
54
54
  parts.push(`Token ${this.formatTokens(totalTokens)}`);
55
55
  }
56
56
 
57
- // 成本
57
+ // Cost
58
58
  if (s.totalCostUSD > 0) {
59
- parts.push(`费用 $${s.totalCostUSD.toFixed(4)}`);
59
+ parts.push(`Cost $${s.totalCostUSD.toFixed(4)}`);
60
60
  }
61
61
 
62
- // 耗时
62
+ // Duration
63
63
  if (s.totalDurationMs > 0) {
64
- parts.push(`耗时 ${this.formatDuration(s.totalDurationMs)}`);
64
+ parts.push(`Duration ${this.formatDuration(s.totalDurationMs)}`);
65
65
  }
66
66
 
67
67
  return parts.join(' | ');
68
68
  }
69
69
 
70
- /** 格式化 Token 数量 */
70
+ /** Format token count */
71
71
  private formatTokens(count: number): string {
72
72
  if (count >= 1_000_000) {
73
73
  return `${(count / 1_000_000).toFixed(1)}M`;
@@ -78,7 +78,7 @@ export class DefaultStatsTracker implements StatsTracker {
78
78
  return `${count}`;
79
79
  }
80
80
 
81
- /** 格式化时长 */
81
+ /** Format duration */
82
82
  private formatDuration(ms: number): string {
83
83
  const secs = ms / 1000;
84
84
  if (secs >= 3600) {
@@ -81,9 +81,9 @@ export interface MessageAttachment {
81
81
  export function buildAttachmentHint(attachments: MessageAttachment[]): string {
82
82
  return attachments.map((att, i) => {
83
83
  const icon = att.type === 'image' ? '🖼️' : att.type === 'audio' ? '🎵' : '📎';
84
- const typeLabel = att.type === 'image' ? '图片' : att.type === 'audio' ? '语音' : '文件';
84
+ const typeLabel = att.type === 'image' ? 'Image' : att.type === 'audio' ? 'Voice' : 'File';
85
85
  const detail = att.filename ? ` (${att.filename})` : '';
86
- const dur = att.durationMs ? ` [时长: ${Math.round(att.durationMs / 1000)}]` : '';
86
+ const dur = att.durationMs ? ` [Duration: ${Math.round(att.durationMs / 1000)}s]` : '';
87
87
  const mimeInfo = att.mimeType ? ` [${att.mimeType}]` : '';
88
88
 
89
89
  // 优先使用预计算的提示(由 MediaResolver 按文件类型生成)
@@ -91,15 +91,15 @@ export function buildAttachmentHint(attachments: MessageAttachment[]): string {
91
91
  if (att.hint) {
92
92
  hint = `\n> 💡 ${att.hint}`;
93
93
  } else if (att.type === 'image') {
94
- hint = `\n> 💡 图片已保存到本地,路径: \`${att.localPath}\`,格式: ${att.mimeType || '未知'},可使用查看图片工具读取`;
94
+ hint = `\n> 💡 Image saved locally at: \`${att.localPath}\`, format: ${att.mimeType || 'unknown'}, use the image viewer tool to open`;
95
95
  } else if (att.type === 'audio') {
96
- hint = `\n> 💡 语音文件路径: \`${att.localPath}\`,可用语音识别工具处理`;
96
+ hint = `\n> 💡 Voice file path: \`${att.localPath}\`, use speech-to-text tool to process`;
97
97
  } else {
98
- const ext = att.filename ? `,扩展名: ${att.filename.split('.').pop()}` : '';
99
- hint = `\n> 💡 文件路径: \`${att.localPath}\`,可直接读取内容${ext}`;
98
+ const ext = att.filename ? `, extension: ${att.filename.split('.').pop()}` : '';
99
+ hint = `\n> 💡 File path: \`${att.localPath}\`, readable with file reading tool${ext}`;
100
100
  }
101
101
 
102
- return `${icon} [用户消息附带${typeLabel} #${i + 1}]${detail}${mimeInfo}${dur}${hint}`;
102
+ return `${icon} [User message with ${typeLabel} #${i + 1}]${detail}${mimeInfo}${dur}${hint}`;
103
103
  }).join('\n\n');
104
104
  }
105
105