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.
- package/README.md +97 -97
- package/bin/imtoagent-real +96 -96
- package/bin/imtoagent.cjs +1 -1
- package/index.ts +106 -106
- package/modules/agent/claude-adapter.ts +6 -6
- package/modules/agent/claude.ts +6 -6
- package/modules/agent/codex-adapter.ts +13 -13
- package/modules/agent/codex-exec-server.ts +11 -11
- package/modules/agent/codex.ts +29 -29
- package/modules/agent/opencode-adapter.ts +17 -17
- package/modules/agent/opencode.ts +10 -10
- package/modules/capabilities.ts +33 -33
- package/modules/cli/setup.ts +164 -164
- package/modules/core/config.ts +5 -5
- package/modules/core/error.ts +8 -8
- package/modules/core/runtime.ts +10 -10
- package/modules/core/session.ts +4 -4
- package/modules/core/stats.ts +14 -14
- package/modules/core/types.ts +7 -7
- package/modules/im/feishu.ts +56 -56
- package/modules/im/telegram.ts +23 -23
- package/modules/im/wechat.ts +54 -54
- package/modules/im/wecom.ts +50 -50
- package/modules/media/feishu-inbound-adapter.ts +4 -4
- package/modules/media/resolver.ts +11 -11
- package/modules/media/telegram-inbound-adapter.ts +8 -8
- package/modules/prompt-builder.ts +12 -12
- package/modules/proxy/anthropic-proxy.ts +31 -31
- package/modules/proxy/codex-proxy.ts +18 -18
- package/modules/utils/backend-check.ts +12 -12
- package/modules/utils/paths.ts +8 -8
- package/package.json +1 -1
- package/scripts/postinstall.cjs +10 -10
- package/scripts/postinstall.ts +13 -13
- package/templates/soul.template/identity.md +5 -5
- package/templates/soul.template/profile.md +7 -7
- package/templates/soul.template/rules.md +5 -5
- package/templates/soul.template/skills.md +2 -2
- package/templates/soul.template/workspace.md +3 -3
package/modules/im/wecom.ts
CHANGED
|
@@ -86,7 +86,7 @@ async function fetchQRCode(): Promise<{ scode: string; authUrl: string }> {
|
|
|
86
86
|
const raw = await httpsGet(url);
|
|
87
87
|
const resp = JSON.parse(raw);
|
|
88
88
|
if (!resp?.data?.scode || !resp?.data?.auth_url) {
|
|
89
|
-
throw new Error(
|
|
89
|
+
throw new Error(`Failed to get QR code: ${raw.slice(0, 200)}`);
|
|
90
90
|
}
|
|
91
91
|
return { scode: resp.data.scode, authUrl: resp.data.auth_url };
|
|
92
92
|
}
|
|
@@ -110,7 +110,7 @@ async function pollResult(scode: string): Promise<{ botId: string; secret: strin
|
|
|
110
110
|
if (status === 'success') {
|
|
111
111
|
const bi = resp.data.bot_info;
|
|
112
112
|
if (!bi?.botid || !bi?.secret) {
|
|
113
|
-
throw new Error('
|
|
113
|
+
throw new Error('QR scan successful but Bot info not received');
|
|
114
114
|
}
|
|
115
115
|
return { botId: bi.botid, secret: bi.secret };
|
|
116
116
|
}
|
|
@@ -118,7 +118,7 @@ async function pollResult(scode: string): Promise<{ botId: string; secret: strin
|
|
|
118
118
|
await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
console.log('\n⏱
|
|
121
|
+
console.log('\n⏱ QR scan timed out (5 min), please retry');
|
|
122
122
|
process.exit(1);
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -127,17 +127,17 @@ async function pollResult(scode: string): Promise<{ botId: string; secret: strin
|
|
|
127
127
|
* 调用后会在终端显示二维码,用户扫码后自动获取 botId 和 secret 并保存到本地
|
|
128
128
|
*/
|
|
129
129
|
export async function bindWeComQR(): Promise<{ botId: string; secret: string }> {
|
|
130
|
-
console.log('\n📱
|
|
131
|
-
console.log('
|
|
130
|
+
console.log('\n📱 WeCom QR Code Binding');
|
|
131
|
+
console.log('Fetching QR code...');
|
|
132
132
|
|
|
133
133
|
const { scode, authUrl } = await fetchQRCode();
|
|
134
134
|
|
|
135
|
-
console.log('
|
|
135
|
+
console.log('Please scan the following QR code with WeCom:');
|
|
136
136
|
await renderQR(authUrl);
|
|
137
|
-
console.log('
|
|
137
|
+
console.log('Waiting for scan...');
|
|
138
138
|
|
|
139
139
|
const result = await pollResult(scode);
|
|
140
|
-
console.log('\n✅
|
|
140
|
+
console.log('\n✅ QR scan successful! Bot ID and Secret saved');
|
|
141
141
|
|
|
142
142
|
const creds: StoredCreds = {
|
|
143
143
|
botId: result.botId,
|
|
@@ -192,12 +192,12 @@ export class WeComIMModule implements IMModule {
|
|
|
192
192
|
getCapabilities(): IMCapabilities {
|
|
193
193
|
return {
|
|
194
194
|
text: true,
|
|
195
|
-
codeBlock: false, //
|
|
196
|
-
cardMessage: true, //
|
|
195
|
+
codeBlock: false, // WeCom doesn't support code blocks
|
|
196
|
+
cardMessage: true, // Template card messages
|
|
197
197
|
fileSend: true,
|
|
198
198
|
imageSend: true,
|
|
199
199
|
audioSend: false,
|
|
200
|
-
buttonAction: true, //
|
|
200
|
+
buttonAction: true, // Template card button callbacks
|
|
201
201
|
maxTextLength: TEXT_MAX,
|
|
202
202
|
};
|
|
203
203
|
}
|
|
@@ -206,7 +206,7 @@ export class WeComIMModule implements IMModule {
|
|
|
206
206
|
|
|
207
207
|
start(handler: MessageHandler): void {
|
|
208
208
|
if (this.running) {
|
|
209
|
-
console.warn('[WeCom]
|
|
209
|
+
console.warn('[WeCom] Already running');
|
|
210
210
|
return;
|
|
211
211
|
}
|
|
212
212
|
this.handler = handler;
|
|
@@ -221,7 +221,7 @@ export class WeComIMModule implements IMModule {
|
|
|
221
221
|
this.ws = null;
|
|
222
222
|
}
|
|
223
223
|
this.handler = null;
|
|
224
|
-
console.log('[WeCom]
|
|
224
|
+
console.log('[WeCom] Disconnected');
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// ── WebSocket 连接 ──
|
|
@@ -236,18 +236,18 @@ export class WeComIMModule implements IMModule {
|
|
|
236
236
|
if (stored) {
|
|
237
237
|
botId = stored.botId;
|
|
238
238
|
secret = stored.secret;
|
|
239
|
-
console.log('[WeCom]
|
|
239
|
+
console.log('[WeCom] Loaded local credentials');
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
if (!botId || !secret) {
|
|
244
|
-
console.log('[WeCom]
|
|
244
|
+
console.log('[WeCom] No credentials found, starting QR binding...');
|
|
245
245
|
const bound = await bindWeComQR();
|
|
246
246
|
botId = bound.botId;
|
|
247
247
|
secret = bound.secret;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
console.log(`[WeCom]
|
|
250
|
+
console.log(`[WeCom] Connecting WebSocket (bot: ${botId.slice(0, 6)}...)`);
|
|
251
251
|
|
|
252
252
|
// 2. 创建 WSClient
|
|
253
253
|
this.ws = new WSClient({
|
|
@@ -266,23 +266,23 @@ export class WeComIMModule implements IMModule {
|
|
|
266
266
|
|
|
267
267
|
// 3. 事件监听
|
|
268
268
|
this.ws.on('connected', () => {
|
|
269
|
-
console.log('[WeCom] WebSocket
|
|
269
|
+
console.log('[WeCom] WebSocket connected');
|
|
270
270
|
});
|
|
271
271
|
|
|
272
272
|
this.ws.on('authenticated', () => {
|
|
273
|
-
console.log('[WeCom]
|
|
273
|
+
console.log('[WeCom] Authenticated');
|
|
274
274
|
});
|
|
275
275
|
|
|
276
276
|
this.ws.on('disconnected', (reason: string) => {
|
|
277
|
-
console.log(`[WeCom]
|
|
277
|
+
console.log(`[WeCom] Disconnected: ${reason}`);
|
|
278
278
|
if (this.running) {
|
|
279
|
-
console.log('[WeCom]
|
|
279
|
+
console.log('[WeCom] Reconnecting in 5s...');
|
|
280
280
|
setTimeout(() => { if (this.running) this._connect(); }, 5000);
|
|
281
281
|
}
|
|
282
282
|
});
|
|
283
283
|
|
|
284
284
|
this.ws.on('error', (err: any) => {
|
|
285
|
-
console.error(`[WeCom]
|
|
285
|
+
console.error(`[WeCom] Error: ${err?.message || err}`);
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
// 4. 接收消息
|
|
@@ -290,12 +290,12 @@ export class WeComIMModule implements IMModule {
|
|
|
290
290
|
try {
|
|
291
291
|
await this._handleMessage(frame);
|
|
292
292
|
} catch (e: any) {
|
|
293
|
-
console.error(`[WeCom]
|
|
293
|
+
console.error(`[WeCom] Message processing error: ${e.message}`);
|
|
294
294
|
}
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
// ──
|
|
298
|
+
// ── Message Parsing ──
|
|
299
299
|
|
|
300
300
|
private async _handleMessage(frame: any): Promise<void> {
|
|
301
301
|
const body = frame.body || {};
|
|
@@ -309,10 +309,10 @@ export class WeComIMModule implements IMModule {
|
|
|
309
309
|
const items = evt.selected_items?.selected_item ?? [];
|
|
310
310
|
const lines = items.map((it: any) => {
|
|
311
311
|
const ids = it.option_ids?.option_id?.filter(Boolean) ?? [];
|
|
312
|
-
return `- ${it.question_key || '?'}: ${ids.join(', ') || '(
|
|
312
|
+
return `- ${it.question_key || '?'}: ${ids.join(', ') || '(not selected)'}`;
|
|
313
313
|
});
|
|
314
314
|
const text = [
|
|
315
|
-
'[
|
|
315
|
+
'[Template card callback]',
|
|
316
316
|
`card_type: ${evt.card_type || '?'}`,
|
|
317
317
|
`event_key: ${evt.event_key || '?'}`,
|
|
318
318
|
...lines,
|
|
@@ -339,28 +339,28 @@ export class WeComIMModule implements IMModule {
|
|
|
339
339
|
text = body.content?.text || body.text?.content || body.content || '';
|
|
340
340
|
break;
|
|
341
341
|
case 'image':
|
|
342
|
-
text = '[
|
|
342
|
+
text = '[Image]';
|
|
343
343
|
if (body.image?.mediaid) {
|
|
344
344
|
const localPath = await this._downloadMedia(body.image.mediaid, body.image.aeskey, 'image.png');
|
|
345
345
|
attachments.push({ type: 'image', localPath: localPath || '', sourceKey: body.image.mediaid, mimeType: 'image/png' });
|
|
346
346
|
}
|
|
347
347
|
break;
|
|
348
348
|
case 'voice':
|
|
349
|
-
text = body.voice?.recognition || body.recognition || '[
|
|
349
|
+
text = body.voice?.recognition || body.recognition || '[Voice]';
|
|
350
350
|
if (body.voice?.mediaid) {
|
|
351
351
|
const localPath = await this._downloadMedia(body.voice.mediaid, body.voice.aeskey, 'voice.amr');
|
|
352
352
|
attachments.push({ type: 'file', localPath: localPath || '', filename: 'voice.amr', sourceKey: body.voice.mediaid });
|
|
353
353
|
}
|
|
354
354
|
break;
|
|
355
355
|
case 'video':
|
|
356
|
-
text = '[
|
|
356
|
+
text = '[Video]';
|
|
357
357
|
if (body.video?.mediaid) {
|
|
358
358
|
const localPath = await this._downloadMedia(body.video.mediaid, body.video.aeskey, 'video.mp4');
|
|
359
359
|
attachments.push({ type: 'file', localPath: localPath || '', filename: 'video.mp4', sourceKey: body.video.mediaid });
|
|
360
360
|
}
|
|
361
361
|
break;
|
|
362
362
|
case 'file':
|
|
363
|
-
text = `[
|
|
363
|
+
text = `[File: ${body.file?.title || body.title || 'unknown'}]`;
|
|
364
364
|
if (body.file?.mediaid) {
|
|
365
365
|
const localPath = await this._downloadMedia(body.file.mediaid, body.file.aeskey, body.file.title || 'file');
|
|
366
366
|
attachments.push({ type: 'file', localPath: localPath || '', filename: body.file.title || 'file', sourceKey: body.file.mediaid });
|
|
@@ -370,11 +370,11 @@ export class WeComIMModule implements IMModule {
|
|
|
370
370
|
text = body.markdown?.content || '';
|
|
371
371
|
break;
|
|
372
372
|
default:
|
|
373
|
-
text = `[${msgType}
|
|
373
|
+
text = `[${msgType} message]`;
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
const preview = text.length > 80 ? text.slice(0, 80) + '...' : text;
|
|
377
|
-
console.log(`[WeCom] ${chatType === 'group' ? '
|
|
377
|
+
console.log(`[WeCom] ${chatType === 'group' ? 'Group' : 'DM'} ${fromUser}@${chatId}: ${preview}`);
|
|
378
378
|
|
|
379
379
|
// 保存 frame 用于被动回复
|
|
380
380
|
this.pendingFrames.set(chatId, frame);
|
|
@@ -388,10 +388,10 @@ export class WeComIMModule implements IMModule {
|
|
|
388
388
|
|
|
389
389
|
async reply(chatId: string, text: string): Promise<void> {
|
|
390
390
|
if (!this.ws?.isConnected) {
|
|
391
|
-
console.error('[WeCom] WS
|
|
391
|
+
console.error('[WeCom] WS not connected');
|
|
392
392
|
return;
|
|
393
393
|
}
|
|
394
|
-
const safe = text.length > TEXT_MAX ? text.slice(0, TEXT_MAX) + '\n
|
|
394
|
+
const safe = text.length > TEXT_MAX ? text.slice(0, TEXT_MAX) + '\n…truncated' : text;
|
|
395
395
|
const body = {
|
|
396
396
|
msgtype: 'markdown',
|
|
397
397
|
markdown: { content: safe },
|
|
@@ -404,7 +404,7 @@ export class WeComIMModule implements IMModule {
|
|
|
404
404
|
await this.ws.reply(frame, body);
|
|
405
405
|
return;
|
|
406
406
|
} catch (e: any) {
|
|
407
|
-
console.warn(`[WeCom]
|
|
407
|
+
console.warn(`[WeCom] Passive reply failed, falling back to push: ${e.message}`);
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
410
|
|
|
@@ -412,7 +412,7 @@ export class WeComIMModule implements IMModule {
|
|
|
412
412
|
try {
|
|
413
413
|
await this.ws.sendMessage(chatId, body);
|
|
414
414
|
} catch (e: any) {
|
|
415
|
-
console.error(`[WeCom]
|
|
415
|
+
console.error(`[WeCom] Send failed: ${e.message}`);
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
@@ -436,12 +436,12 @@ export class WeComIMModule implements IMModule {
|
|
|
436
436
|
try {
|
|
437
437
|
await this.ws.replyStream(frame, streamId, content, finish);
|
|
438
438
|
} catch (e: any) {
|
|
439
|
-
console.error(`[WeCom]
|
|
439
|
+
console.error(`[WeCom] Stream send failed: ${e.message}`);
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
/**
|
|
444
|
-
*
|
|
444
|
+
* Non-blocking streaming reply
|
|
445
445
|
* 当上一条消息还未收到 ACK 时跳过中间帧,避免慢连接下排队积压
|
|
446
446
|
* finish=true 的最终帧不受限制,始终发送
|
|
447
447
|
*/
|
|
@@ -458,7 +458,7 @@ export class WeComIMModule implements IMModule {
|
|
|
458
458
|
// 静默跳过中间帧(非阻塞保护生效)
|
|
459
459
|
}
|
|
460
460
|
} catch (e: any) {
|
|
461
|
-
console.error(`[WeCom]
|
|
461
|
+
console.error(`[WeCom] Non-blocking stream send failed: ${e.message}`);
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
464
|
|
|
@@ -482,7 +482,7 @@ export class WeComIMModule implements IMModule {
|
|
|
482
482
|
try {
|
|
483
483
|
const mediaId = await this._uploadMediaFromSource(b.url, 'image', b.title || 'image.png');
|
|
484
484
|
if (mediaId) await this.ws!.sendMediaMessage(chatId, 'image', mediaId);
|
|
485
|
-
} catch (e: any) { console.error(`[WeCom]
|
|
485
|
+
} catch (e: any) { console.error(`[WeCom] Image upload failed: ${e.message}`); }
|
|
486
486
|
}
|
|
487
487
|
break;
|
|
488
488
|
case 'file':
|
|
@@ -490,7 +490,7 @@ export class WeComIMModule implements IMModule {
|
|
|
490
490
|
try {
|
|
491
491
|
const mediaId = await this._uploadMediaFromSource(b.url, 'file', b.title || 'file');
|
|
492
492
|
if (mediaId) await this.ws!.sendMediaMessage(chatId, 'file', mediaId);
|
|
493
|
-
} catch (e: any) { console.error(`[WeCom]
|
|
493
|
+
} catch (e: any) { console.error(`[WeCom] File upload failed: ${e.message}`); }
|
|
494
494
|
}
|
|
495
495
|
break;
|
|
496
496
|
}
|
|
@@ -499,19 +499,19 @@ export class WeComIMModule implements IMModule {
|
|
|
499
499
|
}
|
|
500
500
|
|
|
501
501
|
async sendImage(chatId: string, imageKey: string, _alt?: string): Promise<void> {
|
|
502
|
-
if (!this.ws?.isConnected) { console.error('[WeCom] WS
|
|
502
|
+
if (!this.ws?.isConnected) { console.error('[WeCom] WS not connected'); return; }
|
|
503
503
|
try {
|
|
504
504
|
const mediaId = await this._uploadMediaFromSource(imageKey, 'image', this._basename(imageKey));
|
|
505
505
|
if (mediaId) await this.ws.sendMediaMessage(chatId, 'image', mediaId);
|
|
506
|
-
} catch (e: any) { console.error(`[WeCom]
|
|
506
|
+
} catch (e: any) { console.error(`[WeCom] Image send failed: ${e.message}`); }
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
async sendFile(chatId: string, fileKey: string, fileName: string): Promise<void> {
|
|
510
|
-
if (!this.ws?.isConnected) { console.error('[WeCom] WS
|
|
510
|
+
if (!this.ws?.isConnected) { console.error('[WeCom] WS not connected'); return; }
|
|
511
511
|
try {
|
|
512
512
|
const mediaId = await this._uploadMediaFromSource(fileKey, 'file', fileName);
|
|
513
513
|
if (mediaId) await this.ws.sendMediaMessage(chatId, 'file', mediaId);
|
|
514
|
-
} catch (e: any) { console.error(`[WeCom]
|
|
514
|
+
} catch (e: any) { console.error(`[WeCom] File send failed: ${e.message}`); }
|
|
515
515
|
}
|
|
516
516
|
|
|
517
517
|
// ── 媒体上传 ──
|
|
@@ -529,17 +529,17 @@ export class WeComIMModule implements IMModule {
|
|
|
529
529
|
const b64 = source.substring(commaIdx + 1);
|
|
530
530
|
buffer = Buffer.from(b64, 'base64');
|
|
531
531
|
} else {
|
|
532
|
-
//
|
|
532
|
+
// local file path
|
|
533
533
|
if (!fs.existsSync(source)) {
|
|
534
|
-
throw new Error(
|
|
534
|
+
throw new Error(`File not found: ${source}`);
|
|
535
535
|
}
|
|
536
536
|
buffer = fs.readFileSync(source);
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
if (buffer.length === 0) throw new Error('
|
|
539
|
+
if (buffer.length === 0) throw new Error('File is empty');
|
|
540
540
|
|
|
541
541
|
const result = await this.ws!.uploadMedia(buffer, { type: mediaType, filename: fileName });
|
|
542
|
-
console.log(`[WeCom]
|
|
542
|
+
console.log(`[WeCom] Media uploaded: ${fileName} → ${result.media_id}`);
|
|
543
543
|
return result.media_id;
|
|
544
544
|
}
|
|
545
545
|
|
|
@@ -593,10 +593,10 @@ export class WeComIMModule implements IMModule {
|
|
|
593
593
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
594
594
|
const filePath = path.join(tempDir, `${Date.now()}_${finalName}`);
|
|
595
595
|
fs.writeFileSync(filePath, buffer);
|
|
596
|
-
console.log(`[WeCom]
|
|
596
|
+
console.log(`[WeCom] Media downloaded: ${mediaId} → ${filePath}`);
|
|
597
597
|
return filePath;
|
|
598
598
|
} catch (e: any) {
|
|
599
|
-
console.error(`[WeCom]
|
|
599
|
+
console.error(`[WeCom] Media download failed (${mediaId}): ${e.message}`);
|
|
600
600
|
return null;
|
|
601
601
|
}
|
|
602
602
|
}
|
|
@@ -44,7 +44,7 @@ export class FeishuInboundAdapter implements InboundMediaAdapter {
|
|
|
44
44
|
if (!resp.ok) {
|
|
45
45
|
// 容错:文件类型 502 时尝试用 media 类型重试
|
|
46
46
|
if (resp.status === 502 && type === 'file') {
|
|
47
|
-
console.log(`[FeishuInbound] file
|
|
47
|
+
console.log(`[FeishuInbound] file type returned 502, retrying with media type`);
|
|
48
48
|
const retryUrl = `https://open.feishu.cn/open-apis/im/v1/messages/${messageId}/resources/${resourceKey}?type=media`;
|
|
49
49
|
const retryResp = await fetch(retryUrl, {
|
|
50
50
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -60,7 +60,7 @@ export class FeishuInboundAdapter implements InboundMediaAdapter {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
console.error(`[FeishuInbound]
|
|
63
|
+
console.error(`[FeishuInbound] download message resource failed: HTTP ${resp.status} (key=${resourceKey})`);
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -74,7 +74,7 @@ export class FeishuInboundAdapter implements InboundMediaAdapter {
|
|
|
74
74
|
sourceKey: resourceKey,
|
|
75
75
|
};
|
|
76
76
|
} catch (e: any) {
|
|
77
|
-
console.error(`[FeishuInbound]
|
|
77
|
+
console.error(`[FeishuInbound] download message resource exception: ${e.message}`);
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -97,7 +97,7 @@ export class FeishuInboundAdapter implements InboundMediaAdapter {
|
|
|
97
97
|
|
|
98
98
|
const data = await resp.json();
|
|
99
99
|
if (data.code !== 0 || !data.tenant_access_token) {
|
|
100
|
-
throw new Error(
|
|
100
|
+
throw new Error(`Failed to get Feishu tenant_access_token: ${JSON.stringify(data)}`);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
this._appToken = data.tenant_access_token;
|
|
@@ -50,31 +50,31 @@ function buildHintForCategory(entry: MediaEntry): string {
|
|
|
50
50
|
|
|
51
51
|
switch (category) {
|
|
52
52
|
case 'image':
|
|
53
|
-
return
|
|
53
|
+
return `Image saved locally, path: \`${localPath}\`, format: ${mimeType}, can be opened with an image viewer`;
|
|
54
54
|
|
|
55
55
|
case 'audio':
|
|
56
|
-
return
|
|
56
|
+
return `Audio file path: \`${localPath}\`, format: ${mimeType}, can be processed with speech recognition tools`;
|
|
57
57
|
|
|
58
58
|
case 'video':
|
|
59
|
-
return
|
|
59
|
+
return `Video file path: \`${localPath}\`, format: ${mimeType}`;
|
|
60
60
|
|
|
61
61
|
case 'document':
|
|
62
|
-
return
|
|
62
|
+
return `Document file path: \`${localPath}\`, type: ${mimeType}, can be read directly (if text/PDF) or processed with appropriate tools`;
|
|
63
63
|
|
|
64
64
|
case 'text':
|
|
65
|
-
return
|
|
65
|
+
return `Text file path: \`${localPath}\`, can be read directly with a file reading tool`;
|
|
66
66
|
|
|
67
67
|
case 'spreadsheet':
|
|
68
|
-
return
|
|
68
|
+
return `Spreadsheet file path: \`${localPath}\`, type: ${mimeType}, can be processed with spreadsheet tools (e.g. Python pandas/openpyxl)`;
|
|
69
69
|
|
|
70
70
|
case 'presentation':
|
|
71
|
-
return
|
|
71
|
+
return `Presentation file path: \`${localPath}\`, type: ${mimeType}`;
|
|
72
72
|
|
|
73
73
|
case 'archive':
|
|
74
|
-
return
|
|
74
|
+
return `Archive file path: \`${localPath}\`, type: ${mimeType}, needs to be extracted before processing`;
|
|
75
75
|
|
|
76
76
|
default:
|
|
77
|
-
return
|
|
77
|
+
return `File path: \`${localPath}\`, format: ${mimeType}, can be analyzed with file tools or read directly`;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -109,7 +109,7 @@ export class InboundMediaResolver {
|
|
|
109
109
|
);
|
|
110
110
|
|
|
111
111
|
if (!downloaded) {
|
|
112
|
-
console.log(`[${this.adapter.platform}]
|
|
112
|
+
console.log(`[${this.adapter.platform}] Failed to download resource: ${request.resourceKey}`);
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -126,7 +126,7 @@ export class InboundMediaResolver {
|
|
|
126
126
|
|
|
127
127
|
return { attachment, entry };
|
|
128
128
|
} catch (e: any) {
|
|
129
|
-
console.error(`[${this.adapter.platform}]
|
|
129
|
+
console.error(`[${this.adapter.platform}] Media resolution error: ${e.message}`);
|
|
130
130
|
return null;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
@@ -40,12 +40,12 @@ export class TelegramInboundAdapter implements InboundMediaAdapter {
|
|
|
40
40
|
const ProxyAgent = (globalThis as any).ProxyAgent;
|
|
41
41
|
if (ProxyAgent) {
|
|
42
42
|
this.dispatcher = new ProxyAgent(this.proxy);
|
|
43
|
-
console.log(`[TelegramInbound]
|
|
43
|
+
console.log(`[TelegramInbound] Proxy dispatcher configured: ${this.proxy}`);
|
|
44
44
|
} else {
|
|
45
|
-
console.log(`[TelegramInbound] ⚠️
|
|
45
|
+
console.log(`[TelegramInbound] ⚠️ Proxy configured but ProxyAgent unavailable, will try direct connection`);
|
|
46
46
|
}
|
|
47
47
|
} catch (e: any) {
|
|
48
|
-
console.log(`[TelegramInbound] ⚠️
|
|
48
|
+
console.log(`[TelegramInbound] ⚠️ Proxy dispatcher init failed: ${e.message}`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -65,13 +65,13 @@ export class TelegramInboundAdapter implements InboundMediaAdapter {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
if (!fileResp.ok) {
|
|
68
|
-
console.error(`[TelegramInbound] getFile
|
|
68
|
+
console.error(`[TelegramInbound] getFile failed: HTTP ${fileResp.status} (file_id=${resourceKey.slice(-10)})`);
|
|
69
69
|
return null;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const fileData = await fileResp.json();
|
|
73
73
|
if (!fileData.ok || !fileData.result?.file_path) {
|
|
74
|
-
console.error(`[TelegramInbound] getFile
|
|
74
|
+
console.error(`[TelegramInbound] getFile returned invalid: ${JSON.stringify(fileData).slice(0, 200)}`);
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -79,7 +79,7 @@ export class TelegramInboundAdapter implements InboundMediaAdapter {
|
|
|
79
79
|
const fileSize = fileData.result.file_size;
|
|
80
80
|
|
|
81
81
|
if (fileSize && fileSize > 20 * 1024 * 1024) {
|
|
82
|
-
console.log(`[TelegramInbound]
|
|
82
|
+
console.log(`[TelegramInbound] File too large (${fileSize} bytes), skipping download`);
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -88,7 +88,7 @@ export class TelegramInboundAdapter implements InboundMediaAdapter {
|
|
|
88
88
|
const contentResp = await this._fetch(downloadUrl);
|
|
89
89
|
|
|
90
90
|
if (!contentResp.ok) {
|
|
91
|
-
console.error(`[TelegramInbound]
|
|
91
|
+
console.error(`[TelegramInbound] Download failed: HTTP ${contentResp.status} (path=${filePath})`);
|
|
92
92
|
return null;
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -105,7 +105,7 @@ export class TelegramInboundAdapter implements InboundMediaAdapter {
|
|
|
105
105
|
sourceKey: resourceKey,
|
|
106
106
|
};
|
|
107
107
|
} catch (e: any) {
|
|
108
|
-
console.error(`[TelegramInbound]
|
|
108
|
+
console.error(`[TelegramInbound] download resource exception: ${e.message}`);
|
|
109
109
|
return null;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -88,32 +88,32 @@ export function buildSystemPrompt(ctx: PromptBuilderContext): string {
|
|
|
88
88
|
// 2. IM 能力
|
|
89
89
|
const caps = ctx.imModule?.getCapabilities() ?? ctx.caps ?? DEFAULT_TERMINAL_CAPS;
|
|
90
90
|
const capSection = buildCapabilityPrompt(caps);
|
|
91
|
-
sections.push('#
|
|
91
|
+
sections.push('# Current IM Capabilities\n\n' + capSection);
|
|
92
92
|
|
|
93
|
-
// 3.
|
|
94
|
-
sections.push(`#
|
|
93
|
+
// 3. Gateway logs (Agent can proactively query)
|
|
94
|
+
sections.push(`# Gateway Runtime Logs
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
Gateway runtime logs: ~/.imtoagent/logs/imtoagent.log
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
- \`tail -n 30 ~/.imtoagent/logs/imtoagent.log\` —
|
|
100
|
-
- \`grep -i "restart\|reload\|shutdown\|SIGTERM" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` —
|
|
101
|
-
- \`grep -i "error\|fail\|crash" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` —
|
|
102
|
-
- \`grep -i "online\|connected\|disconnected" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` — Bot
|
|
98
|
+
You can check logs to understand gateway status, troubleshoot issues, and detect restart events:
|
|
99
|
+
- \`tail -n 30 ~/.imtoagent/logs/imtoagent.log\` — Last 30 lines
|
|
100
|
+
- \`grep -i "restart\|reload\|shutdown\|SIGTERM" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` — Restart/shutdown records
|
|
101
|
+
- \`grep -i "error\|fail\|crash" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` — Error records
|
|
102
|
+
- \`grep -i "online\|connected\|disconnected" ~/.imtoagent/logs/imtoagent.log | tail -n 10\` — Bot connection status
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
Note: Your first message after startup may have lost conversation memory (if the gateway restarted). Check logs first to understand the context.`);
|
|
105
105
|
|
|
106
106
|
// 4. Soul
|
|
107
107
|
const soul = loadSoul(ctx.botName);
|
|
108
108
|
if (soul) {
|
|
109
|
-
sections.push('#
|
|
109
|
+
sections.push('# User-Defined Instructions (IMtoAgent Soul)\n\n' + soul);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return sections.join('\n\n---\n\n');
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// ================================================================
|
|
116
|
-
//
|
|
116
|
+
// Convenience: resolve capabilities directly (eliminate inline fallbacks)
|
|
117
117
|
// ================================================================
|
|
118
118
|
export function resolveCapabilities(
|
|
119
119
|
imModule?: { getCapabilities(): IMCapabilities } | null,
|