imtoagent 0.3.4 → 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 +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
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env bun
2
2
  // ================================================================
3
- // imtoagent CLI — 全局命令入口
3
+ // imtoagent CLI — global command entry point
4
4
  // ================================================================
5
- // npm install -g imtoagent 后可用:
6
- // imtoagent setup — 交互式配置向导
7
- // imtoagent start — 后台启动网关
8
- // imtoagent stop — 停止网关
9
- // imtoagent status — 查看运行状态
10
- // imtoagent restore — 热重载恢复
11
- // imtoagent daemon — 前台守护模式(自动重启 + 日志)
5
+ // Available after npm install -g imtoagent:
6
+ // imtoagent setup — interactive setup wizard
7
+ // imtoagent start — start gateway in background
8
+ // imtoagent stop — stop gateway
9
+ // imtoagent status — check running status
10
+ // imtoagent restore — hot reload
11
+ // imtoagent daemon — foreground daemon (auto-restart + logs)
12
12
  // ================================================================
13
13
 
14
14
  import * as fs from 'fs';
@@ -18,7 +18,7 @@ import { getDataDir } from '../modules/utils/paths';
18
18
  const PID_FILE = '/tmp/imtoagent.pid';
19
19
 
20
20
  // ================================================================
21
- // 命令分发
21
+ // Command dispatch
22
22
  // ================================================================
23
23
  const command = process.argv[2];
24
24
 
