morpheus-cli 0.9.10 → 0.9.12
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/dist/channels/telegram.js +234 -144
- package/dist/http/api.js +1 -0
- package/dist/http/routers/link.js +25 -4
- package/dist/runtime/link-worker.js +49 -0
- package/dist/runtime/memory/sqlite.js +33 -5
- package/dist/runtime/oracle.js +7 -0
- package/dist/runtime/webhooks/dispatcher.js +24 -1
- package/dist/ui/assets/{AuditDashboard-ZPJ5Pfwi.js → AuditDashboard-CM1YN1uk.js} +1 -1
- package/dist/ui/assets/Chat-D4y-g6Tw.js +41 -0
- package/dist/ui/assets/{Chronos-buCS0uuh.js → Chronos-D1yAb4M5.js} +1 -1
- package/dist/ui/assets/{ConfirmationModal-Bud3Ur7D.js → ConfirmationModal-DxUHZgTy.js} +1 -1
- package/dist/ui/assets/{Dashboard-CR6pBg6o.js → Dashboard-BzxmcHaS.js} +1 -1
- package/dist/ui/assets/{DeleteConfirmationModal-Cz1jfXgq.js → DeleteConfirmationModal-CqNXT_YQ.js} +1 -1
- package/dist/ui/assets/Documents-DLFZdmim.js +7 -0
- package/dist/ui/assets/{Logs-BgDEdckr.js → Logs-B1Bpy9dB.js} +1 -1
- package/dist/ui/assets/{MCPManager-yupmREOG.js → MCPManager-BbUDMh5Q.js} +1 -1
- package/dist/ui/assets/{ModelPricing-CbKHyj9P.js → ModelPricing-DCl-2_eJ.js} +1 -1
- package/dist/ui/assets/{Notifications-DpabYsn5.js → Notifications-8Cqj-mNp.js} +1 -1
- package/dist/ui/assets/{SatiMemories-D75MAyPU.js → SatiMemories-CdHUe6di.js} +1 -1
- package/dist/ui/assets/{SessionAudit-B3N9C1NR.js → SessionAudit-CtVHK_IH.js} +1 -1
- package/dist/ui/assets/{Settings-B1qZ-teD.js → Settings-Clge45Z0.js} +1 -1
- package/dist/ui/assets/{Skills-BmqC0xkq.js → Skills-0k7A2T5_.js} +1 -1
- package/dist/ui/assets/{Smiths-DdAcvxg6.js → Smiths-gjgBMN1F.js} +1 -1
- package/dist/ui/assets/{Tasks-D7SrOMOn.js → Tasks-AQ3MrrMp.js} +1 -1
- package/dist/ui/assets/{TrinityDatabases-cyhanIXu.js → TrinityDatabases-CGna6IMX.js} +1 -1
- package/dist/ui/assets/{UsageStats-DOgOQ4YN.js → UsageStats-B7EzZlZe.js} +1 -1
- package/dist/ui/assets/{WebhookManager-D2TdZ5kD.js → WebhookManager-Bb7KiucS.js} +1 -1
- package/dist/ui/assets/{audit-CZVCVFw_.js → audit-CJ2Ms81U.js} +1 -1
- package/dist/ui/assets/{chronos-DWSd5JMm.js → chronos-Bm68OSy4.js} +1 -1
- package/dist/ui/assets/{config-C2GB18ML.js → config-C88yQ_CP.js} +1 -1
- package/dist/ui/assets/{index-BiMscUrn.js → index-BxN2w9sY.js} +6 -6
- package/dist/ui/assets/index-C3Ff736M.css +1 -0
- package/dist/ui/assets/{mcp-JmpcV6nl.js → mcp-BE_OVkBe.js} +1 -1
- package/dist/ui/assets/{skills-D2stQj7i.js → skills-Dt0qU4gH.js} +1 -1
- package/dist/ui/assets/{stats-cDmpngPQ.js → stats-Bmdps1LR.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/ui/sw.js +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/Chat-BAivCJ5N.js +0 -41
- package/dist/ui/assets/Documents-CYW79d8T.js +0 -7
- package/dist/ui/assets/index-U7hzqcK_.css +0 -1
|
@@ -21,6 +21,36 @@ function escapeHtml(text) {
|
|
|
21
21
|
.replace(/</g, '<')
|
|
22
22
|
.replace(/>/g, '>');
|
|
23
23
|
}
|
|
24
|
+
/** Telegram callback_data has a 64-byte limit. Truncate if needed. */
|
|
25
|
+
function safeCallbackData(data, prefix) {
|
|
26
|
+
const MAX_CALLBACK_DATA = 64;
|
|
27
|
+
const full = `${prefix}${data}`;
|
|
28
|
+
if (full.length > MAX_CALLBACK_DATA) {
|
|
29
|
+
// Truncate and add hash suffix to identify original
|
|
30
|
+
const hash = data.slice(-8);
|
|
31
|
+
return `${prefix}${data.slice(0, MAX_CALLBACK_DATA - prefix.length - 8)}${hash}`;
|
|
32
|
+
}
|
|
33
|
+
return full;
|
|
34
|
+
}
|
|
35
|
+
/** Safely reply to a message, handling Telegram API errors gracefully. */
|
|
36
|
+
async function safeReply(ctx, text, options) {
|
|
37
|
+
try {
|
|
38
|
+
await ctx.reply(text, options);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// Common errors: message not found, chat not found, bot blocked, etc.
|
|
42
|
+
const msg = error?.description || error?.message || 'Unknown error';
|
|
43
|
+
if (msg.includes('message to reply not found') ||
|
|
44
|
+
msg.includes('chat not found') ||
|
|
45
|
+
msg.includes('bot was blocked') ||
|
|
46
|
+
msg.includes('user is deactivated')) {
|
|
47
|
+
// Silently ignore - user may have blocked bot or message expired
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Log other errors but don't crash
|
|
51
|
+
console.error('[Telegram] Reply error:', msg);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
24
54
|
/** Strips HTML tags and unescapes entities for plain-text Telegram fallback. */
|
|
25
55
|
function stripHtmlTags(html) {
|
|
26
56
|
return html
|
|
@@ -405,19 +435,31 @@ export class TelegramAdapter {
|
|
|
405
435
|
}
|
|
406
436
|
});
|
|
407
437
|
this.bot.action('confirm_new_session', async (ctx) => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
ctx.
|
|
411
|
-
|
|
438
|
+
try {
|
|
439
|
+
await this.handleApproveNewSessionCommand(ctx, ctx.from.username || ctx.from.first_name);
|
|
440
|
+
if (ctx.updateType === 'callback_query') {
|
|
441
|
+
ctx.answerCbQuery().catch(() => { });
|
|
442
|
+
ctx.deleteMessage().catch(() => { });
|
|
443
|
+
}
|
|
444
|
+
await safeReply(ctx, "New session created.");
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
console.error('[Telegram] confirm_new_session error:', error.message);
|
|
448
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
412
449
|
}
|
|
413
|
-
ctx.reply("New session created.");
|
|
414
450
|
});
|
|
415
451
|
this.bot.action('cancel_new_session', async (ctx) => {
|
|
416
|
-
|
|
417
|
-
ctx.
|
|
418
|
-
|
|
452
|
+
try {
|
|
453
|
+
if (ctx.updateType === 'callback_query') {
|
|
454
|
+
ctx.answerCbQuery().catch(() => { });
|
|
455
|
+
ctx.deleteMessage().catch(() => { });
|
|
456
|
+
}
|
|
457
|
+
await safeReply(ctx, "New session cancelled.");
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
console.error('[Telegram] cancel_new_session error:', error.message);
|
|
461
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
419
462
|
}
|
|
420
|
-
ctx.reply("New session cancelled.");
|
|
421
463
|
});
|
|
422
464
|
this.bot.action(/^switch_session_/, async (ctx) => {
|
|
423
465
|
const callbackQuery = ctx.callbackQuery;
|
|
@@ -425,7 +467,7 @@ export class TelegramAdapter {
|
|
|
425
467
|
const sessionId = typeof data === 'string' ? data.replace('switch_session_', '') : '';
|
|
426
468
|
const userId = ctx.from.id.toString();
|
|
427
469
|
if (!sessionId || sessionId === '') {
|
|
428
|
-
await ctx.answerCbQuery('Invalid session ID');
|
|
470
|
+
await ctx.answerCbQuery('Invalid session ID').catch(() => { });
|
|
429
471
|
return;
|
|
430
472
|
}
|
|
431
473
|
try {
|
|
@@ -436,34 +478,40 @@ export class TelegramAdapter {
|
|
|
436
478
|
// Update user-channel session mapping
|
|
437
479
|
await this.history.setUserChannelSession(this.channel, userId, sessionId);
|
|
438
480
|
this.userSessions.set(userId, sessionId);
|
|
439
|
-
await ctx.answerCbQuery();
|
|
481
|
+
await ctx.answerCbQuery().catch(() => { });
|
|
440
482
|
// Remove the previous message and send confirmation
|
|
441
483
|
if (ctx.updateType === 'callback_query') {
|
|
442
484
|
ctx.deleteMessage().catch(() => { });
|
|
443
485
|
}
|
|
444
|
-
ctx
|
|
486
|
+
await safeReply(ctx, `✅ Switched to session ID: ${sessionId}`);
|
|
445
487
|
}
|
|
446
488
|
catch (error) {
|
|
447
|
-
await ctx.answerCbQuery(`Error switching session: ${error.message}`, { show_alert: true });
|
|
489
|
+
await ctx.answerCbQuery(`Error switching session: ${error.message}`, { show_alert: true }).catch(() => { });
|
|
448
490
|
}
|
|
449
491
|
});
|
|
450
492
|
// --- Archive Flow ---
|
|
451
493
|
this.bot.action(/^ask_archive_session_/, async (ctx) => {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
[
|
|
460
|
-
|
|
461
|
-
|
|
494
|
+
try {
|
|
495
|
+
const data = ctx.callbackQuery.data;
|
|
496
|
+
const sessionId = data.replace('ask_archive_session_', '');
|
|
497
|
+
// Fetch session title for better UX (optional, but nice) - for now just use ID
|
|
498
|
+
await ctx.reply(`⚠️ *ARCHIVE SESSION?*\n\nAre you sure you want to archive session \`${escMd(sessionId)}\`?\n\nIt will be moved to long\\-term memory \\(SATI\\) and removed from the active list\\. This action cannot be easily undone via Telegram\\.`, {
|
|
499
|
+
parse_mode: 'MarkdownV2',
|
|
500
|
+
reply_markup: {
|
|
501
|
+
inline_keyboard: [
|
|
502
|
+
[
|
|
503
|
+
{ text: '✅ Yes, Archive', callback_data: safeCallbackData(sessionId, 'confirm_archive_session_') },
|
|
504
|
+
{ text: '❌ Cancel', callback_data: 'cancel_session_action' }
|
|
505
|
+
]
|
|
462
506
|
]
|
|
463
|
-
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
await ctx.answerCbQuery().catch(() => { });
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.error('[Telegram] ask_archive_session error:', error.message);
|
|
513
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
514
|
+
}
|
|
467
515
|
});
|
|
468
516
|
this.bot.action(/^confirm_archive_session_/, async (ctx) => {
|
|
469
517
|
const data = ctx.callbackQuery.data;
|
|
@@ -478,32 +526,38 @@ export class TelegramAdapter {
|
|
|
478
526
|
await this.history.deleteUserChannelSession(this.channel, userId);
|
|
479
527
|
this.userSessions.delete(userId);
|
|
480
528
|
}
|
|
481
|
-
await ctx.answerCbQuery('Session archived successfully');
|
|
529
|
+
await ctx.answerCbQuery('Session archived successfully').catch(() => { });
|
|
482
530
|
if (ctx.updateType === 'callback_query') {
|
|
483
531
|
ctx.deleteMessage().catch(() => { });
|
|
484
532
|
}
|
|
485
|
-
await ctx
|
|
533
|
+
await safeReply(ctx, `✅ Session \`${escMd(sessionId)}\` has been archived and moved to long\\-term memory\\.`, { parse_mode: 'MarkdownV2' });
|
|
486
534
|
}
|
|
487
535
|
catch (error) {
|
|
488
|
-
await ctx.answerCbQuery(`Error archiving: ${error.message}`, { show_alert: true });
|
|
536
|
+
await ctx.answerCbQuery(`Error archiving: ${error.message}`, { show_alert: true }).catch(() => { });
|
|
489
537
|
}
|
|
490
538
|
});
|
|
491
539
|
// --- Delete Flow ---
|
|
492
540
|
this.bot.action(/^ask_delete_session_/, async (ctx) => {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
[
|
|
500
|
-
|
|
501
|
-
|
|
541
|
+
try {
|
|
542
|
+
const data = ctx.callbackQuery.data;
|
|
543
|
+
const sessionId = data.replace('ask_delete_session_', '');
|
|
544
|
+
await ctx.reply(`🚫 *DELETE SESSION?*\n\nAre you sure you want to PERMANENTLY DELETE session \`${escMd(sessionId)}\`?\n\nThis action is *IRREVERSIBLE*\\. All data will be lost\\.`, {
|
|
545
|
+
parse_mode: 'MarkdownV2',
|
|
546
|
+
reply_markup: {
|
|
547
|
+
inline_keyboard: [
|
|
548
|
+
[
|
|
549
|
+
{ text: '🗑️ Yes, DELETE PERMANENTLY', callback_data: safeCallbackData(sessionId, 'confirm_delete_session_') },
|
|
550
|
+
{ text: '❌ Cancel', callback_data: 'cancel_session_action' }
|
|
551
|
+
]
|
|
502
552
|
]
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
await ctx.answerCbQuery().catch(() => { });
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
console.error('[Telegram] ask_delete_session error:', error.message);
|
|
559
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
560
|
+
}
|
|
507
561
|
});
|
|
508
562
|
this.bot.action(/^confirm_delete_session_/, async (ctx) => {
|
|
509
563
|
const data = ctx.callbackQuery.data;
|
|
@@ -518,169 +572,205 @@ export class TelegramAdapter {
|
|
|
518
572
|
await this.history.deleteUserChannelSession(this.channel, userId);
|
|
519
573
|
this.userSessions.delete(userId);
|
|
520
574
|
}
|
|
521
|
-
await ctx.answerCbQuery('Session deleted successfully');
|
|
575
|
+
await ctx.answerCbQuery('Session deleted successfully').catch(() => { });
|
|
522
576
|
if (ctx.updateType === 'callback_query') {
|
|
523
577
|
ctx.deleteMessage().catch(() => { });
|
|
524
578
|
}
|
|
525
|
-
await ctx
|
|
579
|
+
await safeReply(ctx, `🗑️ Session \`${escMd(sessionId)}\` has been permanently deleted\\.`, { parse_mode: 'MarkdownV2' });
|
|
526
580
|
}
|
|
527
581
|
catch (error) {
|
|
528
|
-
await ctx.answerCbQuery(`Error deleting: ${error.message}`, { show_alert: true });
|
|
582
|
+
await ctx.answerCbQuery(`Error deleting: ${error.message}`, { show_alert: true }).catch(() => { });
|
|
529
583
|
}
|
|
530
584
|
});
|
|
531
585
|
// --- Cancel Action ---
|
|
532
586
|
this.bot.action('cancel_session_action', async (ctx) => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
ctx.
|
|
587
|
+
try {
|
|
588
|
+
await ctx.answerCbQuery('Action cancelled').catch(() => { });
|
|
589
|
+
if (ctx.updateType === 'callback_query') {
|
|
590
|
+
ctx.deleteMessage().catch(() => { });
|
|
591
|
+
}
|
|
592
|
+
await safeReply(ctx, 'Action cancelled.');
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
console.error('[Telegram] cancel_session_action error:', error.message);
|
|
596
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
536
597
|
}
|
|
537
|
-
await ctx.reply('Action cancelled.');
|
|
538
598
|
});
|
|
539
599
|
this.bot.action(/^toggle_mcp_/, async (ctx) => {
|
|
540
600
|
const data = ctx.callbackQuery.data;
|
|
541
601
|
// format: toggle_mcp_enable_<name> or toggle_mcp_disable_<name>
|
|
542
602
|
const match = data.match(/^toggle_mcp_(enable|disable)_(.+)$/);
|
|
543
603
|
if (!match) {
|
|
544
|
-
await ctx.answerCbQuery('Invalid action');
|
|
604
|
+
await ctx.answerCbQuery('Invalid action').catch(() => { });
|
|
545
605
|
return;
|
|
546
606
|
}
|
|
547
607
|
const [, action, serverName] = match;
|
|
548
608
|
const enable = action === 'enable';
|
|
549
609
|
try {
|
|
550
610
|
await MCPManager.setServerEnabled(serverName, enable);
|
|
551
|
-
await ctx.answerCbQuery(`${enable ? '✅ Enabled' : '❌ Disabled'}: ${serverName}`);
|
|
611
|
+
await ctx.answerCbQuery(`${enable ? '✅ Enabled' : '❌ Disabled'}: ${serverName}`).catch(() => { });
|
|
552
612
|
if (ctx.updateType === 'callback_query') {
|
|
553
613
|
ctx.deleteMessage().catch(() => { });
|
|
554
614
|
}
|
|
555
615
|
const user = ctx.from?.username || ctx.from?.first_name || 'unknown';
|
|
556
616
|
this.display.log(`MCP '${serverName}' ${enable ? 'enabled' : 'disabled'} by @${user}`, { source: 'Telegram', level: 'info' });
|
|
557
617
|
await this.handleMcpListCommand(ctx, user);
|
|
558
|
-
await ctx
|
|
618
|
+
await safeReply(ctx, `⚠️ Use /mcpreload for the changes to take effect.`);
|
|
559
619
|
}
|
|
560
620
|
catch (error) {
|
|
561
|
-
await ctx.answerCbQuery('Failed to update MCP');
|
|
562
|
-
await ctx
|
|
621
|
+
await ctx.answerCbQuery('Failed to update MCP').catch(() => { });
|
|
622
|
+
await safeReply(ctx, `❌ Failed to ${enable ? 'enable' : 'disable'} MCP '${serverName}': ${error.message}`);
|
|
563
623
|
}
|
|
564
624
|
});
|
|
565
625
|
// --- Trinity DB Test Connection ---
|
|
566
626
|
this.bot.action(/^test_trinity_db_/, async (ctx) => {
|
|
567
|
-
const data = ctx.callbackQuery.data;
|
|
568
|
-
const id = parseInt(data.replace('test_trinity_db_', ''), 10);
|
|
569
|
-
if (isNaN(id)) {
|
|
570
|
-
await ctx.answerCbQuery('Invalid ID');
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
await ctx.answerCbQuery('Testing connection…');
|
|
574
627
|
try {
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
await ctx.reply('❌ Database not found.');
|
|
628
|
+
const data = ctx.callbackQuery.data;
|
|
629
|
+
const id = parseInt(data.replace('test_trinity_db_', ''), 10);
|
|
630
|
+
if (isNaN(id)) {
|
|
631
|
+
await ctx.answerCbQuery('Invalid ID').catch(() => { });
|
|
580
632
|
return;
|
|
581
633
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
634
|
+
await ctx.answerCbQuery('Testing connection…').catch(() => { });
|
|
635
|
+
try {
|
|
636
|
+
const { DatabaseRegistry } = await import('../runtime/memory/trinity-db.js');
|
|
637
|
+
const { testConnection } = await import('../runtime/trinity-connector.js');
|
|
638
|
+
const db = DatabaseRegistry.getInstance().getDatabase(id);
|
|
639
|
+
if (!db) {
|
|
640
|
+
await safeReply(ctx, '❌ Database not found.');
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const ok = await testConnection(db);
|
|
644
|
+
await safeReply(ctx, ok
|
|
645
|
+
? `✅ <b>${escapeHtml(db.name)}</b>: connection successful.`
|
|
646
|
+
: `❌ <b>${escapeHtml(db.name)}</b>: connection failed.`, { parse_mode: 'HTML' });
|
|
647
|
+
}
|
|
648
|
+
catch (e) {
|
|
649
|
+
await safeReply(ctx, `❌ Error testing connection: ${escapeHtml(e.message)}`, { parse_mode: 'HTML' });
|
|
650
|
+
}
|
|
586
651
|
}
|
|
587
|
-
catch (
|
|
588
|
-
|
|
652
|
+
catch (error) {
|
|
653
|
+
console.error('[Telegram] test_trinity_db error:', error.message);
|
|
654
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
589
655
|
}
|
|
590
656
|
});
|
|
591
657
|
// --- Trinity DB Refresh Schema ---
|
|
592
658
|
this.bot.action(/^refresh_trinity_db_schema_/, async (ctx) => {
|
|
593
|
-
const data = ctx.callbackQuery.data;
|
|
594
|
-
const id = parseInt(data.replace('refresh_trinity_db_schema_', ''), 10);
|
|
595
|
-
if (isNaN(id)) {
|
|
596
|
-
await ctx.answerCbQuery('Invalid ID');
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
await ctx.answerCbQuery('Refreshing schema…');
|
|
600
659
|
try {
|
|
601
|
-
const
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const db = registry.getDatabase(id);
|
|
606
|
-
if (!db) {
|
|
607
|
-
await ctx.reply('❌ Database not found.');
|
|
660
|
+
const data = ctx.callbackQuery.data;
|
|
661
|
+
const id = parseInt(data.replace('refresh_trinity_db_schema_', ''), 10);
|
|
662
|
+
if (isNaN(id)) {
|
|
663
|
+
await ctx.answerCbQuery('Invalid ID').catch(() => { });
|
|
608
664
|
return;
|
|
609
665
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
666
|
+
await ctx.answerCbQuery('Refreshing schema…').catch(() => { });
|
|
667
|
+
try {
|
|
668
|
+
const { DatabaseRegistry } = await import('../runtime/memory/trinity-db.js');
|
|
669
|
+
const { introspectSchema } = await import('../runtime/trinity-connector.js');
|
|
670
|
+
const { Trinity } = await import('../runtime/trinity.js');
|
|
671
|
+
const registry = DatabaseRegistry.getInstance();
|
|
672
|
+
const db = registry.getDatabase(id);
|
|
673
|
+
if (!db) {
|
|
674
|
+
await safeReply(ctx, '❌ Database not found.');
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const schema = await introspectSchema(db);
|
|
678
|
+
registry.updateSchema(id, JSON.stringify(schema, null, 2));
|
|
679
|
+
await Trinity.refreshDelegateCatalog().catch(() => { });
|
|
680
|
+
const tableNames = schema.databases
|
|
681
|
+
? schema.databases.flatMap((d) => d.tables.map((t) => `${d.name}.${t.name}`))
|
|
682
|
+
: schema.tables.map((t) => t.name);
|
|
683
|
+
const count = tableNames.length;
|
|
684
|
+
await safeReply(ctx, `🔄 <b>${escapeHtml(db.name)}</b>: schema refreshed — ${count} ${count === 1 ? 'table' : 'tables'}.`, { parse_mode: 'HTML' });
|
|
685
|
+
}
|
|
686
|
+
catch (e) {
|
|
687
|
+
await safeReply(ctx, `❌ Error refreshing schema: ${escapeHtml(e.message)}`, { parse_mode: 'HTML' });
|
|
688
|
+
}
|
|
618
689
|
}
|
|
619
|
-
catch (
|
|
620
|
-
|
|
690
|
+
catch (error) {
|
|
691
|
+
console.error('[Telegram] refresh_trinity_db_schema error:', error.message);
|
|
692
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
621
693
|
}
|
|
622
694
|
});
|
|
623
695
|
// --- Trinity DB Delete Flow ---
|
|
624
696
|
this.bot.action(/^ask_trinity_db_delete_/, async (ctx) => {
|
|
625
|
-
const data = ctx.callbackQuery.data;
|
|
626
|
-
const id = parseInt(data.replace('ask_trinity_db_delete_', ''), 10);
|
|
627
|
-
if (isNaN(id)) {
|
|
628
|
-
await ctx.answerCbQuery('Invalid ID');
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
697
|
try {
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
if (
|
|
635
|
-
await ctx.answerCbQuery('
|
|
698
|
+
const data = ctx.callbackQuery.data;
|
|
699
|
+
const id = parseInt(data.replace('ask_trinity_db_delete_', ''), 10);
|
|
700
|
+
if (isNaN(id)) {
|
|
701
|
+
await ctx.answerCbQuery('Invalid ID').catch(() => { });
|
|
636
702
|
return;
|
|
637
703
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
704
|
+
try {
|
|
705
|
+
const { DatabaseRegistry } = await import('../runtime/memory/trinity-db.js');
|
|
706
|
+
const db = DatabaseRegistry.getInstance().getDatabase(id);
|
|
707
|
+
if (!db) {
|
|
708
|
+
await ctx.answerCbQuery('Database not found').catch(() => { });
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
await ctx.answerCbQuery().catch(() => { });
|
|
712
|
+
await ctx.reply(`⚠️ Delete <b>${escapeHtml(db.name)}</b> (${escapeHtml(db.type)}) from Trinity?\n\nThe actual database won't be affected — only this registration will be removed.`, {
|
|
713
|
+
parse_mode: 'HTML',
|
|
714
|
+
reply_markup: {
|
|
715
|
+
inline_keyboard: [
|
|
716
|
+
[
|
|
717
|
+
{ text: '🗑️ Yes, delete', callback_data: `confirm_trinity_db_delete_${id}` },
|
|
718
|
+
{ text: 'Cancel', callback_data: 'cancel_trinity_db_delete' },
|
|
719
|
+
],
|
|
646
720
|
],
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
}
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
catch (e) {
|
|
725
|
+
await ctx.answerCbQuery(`Error: ${e.message}`, { show_alert: true }).catch(() => { });
|
|
726
|
+
}
|
|
650
727
|
}
|
|
651
|
-
catch (
|
|
652
|
-
|
|
728
|
+
catch (error) {
|
|
729
|
+
console.error('[Telegram] ask_trinity_db_delete error:', error.message);
|
|
730
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
653
731
|
}
|
|
654
732
|
});
|
|
655
733
|
this.bot.action(/^confirm_trinity_db_delete_/, async (ctx) => {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
734
|
+
try {
|
|
735
|
+
const data = ctx.callbackQuery.data;
|
|
736
|
+
const id = parseInt(data.replace('confirm_trinity_db_delete_', ''), 10);
|
|
737
|
+
if (isNaN(id)) {
|
|
738
|
+
await ctx.answerCbQuery('Invalid ID').catch(() => { });
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
const { DatabaseRegistry } = await import('../runtime/memory/trinity-db.js');
|
|
743
|
+
const registry = DatabaseRegistry.getInstance();
|
|
744
|
+
const db = registry.getDatabase(id);
|
|
745
|
+
const name = db?.name ?? `#${id}`;
|
|
746
|
+
const deleted = registry.deleteDatabase(id);
|
|
747
|
+
await ctx.answerCbQuery(deleted ? '🗑️ Deleted' : 'Not found').catch(() => { });
|
|
748
|
+
if (ctx.updateType === 'callback_query')
|
|
749
|
+
ctx.deleteMessage().catch(() => { });
|
|
750
|
+
const user = ctx.from?.username || ctx.from?.first_name || 'unknown';
|
|
751
|
+
this.display.log(`Trinity DB '${name}' deleted by @${user}`, { source: 'Telegram', level: 'info' });
|
|
752
|
+
await safeReply(ctx, deleted ? `🗑️ <b>${escapeHtml(name)}</b> removed from Trinity.` : `❌ Database #${id} not found.`, { parse_mode: 'HTML' });
|
|
753
|
+
}
|
|
754
|
+
catch (e) {
|
|
755
|
+
await ctx.answerCbQuery(`Error: ${e.message}`, { show_alert: true }).catch(() => { });
|
|
756
|
+
}
|
|
661
757
|
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
console.error('[Telegram] confirm_trinity_db_delete error:', error.message);
|
|
760
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
this.bot.action('cancel_trinity_db_delete', async (ctx) => {
|
|
662
764
|
try {
|
|
663
|
-
|
|
664
|
-
const registry = DatabaseRegistry.getInstance();
|
|
665
|
-
const db = registry.getDatabase(id);
|
|
666
|
-
const name = db?.name ?? `#${id}`;
|
|
667
|
-
const deleted = registry.deleteDatabase(id);
|
|
668
|
-
await ctx.answerCbQuery(deleted ? '🗑️ Deleted' : 'Not found');
|
|
765
|
+
await ctx.answerCbQuery('Cancelled').catch(() => { });
|
|
669
766
|
if (ctx.updateType === 'callback_query')
|
|
670
767
|
ctx.deleteMessage().catch(() => { });
|
|
671
|
-
const user = ctx.from?.username || ctx.from?.first_name || 'unknown';
|
|
672
|
-
this.display.log(`Trinity DB '${name}' deleted by @${user}`, { source: 'Telegram', level: 'info' });
|
|
673
|
-
await ctx.reply(deleted ? `🗑️ <b>${escapeHtml(name)}</b> removed from Trinity.` : `❌ Database #${id} not found.`, { parse_mode: 'HTML' });
|
|
674
768
|
}
|
|
675
|
-
catch (
|
|
676
|
-
|
|
769
|
+
catch (error) {
|
|
770
|
+
console.error('[Telegram] cancel_trinity_db_delete error:', error.message);
|
|
771
|
+
ctx.answerCbQuery(`Error: ${error.message}`).catch(() => { });
|
|
677
772
|
}
|
|
678
773
|
});
|
|
679
|
-
this.bot.action('cancel_trinity_db_delete', async (ctx) => {
|
|
680
|
-
await ctx.answerCbQuery('Cancelled');
|
|
681
|
-
if (ctx.updateType === 'callback_query')
|
|
682
|
-
ctx.deleteMessage().catch(() => { });
|
|
683
|
-
});
|
|
684
774
|
this.bot.launch().catch((err) => {
|
|
685
775
|
if (this.isConnected) {
|
|
686
776
|
this.display.log(`Telegram bot error: ${err}`, { source: 'Telegram', level: 'error' });
|
|
@@ -1244,16 +1334,16 @@ export class TelegramAdapter {
|
|
|
1244
1334
|
if (!isCurrent) {
|
|
1245
1335
|
sessionButtons.push({
|
|
1246
1336
|
text: `➡️ Switch`,
|
|
1247
|
-
callback_data:
|
|
1337
|
+
callback_data: safeCallbackData(session.id, 'switch_session_')
|
|
1248
1338
|
});
|
|
1249
1339
|
}
|
|
1250
1340
|
sessionButtons.push({
|
|
1251
1341
|
text: `📂 Archive`,
|
|
1252
|
-
callback_data:
|
|
1342
|
+
callback_data: safeCallbackData(session.id, 'ask_archive_session_')
|
|
1253
1343
|
});
|
|
1254
1344
|
sessionButtons.push({
|
|
1255
1345
|
text: `🗑️ Delete`,
|
|
1256
|
-
callback_data:
|
|
1346
|
+
callback_data: safeCallbackData(session.id, 'ask_delete_session_')
|
|
1257
1347
|
});
|
|
1258
1348
|
keyboard.push(sessionButtons);
|
|
1259
1349
|
}
|
package/dist/http/api.js
CHANGED
|
@@ -184,6 +184,7 @@ export function createApiRouter(oracle, chronosWorker) {
|
|
|
184
184
|
provider: row.provider ?? null,
|
|
185
185
|
model: row.model ?? null,
|
|
186
186
|
audio_duration_seconds: row.audio_duration_seconds ?? null,
|
|
187
|
+
source: row.source ?? null,
|
|
187
188
|
sati_memories_count: null,
|
|
188
189
|
};
|
|
189
190
|
});
|
|
@@ -79,6 +79,10 @@ export function createLinkRouter() {
|
|
|
79
79
|
try {
|
|
80
80
|
const config = ConfigManager.getInstance().getLinkConfig();
|
|
81
81
|
const maxSizeMB = config.max_file_size_mb;
|
|
82
|
+
// Get expected size from header for validation
|
|
83
|
+
const expectedSize = req.headers['x-expected-size']
|
|
84
|
+
? parseInt(req.headers['x-expected-size'], 10)
|
|
85
|
+
: null;
|
|
82
86
|
// Configure multer with config max size
|
|
83
87
|
const uploadWithConfig = multer({
|
|
84
88
|
storage,
|
|
@@ -107,13 +111,30 @@ export function createLinkRouter() {
|
|
|
107
111
|
if (!req.file) {
|
|
108
112
|
return res.status(400).json({ error: 'No file uploaded' });
|
|
109
113
|
}
|
|
110
|
-
//
|
|
111
|
-
|
|
114
|
+
// Validate file size if expected size was provided
|
|
115
|
+
if (expectedSize !== null) {
|
|
116
|
+
const actualSize = req.file.size;
|
|
117
|
+
const sizeDiff = Math.abs(actualSize - expectedSize);
|
|
118
|
+
const tolerance = expectedSize * 0.01; // 1% tolerance
|
|
119
|
+
if (sizeDiff > tolerance) {
|
|
120
|
+
// Delete the uploaded file
|
|
121
|
+
await fs.unlink(req.file.path).catch(() => { });
|
|
122
|
+
return res.status(400).json({
|
|
123
|
+
error: 'File size mismatch',
|
|
124
|
+
expected: expectedSize,
|
|
125
|
+
actual: actualSize,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Process the document immediately (validates integrity + indexes)
|
|
130
|
+
const linkWorker = LinkWorker.getInstance();
|
|
131
|
+
const result = await linkWorker.processDocument(req.file.path);
|
|
112
132
|
res.json({
|
|
113
|
-
message: 'File uploaded
|
|
133
|
+
message: 'File uploaded and processed',
|
|
114
134
|
filename: Buffer.from(req.file.originalname, 'latin1').toString('utf-8'),
|
|
115
135
|
path: req.file.path,
|
|
116
|
-
indexed: result
|
|
136
|
+
indexed: result === 'indexed',
|
|
137
|
+
status: result,
|
|
117
138
|
});
|
|
118
139
|
}
|
|
119
140
|
catch (err) {
|