@@ -42,19 +42,19 @@ switch (command) {
42
42
  await cmdDaemon();
43
43
  break;
44
44
  case undefined: {
45
- // 无命令没完成 setup 自动进向导,已完成显示帮助
45
+ // No command auto-enter setup if not configured, show help otherwise
46
46
  const dataDir = getDataDir();
47
47
  const configPath = path.join(dataDir, 'config.json');
48
48
  let needsSetup = !fs.existsSync(configPath);
49
49
  if (!needsSetup) {
50
50
  try {
51
51
  const raw = fs.readFileSync(configPath, 'utf-8');
52
- // 如果配置里还有 YOUR_ 占位符,说明还没完成 setup
52
+ // If config still has YOUR_ placeholders, setup is incomplete
53
53
  needsSetup = /YOUR_[A-Z_]+/.test(raw);
54
54
  } catch { needsSetup = true; }
55
55
  }
56
56
  if (needsSetup) {
57
- console.log('👋 欢迎使用 imtoagent,请先完成初始配置\n');
57
+ console.log('👋 Welcome to imtoagent! Please run setup first.\n');
58
58
  await cmdSetup();
59
59
  } else {
60
60
  printHelp();
@@ -67,7 +67,7 @@ switch (command) {
67
67
  printHelp();
68
68
  break;
69
69
  default:
70
- console.error(`❌ 未知命令: ${command}`);
70
+ console.error(`❌ Unknown command: ${command}`);
71
71
  printHelp();
72
72
  process.exit(1);
73
73
  }
@@ -77,22 +77,22 @@ switch (command) {
77
77
  // ================================================================
78
78
  function printHelp() {
79
79
  console.log(`
80
- imtoagent — IM ↔ Agent 统一网关
80
+ imtoagent — IM ↔ Agent Unified Gateway
81
81
 
82
- 用法:
83
- imtoagent setup 交互式配置向导
84
- imtoagent start 后台启动网关
85
- imtoagent stop 停止网关
86
- imtoagent status 查看运行状态
87
- imtoagent restore 热重载恢复
88
- imtoagent daemon 前台守护模式(自动重启 + 日志,适合 launchd/systemd 托管)
82
+ Usage:
83
+ imtoagent setup Interactive setup wizard
84
+ imtoagent start Start gateway in background
85
+ imtoagent stop Stop gateway
86
+ imtoagent status Check running status
87
+ imtoagent restore Hot reload
88
+ imtoagent daemon Foreground daemon (auto-restart + logs, for launchd/systemd)
89
89
 
90
- 数据目录: ${getDataDir()}
90
+ Data directory: ${getDataDir()}
91
91
  `);
92
92
  }
93
93
 
94
94
  // ================================================================
95
- // setup — 交互式向导
95
+ // setup — interactive wizard
96
96
  // ================================================================
97
97
  async function cmdSetup() {
98
98
  const { runSetupWizard } = await import('../modules/cli/setup');
@@ -100,32 +100,32 @@ async function cmdSetup() {
100
100
  }
101
101
 
102
102
  // ================================================================
103
- // start — 后台启动
103
+ // start — launch in background
104
104
  // ================================================================
105
105
  async function cmdStart() {
106
- // 检查是否已在运行
106
+ // Check if already running
107
107
  if (fs.existsSync(PID_FILE)) {
108
108
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
109
109
  try {
110
110
  process.kill(pid, 0);
111
- console.error(`❌ 网关已在运行 (PID=${pid})`);
112
- console.error(` 运行 "imtoagent stop" 先停止`);
111
+ console.error(`❌ Gateway already running (PID=${pid})`);
112
+ console.error(` Run "imtoagent stop" to stop first`);
113
113
  process.exit(1);
114
114
  } catch {
115
- // PID 文件残留,清理
115
+ // Stale PID file, clean up
116
116
  fs.unlinkSync(PID_FILE);
117
117
  }
118
118
  }
119
119
 
120
- // 检查配置是否存在
120
+ // Check if config exists
121
121
  const dataDir = getDataDir();
122
122
  const configPath = path.join(dataDir, 'config.json');
123
123
  if (!fs.existsSync(configPath)) {
124
- console.error('❌ 未找到配置文件,请先运行 "imtoagent setup"');
124
+ console.error('❌ No config file found. Please run "imtoagent setup" first');
125
125
  process.exit(1);
126
126
  }
127
127
 
128
- // 检查配置的后端是否已安装
128
+ // Check if configured backends are installed
129
129
  try {
130
130
  const { checkBackend } = await import('../modules/utils/backend-check');
131
131
  const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
@@ -139,22 +139,22 @@ async function cmdStart() {
139
139
  }
140
140
  }
141
141
  if (missingBackends.length > 0) {
142
- console.error(`\n⚠️ 以下后端已配置但未安装,网关启动后发消息会报错:`);
142
+ console.error(`\n⚠️ The following backends are configured but not installed, messages will fail after gateway starts:`);
143
143
  for (const b of missingBackends) {
144
144
  console.error(` ❌ ${b}`);
145
145
  }
146
- console.error(`\n请先安装缺失的后端,或运行 "imtoagent setup" 修改配置。\n`);
147
- // 不强制退出,允许用户先启动网关再慢慢装后端
146
+ console.error(`\nPlease install the missing backends, or run "imtoagent setup" to reconfigure.\n`);
147
+ // Don't force exit, let user start gateway and install backends later
148
148
  }
149
149
  } catch {
150
- // 检查失败不影响启动
150
+ // Check failure doesn't block startup
151
151
  }
152
152
 
153
- console.log('🚀 启动 imtoagent 网关...');
154
- console.log(` 数据目录: ${dataDir}`);
155
- console.log(` 配置文件: ${configPath}`);
153
+ console.log('🚀 Starting imtoagent gateway...');
154
+ console.log(` Data directory: ${dataDir}`);
155
+ console.log(` Config file: ${configPath}`);
156
156
 
157
- // 使用 Bun.spawn 后台启动
157
+ // Launch in background using Bun.spawn
158
158
  const pkgDir = path.resolve(import.meta.dirname, '..');
159
159
  const indexFile = path.join(pkgDir, 'index.ts');
160
160
 
@@ -166,14 +166,14 @@ async function cmdStart() {
166
166
  });
167
167
 
168
168
  fs.writeFileSync(PID_FILE, String(child.pid));
169
- console.log(`✅ 网关已启动 (PID=${child.pid})`);
169
+ console.log(`✅ Gateway started (PID=${child.pid})`);
170
170
 
171
- // 后台日志重定向到 logs/
171
+ // Redirect background logs to logs/
172
172
  const logsDir = path.join(dataDir, 'logs');
173
173
  if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
174
174
  const logFile = path.join(logsDir, 'imtoagent.log');
175
175
 
176
- // 启动日志收集
176
+ // Start log collection
177
177
  (async () => {
178
178
  const logStream = fs.createWriteStream(logFile, { flags: 'a' });
179
179
  for await (const chunk of child.stdout as any) {
@@ -192,13 +192,13 @@ async function cmdStart() {
192
192
  }
193
193
  })().catch(() => {});
194
194
 
195
- // 等待启动验证(5 秒内检查 PID 是否存活)
195
+ // Wait for startup verification (check PID survives 5s)
196
196
  await new Promise(r => setTimeout(r, 3000));
197
197
  try {
198
198
  process.kill(child.pid, 0);
199
- console.log('✅ 网关运行正常');
199
+ console.log('✅ Gateway is running');
200
200
  } catch {
201
- console.error('❌ 网关启动失败,查看日志:');
201
+ console.error('❌ Gateway failed to start, check logs:');
202
202
  if (fs.existsSync(logFile)) {
203
203
  console.log(fs.readFileSync(logFile, 'utf-8').slice(-2000));
204
204
  }
@@ -208,21 +208,21 @@ async function cmdStart() {
208
208
  }
209
209
 
210
210
  // ================================================================
211
- // stop — 停止
211
+ // stop — stop gateway
212
212
  // ================================================================
213
213
  async function cmdStop() {
214
214
  if (!fs.existsSync(PID_FILE)) {
215
- console.log('ℹ️ 网关未运行');
215
+ console.log('ℹ️ Gateway is not running');
216
216
  return;
217
217
  }
218
218
 
219
219
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
220
220
  try {
221
221
  process.kill(pid, 0);
222
- console.log(`⏹ 正在停止网关 (PID=${pid})...`);
222
+ console.log(`⏹ Stopping gateway (PID=${pid})...`);
223
223
  process.kill(pid, 'SIGTERM');
224
224
 
225
- // 等待退出
225
+ // Wait for process to exit
226
226
  for (let i = 0; i < 20; i++) {
227
227
  try {
228
228
  process.kill(pid, 0);
@@ -232,108 +232,108 @@ async function cmdStop() {
232
232
  }
233
233
  }
234
234
 
235
- // 检查是否还在
235
+ // Check if still running
236
236
  try {
237
237
  process.kill(pid, 0);
238
- console.log('⚠️ 进程未响应,强制终止...');
238
+ console.log('⚠️ Process not responding, force killing...');
239
239
  process.kill(pid, 'SIGKILL');
240
240
  } catch {
241
- console.log('✅ 网关已停止');
241
+ console.log('✅ Gateway stopped');
242
242
  }
243
243
  } catch {
244
- console.log('ℹ️ 网关未运行(PID 文件残留,已清理)');
244
+ console.log('ℹ️ Gateway not running (stale PID file cleaned up)');
245
245
  }
246
246
 
247
247
  try { fs.unlinkSync(PID_FILE); } catch {}
248
248
  }
249
249
 
250
250
  // ================================================================
251
- // status — 状态
251
+ // status — status check
252
252
  // ================================================================
253
253
  async function cmdStatus() {
254
254
  const dataDir = getDataDir();
255
255
 
256
- console.log(`\n📊 imtoagent 状态`);
257
- console.log(` 数据目录: ${dataDir}`);
256
+ console.log(`\n📊 imtoagent Status`);
257
+ console.log(` Data directory: ${dataDir}`);
258
258
 
259
- // 进程状态
259
+ // Process status
260
260
  if (fs.existsSync(PID_FILE)) {
261
261
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
262
262
  try {
263
263
  process.kill(pid, 0);
264
- console.log(` 进程:运行中 (PID=${pid})`);
264
+ console.log(` Process:Running (PID=${pid})`);
265
265
  } catch {
266
- console.log(` 进程:已停止 (PID=${pid} 不存在)`);
266
+ console.log(` Process:Stopped (PID=${pid} does not exist)`);
267
267
  }
268
268
  } else {
269
- console.log(` 进程:未运行`);
269
+ console.log(` Process:Not running`);
270
270
  }
271
271
 
272
- // 配置文件
272
+ // Config file
273
273
  const configPath = path.join(dataDir, 'config.json');
274
274
  if (fs.existsSync(configPath)) {
275
275
  try {
276
276
  const cfg = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
277
277
  const bots = cfg.bots || [];
278
- console.log(` 配置:已配置 (${bots.length} Bot)`);
278
+ console.log(` Config:Configured (${bots.length} Bot(s))`);
279
279
  for (const bot of bots) {
280
280
  console.log(` - ${bot.name} (${bot.backend})`);
281
281
  }
282
282
  } catch {
283
- console.log(` 配置:解析失败`);
283
+ console.log(` Config:Parse error`);
284
284
  }
285
285
  } else {
286
- console.log(` 配置:未找到 (运行 "imtoagent setup")`);
286
+ console.log(` Config:Not found (run "imtoagent setup")`);
287
287
  }
288
288
 
289
- // 日志
289
+ // Log file
290
290
  const logFile = path.join(dataDir, 'logs', 'imtoagent.log');
291
291
  if (fs.existsSync(logFile)) {
292
292
  const stats = fs.statSync(logFile);
293
293
  const size = stats.size > 1024 * 1024
294
294
  ? (stats.size / (1024 * 1024)).toFixed(1) + ' MB'
295
295
  : (stats.size / 1024).toFixed(1) + ' KB';
296
- console.log(` 日志: ${size} (${logFile})`);
296
+ console.log(` Log: ${size} (${logFile})`);
297
297
  }
298
298
 
299
299
  console.log();
300
300
  }
301
301
 
302
302
  // ================================================================
303
- // restore — 热重载
303
+ // restore — hot reload
304
304
  // ================================================================
305
305
  async function cmdRestore() {
306
306
  if (!fs.existsSync(PID_FILE)) {
307
- console.error('❌ 网关未运行,无法热重载');
307
+ console.error('❌ Gateway is not running, cannot hot reload');
308
308
  process.exit(1);
309
309
  }
310
310
 
311
311
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
312
- console.log(`🔄 发送 SIGHUP 到网关 (PID=${pid})...`);
312
+ console.log(`🔄 Sending SIGHUP to gateway (PID=${pid})...`);
313
313
  try {
314
314
  process.kill(pid, 'SIGHUP');
315
- console.log('✅ 热重载信号已发送');
315
+ console.log('✅ Hot reload signal sent');
316
316
  } catch (e: any) {
317
- console.error(`❌ 发送失败: ${e.message}`);
317
+ console.error(`❌ Failed to send: ${e.message}`);
318
318
  process.exit(1);
319
319
  }
320
320
  }
321
321
 
322
322
  // ================================================================
323
- // daemon — 前台守护模式(自动重启 + 日志 + 优雅退出)
323
+ // daemon — foreground daemon mode (auto-restart + logs + graceful shutdown)
324
324
  // ================================================================
325
- // 设计用途:
326
- // - 前台运行,被 launchd / systemd 等进程管理器托管
327
- // - 崩溃时自动重启(指数退避,最长 30s
328
- // - 收到 SIGTERM/SIGINT 时优雅关闭,不重启
329
- // - 日志写入 ~/.imtoagent/logs/imtoagent.log
325
+ // Design:
326
+ // - Runs in foreground, managed by launchd / systemd etc.
327
+ // - Auto-restarts on crash (exponential backoff, max 30s)
328
+ // - Graceful shutdown on SIGTERM/SIGINT, no restart
329
+ // - Logs written to ~/.imtoagent/logs/imtoagent.log
330
330
  // ================================================================
331
331
  async function cmdDaemon(): Promise<void> {
332
332
  const dataDir = getDataDir();
333
333
  const configPath = path.join(dataDir, 'config.json');
334
334
 
335
335
  if (!fs.existsSync(configPath)) {
336
- console.error('❌ 未找到配置文件,请先运行 "imtoagent setup"');
336
+ console.error('❌ No config file found. Please run "imtoagent setup" first');
337
337
  process.exit(1);
338
338
  }
339
339
 
@@ -344,33 +344,33 @@ async function cmdDaemon(): Promise<void> {
344
344
  const pkgDir = path.resolve(import.meta.dirname, '..');
345
345
  const indexFile = path.join(pkgDir, 'index.ts');
346
346
 
347
- console.log(`🛡 imtoagent 守护模式`);
348
- console.log(` 数据目录: ${dataDir}`);
349
- console.log(` 日志文件: ${logFile}`);
350
- console.log(` Ctrl+C 停止\n`);
347
+ console.log(`🛡 imtoagent Daemon Mode`);
348
+ console.log(` Data directory: ${dataDir}`);
349
+ console.log(` Log file: ${logFile}`);
350
+ console.log(` Press Ctrl+C to stop\n`);
351
351
 
352
- // 优雅退出标记
352
+ // Graceful shutdown flag
353
353
  let shuttingDown = false;
354
354
 
355
355
  const shutdown = () => {
356
356
  if (shuttingDown) return;
357
357
  shuttingDown = true;
358
- console.log('\n🛑 收到停止信号,正在关闭...');
358
+ console.log('\n🛑 Received stop signal, shutting down...');
359
359
  };
360
360
 
361
361
  process.on('SIGTERM', shutdown);
362
362
  process.on('SIGINT', shutdown);
363
363
 
364
364
  let retryDelay = 0;
365
- const MAX_RETRY_DELAY = 30_000; // 30s 上限
365
+ const MAX_RETRY_DELAY = 30_000; // 30s cap
366
366
 
367
367
  while (!shuttingDown) {
368
- // 首次无延迟,之后指数退避
368
+ // No delay on first run, exponential backoff after
369
369
  if (retryDelay > 0) {
370
- console.log(` 等待 ${retryDelay / 1000}s 后重启...`);
370
+ console.log(` Waiting ${retryDelay / 1000}s before restart...`);
371
371
  await new Promise<void>(resolve => {
372
372
  const timer = setTimeout(resolve, retryDelay);
373
- // 等待期间如果收到停止信号,立即退出
373
+ // Exit immediately if shutdown signal received during wait
374
374
  const check = setInterval(() => {
375
375
  if (shuttingDown) {
376
376
  clearTimeout(timer);
@@ -393,9 +393,9 @@ async function cmdDaemon(): Promise<void> {
393
393
 
394
394
  const childPid = child.pid;
395
395
  fs.writeFileSync(PID_FILE, String(childPid));
396
- console.log(`[${new Date().toISOString()}] 🚀 启动网关 (PID=${childPid})`);
396
+ console.log(`[${new Date().toISOString()}] 🚀 Starting gateway (PID=${childPid})`);
397
397
 
398
- // 日志收集
398
+ // Log collection
399
399
  const pumpStdout = (async () => {
400
400
  for await (const chunk of child.stdout as any) {
401
401
  const line = new TextDecoder().decode(chunk);
@@ -412,7 +412,7 @@ async function cmdDaemon(): Promise<void> {
412
412
  }
413
413
  })().catch(() => {});
414
414
 
415
- // 等待子进程退出
415
+ // Wait for child process to exit
416
416
  const exitCode = await child.exited;
417
417
  await Promise.allSettled([pumpStdout, pumpStderr]);
418
418
  logStream.end();
@@ -421,17 +421,17 @@ async function cmdDaemon(): Promise<void> {
421
421
 
422
422
  if (shuttingDown) break;
423
423
 
424
- // 判断是否需要重启
424
+ // Determine if restart needed
425
425
  if (exitCode === 0) {
426
- console.log(`[${new Date().toISOString()}] ⏹ 网关正常退出 (code=0),不重启`);
426
+ console.log(`[${new Date().toISOString()}] ⏹ Gateway exited cleanly (code=0), not restarting`);
427
427
  break;
428
428
  }
429
429
 
430
- // 崩溃指数退避重启
430
+ // Crashexponential backoff restart
431
431
  retryDelay = retryDelay === 0 ? 3_000 : Math.min(retryDelay * 2, MAX_RETRY_DELAY);
432
- console.log(`[${new Date().toISOString()}] ⚠️ 网关异常退出 (code=${exitCode}),${retryDelay / 1000}s 后重启`);
432
+ console.log(`[${new Date().toISOString()}] ⚠️ Gateway crashed (code=${exitCode}), restarting in ${retryDelay / 1000}s`);
433
433
  }
434
434
 
435
- console.log('👋 守护进程已停止');
435
+ console.log('👋 Daemon stopped');
436
436
  }
437
437
 
package/bin/imtoagent.cjs CHANGED
@@ -25,7 +25,7 @@ for (var i = 0; i < candidates.length; i++) {
25
25
  }
26
26
 
27
27
  if (!bunPath) {
28
- console.error("❌ bun 未找到,请先安装: https://bun.sh");
28
+ console.error("❌ bun not found, please install: https://bun.sh");
29
29
  console.error(" curl -fsSL https://bun.sh/install | bash");
30
30
  process.exit(1);
31
31
  }