lazy-gravity 0.6.2 → 0.7.0
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/bot/index.js +354 -28
- package/dist/bot/telegramCommands.js +175 -48
- package/dist/bot/telegramJoinCommand.js +170 -0
- package/dist/bot/telegramMessageHandler.js +24 -7
- package/dist/bot/telegramProjectCommand.js +71 -18
- package/dist/bot/telegramStartupTarget.js +54 -0
- package/dist/commands/chatCommandHandler.js +8 -12
- package/dist/commands/joinCommandHandler.js +16 -10
- package/dist/commands/registerSlashCommands.js +13 -1
- package/dist/commands/workspaceCommandHandler.js +22 -7
- package/dist/database/accountPreferenceRepository.js +29 -0
- package/dist/database/channelPreferenceRepository.js +29 -0
- package/dist/database/chatSessionRepository.js +66 -3
- package/dist/database/telegramBindingRepository.js +13 -0
- package/dist/events/interactionCreateHandler.js +194 -13
- package/dist/events/messageCreateHandler.js +103 -7
- package/dist/handlers/accountSelectAction.js +45 -0
- package/dist/handlers/modelButtonAction.js +13 -0
- package/dist/services/cdpBridgeManager.js +23 -18
- package/dist/services/cdpConnectionPool.js +133 -206
- package/dist/services/chatSessionService.js +199 -16
- package/dist/services/userMessageDetector.js +4 -4
- package/dist/ui/accountUi.js +60 -0
- package/dist/utils/accountUtils.js +36 -0
- package/dist/utils/cdpPorts.js +97 -2
- package/dist/utils/configLoader.js +14 -0
- package/package.json +1 -1
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Telegram command parser and handlers.
|
|
4
|
-
*
|
|
5
|
-
* Handles built-in bot commands that can be answered immediately
|
|
6
|
-
* without routing through CDP/Antigravity:
|
|
7
|
-
* /start — Welcome message
|
|
8
|
-
* /help — List available commands
|
|
9
|
-
* /status — Show bot connection status
|
|
10
|
-
* /stop — Interrupt active LLM generation
|
|
11
|
-
* /ping — Latency check
|
|
12
|
-
* /mode — Switch execution mode
|
|
13
|
-
* /model — Switch LLM model
|
|
14
|
-
* /screenshot — Capture Antigravity screenshot
|
|
15
|
-
* /autoaccept — Toggle auto-accept for approval dialogs
|
|
16
|
-
* /template — List and execute prompt templates
|
|
17
|
-
* /logs — Show recent log entries
|
|
18
|
-
* /new — Start a new chat session
|
|
19
|
-
*/
|
|
20
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
3
|
if (k2 === undefined) k2 = k;
|
|
22
4
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -56,6 +38,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
56
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
39
|
exports.parseTelegramCommand = parseTelegramCommand;
|
|
58
40
|
exports.handleTelegramCommand = handleTelegramCommand;
|
|
41
|
+
const telegramJoinCommand_1 = require("./telegramJoinCommand");
|
|
42
|
+
/**
|
|
43
|
+
* Telegram command parser and handlers.
|
|
44
|
+
*
|
|
45
|
+
* Handles built-in bot commands that can be answered immediately
|
|
46
|
+
* without routing through CDP/Antigravity:
|
|
47
|
+
* /start — Welcome message
|
|
48
|
+
* /help — List available commands
|
|
49
|
+
* /status — Show bot connection status
|
|
50
|
+
* /stop — Interrupt active LLM generation
|
|
51
|
+
* /ping — Latency check
|
|
52
|
+
* /mode — Switch execution mode
|
|
53
|
+
* /model — Switch LLM model
|
|
54
|
+
* /screenshot — Capture Antigravity screenshot
|
|
55
|
+
* /autoaccept — Toggle auto-accept for approval dialogs
|
|
56
|
+
* /template — List and execute prompt templates
|
|
57
|
+
* /logs — Show recent log entries
|
|
58
|
+
* /new — Start a new chat session
|
|
59
|
+
*/
|
|
59
60
|
const fs_1 = __importDefault(require("fs"));
|
|
60
61
|
const cdpBridgeManager_1 = require("../services/cdpBridgeManager");
|
|
61
62
|
const modeUi_1 = require("../ui/modeUi");
|
|
@@ -63,13 +64,16 @@ const modelsUi_1 = require("../ui/modelsUi");
|
|
|
63
64
|
const autoAcceptUi_1 = require("../ui/autoAcceptUi");
|
|
64
65
|
const templateUi_1 = require("../ui/templateUi");
|
|
65
66
|
const screenshotUi_1 = require("../ui/screenshotUi");
|
|
67
|
+
const accountUi_1 = require("../ui/accountUi");
|
|
66
68
|
const logBuffer_1 = require("../utils/logBuffer");
|
|
67
69
|
const telegramFormatter_1 = require("../platform/telegram/telegramFormatter");
|
|
68
70
|
const logger_1 = require("../utils/logger");
|
|
71
|
+
const telegramProjectCommand_1 = require("./telegramProjectCommand");
|
|
72
|
+
const accountUtils_1 = require("../utils/accountUtils");
|
|
69
73
|
// ---------------------------------------------------------------------------
|
|
70
74
|
// Known commands (used by both parser and /help output)
|
|
71
75
|
// ---------------------------------------------------------------------------
|
|
72
|
-
const KNOWN_COMMANDS = ['start', 'help', 'status', 'stop', 'ping', 'mode', 'model', 'screenshot', 'autoaccept', 'template', 'template_add', 'template_delete', 'project_create', 'logs', 'new'];
|
|
76
|
+
const KNOWN_COMMANDS = ['start', 'help', 'status', 'stop', 'ping', 'mode', 'model', 'screenshot', 'autoaccept', 'account', 'project_reopen', 'template', 'template_add', 'template_delete', 'project_create', 'logs', 'new', 'join', 'mirror'];
|
|
73
77
|
/**
|
|
74
78
|
* Parse a Telegram command from message text.
|
|
75
79
|
*
|
|
@@ -133,6 +137,12 @@ async function handleTelegramCommand(deps, message, parsed) {
|
|
|
133
137
|
case 'autoaccept':
|
|
134
138
|
await handleAutoAccept(deps, message, parsed.args);
|
|
135
139
|
break;
|
|
140
|
+
case 'account':
|
|
141
|
+
await handleAccount(deps, message, parsed.args);
|
|
142
|
+
break;
|
|
143
|
+
case 'project_reopen':
|
|
144
|
+
await handleProjectReopen(deps, message);
|
|
145
|
+
break;
|
|
136
146
|
case 'template':
|
|
137
147
|
await handleTemplate(deps, message);
|
|
138
148
|
break;
|
|
@@ -151,6 +161,12 @@ async function handleTelegramCommand(deps, message, parsed) {
|
|
|
151
161
|
case 'new':
|
|
152
162
|
await handleNew(deps, message);
|
|
153
163
|
break;
|
|
164
|
+
case 'join':
|
|
165
|
+
await (0, telegramJoinCommand_1.handleJoin)(deps, message);
|
|
166
|
+
break;
|
|
167
|
+
case 'mirror':
|
|
168
|
+
await (0, telegramJoinCommand_1.handleMirror)(deps, message);
|
|
169
|
+
break;
|
|
154
170
|
default:
|
|
155
171
|
// Should not happen — parser filters unknowns
|
|
156
172
|
break;
|
|
@@ -183,11 +199,15 @@ async function handleHelp(message) {
|
|
|
183
199
|
'/model — Switch LLM model',
|
|
184
200
|
'/screenshot — Capture Antigravity screenshot',
|
|
185
201
|
'/autoaccept — Toggle auto-accept mode',
|
|
202
|
+
'/account — Show and switch Antigravity account',
|
|
203
|
+
'/project_reopen — Reopen the bound project in the selected Antigravity account',
|
|
186
204
|
'/template — List prompt templates',
|
|
187
205
|
'/template_add — Add a prompt template',
|
|
188
206
|
'/template_delete — Delete a prompt template',
|
|
189
207
|
'/project_create — Create a new workspace',
|
|
190
208
|
'/new — Start a new chat session',
|
|
209
|
+
'/join — Take over an existing Antigravity session',
|
|
210
|
+
'/mirror — Toggle PC-to-Telegram message mirroring',
|
|
191
211
|
'/logs — Show recent log entries',
|
|
192
212
|
'/stop — Interrupt active LLM generation',
|
|
193
213
|
'/ping — Check bot latency',
|
|
@@ -316,6 +336,52 @@ async function handleAutoAccept(deps, message, args) {
|
|
|
316
336
|
const payload = (0, autoAcceptUi_1.buildAutoAcceptPayload)(deps.bridge.autoAccept.isEnabled());
|
|
317
337
|
await message.reply(payload).catch(logger_1.logger.error);
|
|
318
338
|
}
|
|
339
|
+
async function handleAccount(deps, message, args) {
|
|
340
|
+
if (!deps.accountPrefRepo) {
|
|
341
|
+
await message.reply({ text: 'Account preference service not available.' }).catch(logger_1.logger.error);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const names = (0, accountUtils_1.listAccountNames)(deps.antigravityAccounts);
|
|
345
|
+
const chatId = message.channel.id;
|
|
346
|
+
const userId = message.author.id;
|
|
347
|
+
const applySelection = (selectedAccount) => {
|
|
348
|
+
deps.accountPrefRepo?.setAccountName(userId, selectedAccount);
|
|
349
|
+
deps.channelPrefRepo?.setAccountName(chatId, selectedAccount);
|
|
350
|
+
deps.bridge.selectedAccountByChannel?.set(chatId, selectedAccount);
|
|
351
|
+
const channelBinding = deps.telegramBindingRepo?.findByChatId(chatId);
|
|
352
|
+
const workspacePath = channelBinding
|
|
353
|
+
? (deps.workspaceService
|
|
354
|
+
? deps.workspaceService.getWorkspacePath(channelBinding.workspacePath)
|
|
355
|
+
: channelBinding.workspacePath)
|
|
356
|
+
: null;
|
|
357
|
+
const selectedPort = deps.antigravityAccounts?.find((a) => a.name === selectedAccount)?.cdpPort;
|
|
358
|
+
logger_1.logger.info(`[AccountSwitch] source=telegram_command channel=${chatId} user=${userId} ` +
|
|
359
|
+
`account=${selectedAccount} port=${selectedPort ?? 'unknown'} ` +
|
|
360
|
+
`workspace=${workspacePath ?? 'unbound'}`);
|
|
361
|
+
};
|
|
362
|
+
const requested = args.trim();
|
|
363
|
+
if (requested) {
|
|
364
|
+
if (!names.includes(requested)) {
|
|
365
|
+
await message.reply({
|
|
366
|
+
text: `⚠️ Unknown account: <b>${(0, telegramFormatter_1.escapeHtml)(requested)}</b>\nAvailable: ${names.map(telegramFormatter_1.escapeHtml).join(', ')}`,
|
|
367
|
+
}).catch(logger_1.logger.error);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
applySelection(requested);
|
|
371
|
+
await message.reply({ text: `✅ Switched account to <b>${(0, telegramFormatter_1.escapeHtml)(requested)}</b>.` }).catch(logger_1.logger.error);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const current = (0, accountUtils_1.resolveScopedAccountName)({
|
|
375
|
+
channelId: chatId,
|
|
376
|
+
userId,
|
|
377
|
+
selectedAccountByChannel: deps.bridge.selectedAccountByChannel,
|
|
378
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
379
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
380
|
+
accounts: deps.antigravityAccounts,
|
|
381
|
+
});
|
|
382
|
+
const payload = (0, accountUi_1.buildAccountPayload)(current, names);
|
|
383
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
384
|
+
}
|
|
319
385
|
async function handleTemplate(deps, message) {
|
|
320
386
|
if (!deps.templateRepo) {
|
|
321
387
|
await message.reply({ text: 'Template service not available.' }).catch(logger_1.logger.error);
|
|
@@ -415,48 +481,50 @@ async function handleLogs(message, args) {
|
|
|
415
481
|
await message.reply({ text: truncated }).catch(logger_1.logger.error);
|
|
416
482
|
}
|
|
417
483
|
async function handleNew(deps, message) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
// Resolve workspace binding for this chat
|
|
423
|
-
const chatId = message.channel.id;
|
|
424
|
-
const binding = deps.telegramBindingRepo?.findByChatId(chatId);
|
|
484
|
+
const originalChannelId = message.channel.id;
|
|
485
|
+
const binding = deps.telegramBindingRepo?.findByChatIdWithParentFallback(originalChannelId);
|
|
425
486
|
if (!binding) {
|
|
426
|
-
await message.reply({
|
|
427
|
-
text: 'No project is linked to this chat. Use /project to bind a workspace first.',
|
|
428
|
-
}).catch(logger_1.logger.error);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
// Resolve workspace path and connect to CDP
|
|
432
|
-
let cdp;
|
|
433
|
-
try {
|
|
434
|
-
const workspacePath = deps.workspaceService
|
|
435
|
-
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
436
|
-
: binding.workspacePath;
|
|
437
|
-
cdp = await deps.bridge.pool.getOrConnect(workspacePath);
|
|
438
|
-
}
|
|
439
|
-
catch (err) {
|
|
440
|
-
logger_1.logger.error('[TelegramCommand:new] CDP connection failed:', err?.message || err);
|
|
441
|
-
await message.reply({ text: 'Failed to connect to Antigravity.' }).catch(logger_1.logger.error);
|
|
487
|
+
await message.reply({ text: '⚠️ No project is linked to this chat. Use /project first, or /project_reopen if this is a previously used session.' }).catch(logger_1.logger.error);
|
|
442
488
|
return;
|
|
443
489
|
}
|
|
444
|
-
|
|
490
|
+
const resolvedWorkspacePath = deps.workspaceService
|
|
491
|
+
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
492
|
+
: binding.workspacePath;
|
|
493
|
+
let targetChannelId = originalChannelId;
|
|
494
|
+
if (deps.botApi && deps.bridge && deps.telegramBindingRepo) {
|
|
495
|
+
targetChannelId = await (0, telegramProjectCommand_1.tryCreateTopicAndBind)(deps.botApi, originalChannelId, binding.workspacePath, deps.telegramBindingRepo, deps.bridge.pool);
|
|
496
|
+
}
|
|
497
|
+
if (targetChannelId !== originalChannelId) {
|
|
498
|
+
await message.reply({ text: `✅ Created a new topic for the session.` }).catch(() => { });
|
|
499
|
+
}
|
|
500
|
+
const selectedAccount = (0, accountUtils_1.resolveScopedAccountName)({
|
|
501
|
+
channelId: originalChannelId,
|
|
502
|
+
userId: message.author.id,
|
|
503
|
+
selectedAccountByChannel: deps.bridge.selectedAccountByChannel,
|
|
504
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
505
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
506
|
+
accounts: deps.antigravityAccounts,
|
|
507
|
+
});
|
|
445
508
|
try {
|
|
509
|
+
const cdp = await deps.bridge.pool.getOrConnect(resolvedWorkspacePath, { name: selectedAccount });
|
|
510
|
+
if (!deps.chatSessionService) {
|
|
511
|
+
await message.reply({ text: 'Chat session service not available.' }).catch(logger_1.logger.error);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
446
514
|
const result = await deps.chatSessionService.startNewChat(cdp);
|
|
447
515
|
if (result.ok) {
|
|
448
|
-
|
|
516
|
+
if (targetChannelId === originalChannelId) {
|
|
517
|
+
await message.reply({ text: '✅ New chat session started.' }).catch(logger_1.logger.error);
|
|
518
|
+
}
|
|
449
519
|
}
|
|
450
520
|
else {
|
|
451
521
|
logger_1.logger.warn('[TelegramCommand:new] startNewChat failed:', result.error);
|
|
452
|
-
await message.reply({
|
|
453
|
-
text: `Failed to start new chat: ${(0, telegramFormatter_1.escapeHtml)(result.error || 'unknown error')}`,
|
|
454
|
-
}).catch(logger_1.logger.error);
|
|
522
|
+
await message.reply({ text: `❌ Failed to start new chat: ${result.error}` }).catch(logger_1.logger.error);
|
|
455
523
|
}
|
|
456
524
|
}
|
|
457
525
|
catch (err) {
|
|
458
526
|
logger_1.logger.error('[TelegramCommand:new] startNewChat threw:', err?.message || err);
|
|
459
|
-
await message.reply({ text: 'Failed to
|
|
527
|
+
await message.reply({ text: '❌ Failed to connect to Antigravity. Is it running?' }).catch(logger_1.logger.error);
|
|
460
528
|
}
|
|
461
529
|
}
|
|
462
530
|
// ---------------------------------------------------------------------------
|
|
@@ -476,3 +544,62 @@ async function sendFilePayload(message, payload) {
|
|
|
476
544
|
await message.reply({ text: 'Screenshot captured but file sending failed.' }).catch(logger_1.logger.error);
|
|
477
545
|
}
|
|
478
546
|
}
|
|
547
|
+
async function handleProjectReopen(deps, message) {
|
|
548
|
+
const chatId = message.channel.id;
|
|
549
|
+
const channelBinding = deps.telegramBindingRepo?.findByChatIdWithParentFallback(chatId);
|
|
550
|
+
if (!channelBinding) {
|
|
551
|
+
await message.reply({ text: '⚠️ No project is bound to this chat. Use /project first, or /project_reopen if this is a previously used session.' }).catch(logger_1.logger.error);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const workspacePath = deps.workspaceService
|
|
555
|
+
? deps.workspaceService.getWorkspacePath(channelBinding.workspacePath)
|
|
556
|
+
: channelBinding.workspacePath;
|
|
557
|
+
if (!fs_1.default.existsSync(workspacePath) || !fs_1.default.statSync(workspacePath).isDirectory()) {
|
|
558
|
+
await message.reply({ text: `❌ Project folder does not exist: <code>${(0, telegramFormatter_1.escapeHtml)(workspacePath)}</code>` }).catch(logger_1.logger.error);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const selectedAccount = (0, accountUtils_1.resolveScopedAccountName)({
|
|
562
|
+
channelId: chatId,
|
|
563
|
+
userId: message.author.id,
|
|
564
|
+
selectedAccountByChannel: deps.bridge.selectedAccountByChannel,
|
|
565
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
566
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
567
|
+
accounts: deps.antigravityAccounts,
|
|
568
|
+
});
|
|
569
|
+
const accountPorts = Object.fromEntries((deps.antigravityAccounts ?? []).map((account) => [account.name, account.cdpPort]));
|
|
570
|
+
const accountUserDataDirs = Object.fromEntries((deps.antigravityAccounts ?? [])
|
|
571
|
+
.filter((account) => typeof account.userDataDir === 'string' && account.userDataDir.trim().length > 0)
|
|
572
|
+
.map((account) => [account.name, account.userDataDir.trim()]));
|
|
573
|
+
const port = accountPorts[selectedAccount] ?? null;
|
|
574
|
+
const projectName = deps.bridge.pool.extractProjectName(workspacePath);
|
|
575
|
+
logger_1.logger.info(`[ProjectReopenCommand] channel=${chatId} user=${message.author.id} ` +
|
|
576
|
+
`project=${projectName} account=${selectedAccount} ` +
|
|
577
|
+
`port=${port ?? 'unknown'} workspacePath=${workspacePath}`);
|
|
578
|
+
try {
|
|
579
|
+
const { CdpService } = await Promise.resolve().then(() => __importStar(require('../services/cdpService')));
|
|
580
|
+
const cdp = new CdpService({
|
|
581
|
+
accountName: selectedAccount,
|
|
582
|
+
accountPorts,
|
|
583
|
+
accountUserDataDirs,
|
|
584
|
+
cdpCallTimeout: 15000,
|
|
585
|
+
maxReconnectAttempts: 0,
|
|
586
|
+
});
|
|
587
|
+
try {
|
|
588
|
+
await cdp.openWorkspace(workspacePath);
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
await cdp.disconnect().catch(() => { });
|
|
592
|
+
}
|
|
593
|
+
deps.bridge.selectedAccountByChannel?.set(chatId, selectedAccount);
|
|
594
|
+
deps.bridge.pool.setPreferredAccountForWorkspace(workspacePath, selectedAccount);
|
|
595
|
+
await message.reply({
|
|
596
|
+
text: `✅ Reopened <b>${(0, telegramFormatter_1.escapeHtml)(projectName)}</b> in account <b>${(0, telegramFormatter_1.escapeHtml)(selectedAccount)}</b>${port ? ` (CDP ${port})` : ''}.`,
|
|
597
|
+
}).catch(logger_1.logger.error);
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
logger_1.logger.error('[ProjectReopenCommand] Failed to reopen workspace:', error);
|
|
601
|
+
await message.reply({
|
|
602
|
+
text: `❌ Failed to reopen project in account <b>${(0, telegramFormatter_1.escapeHtml)(selectedAccount)}</b>: ${(0, telegramFormatter_1.escapeHtml)(error?.message || String(error))}`,
|
|
603
|
+
}).catch(logger_1.logger.error);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleJoin = handleJoin;
|
|
4
|
+
exports.handleTelegramJoinSelect = handleTelegramJoinSelect;
|
|
5
|
+
exports.handleMirror = handleMirror;
|
|
6
|
+
const cdpBridgeManager_1 = require("../services/cdpBridgeManager");
|
|
7
|
+
const responseMonitor_1 = require("../services/responseMonitor");
|
|
8
|
+
const sessionPickerUi_1 = require("../ui/sessionPickerUi");
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const telegramFormatter_1 = require("../platform/telegram/telegramFormatter");
|
|
11
|
+
const telegramProjectCommand_1 = require("./telegramProjectCommand");
|
|
12
|
+
const accountUtils_1 = require("../utils/accountUtils");
|
|
13
|
+
const activeResponseMonitors = new Map();
|
|
14
|
+
function resolveAccount(deps, chatId, userId) {
|
|
15
|
+
return (0, accountUtils_1.resolveScopedAccountName)({
|
|
16
|
+
channelId: chatId,
|
|
17
|
+
userId,
|
|
18
|
+
selectedAccountByChannel: deps.bridge.selectedAccountByChannel,
|
|
19
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
20
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
21
|
+
accounts: deps.antigravityAccounts,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function handleJoin(deps, message) {
|
|
25
|
+
const binding = deps.telegramBindingRepo?.findByChatIdWithParentFallback(message.channel.id);
|
|
26
|
+
if (!binding) {
|
|
27
|
+
await message.reply({ text: '⚠️ No project is linked to this chat. Use /project first, or /project_reopen if this is a previously used session.' }).catch(logger_1.logger.error);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const resolvedWorkspacePath = deps.workspaceService
|
|
31
|
+
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
32
|
+
: binding.workspacePath;
|
|
33
|
+
const account = resolveAccount(deps, message.channel.id, message.author.id);
|
|
34
|
+
try {
|
|
35
|
+
const cdp = await deps.bridge.pool.getOrConnect(resolvedWorkspacePath, { name: account });
|
|
36
|
+
if (!deps.chatSessionService) {
|
|
37
|
+
await message.reply({ text: 'Chat session service not available.' }).catch(logger_1.logger.error);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const sessions = await deps.chatSessionService.listAllSessions(cdp);
|
|
41
|
+
if (sessions.length === 0) {
|
|
42
|
+
await message.reply({ text: 'No active sessions found in this project.' }).catch(logger_1.logger.error);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const ui = (0, sessionPickerUi_1.buildSessionPickerPayload)(sessions);
|
|
46
|
+
await message.reply(ui).catch(logger_1.logger.error);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
await message.reply({ text: `⚠️ Failed to connect to project: ${e.message}` }).catch(logger_1.logger.error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function handleTelegramJoinSelect(deps, interaction) {
|
|
53
|
+
const selectedTitle = interaction.values[0];
|
|
54
|
+
const originalChannelId = interaction.channel.id;
|
|
55
|
+
const binding = deps.telegramBindingRepo?.findByChatId(originalChannelId);
|
|
56
|
+
if (!binding) {
|
|
57
|
+
await interaction.update({ text: '⚠️ No project is bound to this chat.' }).catch(logger_1.logger.error);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const resolvedWorkspacePath = deps.workspaceService
|
|
61
|
+
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
62
|
+
: binding.workspacePath;
|
|
63
|
+
const account = resolveAccount(deps, interaction.channel.id, interaction.user.id);
|
|
64
|
+
let cdp;
|
|
65
|
+
try {
|
|
66
|
+
cdp = await deps.bridge.pool.getOrConnect(resolvedWorkspacePath, { name: account });
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
await interaction.update({ text: `⚠️ Failed to connect to project: ${e.message}` }).catch(logger_1.logger.error);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!deps.chatSessionService) {
|
|
73
|
+
await interaction.update({ text: 'Chat session service not available.' }).catch(logger_1.logger.error);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const activateResult = await deps.chatSessionService.activateSessionByTitle(cdp, selectedTitle);
|
|
77
|
+
if (!activateResult.ok) {
|
|
78
|
+
await interaction.update({ text: `⚠️ Failed to join session: ${activateResult.error}` }).catch(logger_1.logger.error);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let targetChannelId = originalChannelId;
|
|
82
|
+
if (deps.botApi && deps.bridge && deps.telegramBindingRepo) {
|
|
83
|
+
targetChannelId = await (0, telegramProjectCommand_1.tryCreateTopicAndBind)(deps.botApi, originalChannelId, binding.workspacePath, deps.telegramBindingRepo, deps.bridge.pool);
|
|
84
|
+
}
|
|
85
|
+
const replyMsg = targetChannelId !== originalChannelId
|
|
86
|
+
? `✅ Joined session in new topic: <b>${(0, telegramFormatter_1.escapeHtml)(selectedTitle)}</b>\nUse /mirror if you want to forward PC messages here.`
|
|
87
|
+
: `✅ Joined session: <b>${(0, telegramFormatter_1.escapeHtml)(selectedTitle)}</b>\nUse /mirror if you want to forward PC messages here.`;
|
|
88
|
+
await interaction.update({ text: replyMsg }).catch(logger_1.logger.error);
|
|
89
|
+
}
|
|
90
|
+
async function handleMirror(deps, message) {
|
|
91
|
+
const binding = deps.telegramBindingRepo?.findByChatIdWithParentFallback(message.channel.id);
|
|
92
|
+
if (!binding) {
|
|
93
|
+
await message.reply({ text: '⚠️ No project is linked to this chat. Use /project first, or /project_reopen if this is a previously used session.' }).catch(logger_1.logger.error);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const resolvedWorkspacePath = deps.workspaceService
|
|
97
|
+
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
98
|
+
: binding.workspacePath;
|
|
99
|
+
const projectName = deps.bridge.pool.extractProjectName(resolvedWorkspacePath);
|
|
100
|
+
const account = resolveAccount(deps, message.channel.id, message.author.id);
|
|
101
|
+
const detector = deps.bridge.pool.getUserMessageDetector(projectName, account);
|
|
102
|
+
if (detector?.isActive()) {
|
|
103
|
+
detector.stop();
|
|
104
|
+
const responseMonitor = activeResponseMonitors.get(resolvedWorkspacePath);
|
|
105
|
+
if (responseMonitor?.isActive()) {
|
|
106
|
+
await responseMonitor.stop();
|
|
107
|
+
activeResponseMonitors.delete(resolvedWorkspacePath);
|
|
108
|
+
}
|
|
109
|
+
await message.reply({ text: '📡 Mirroring OFF\nPC-to-Telegram message mirroring has been stopped.' }).catch(logger_1.logger.error);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
let cdp;
|
|
113
|
+
try {
|
|
114
|
+
cdp = await deps.bridge.pool.getOrConnect(resolvedWorkspacePath, { name: account });
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
await message.reply({ text: `⚠️ Failed to connect to project: ${e.message}` }).catch(logger_1.logger.error);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const existing = deps.bridge.pool.getUserMessageDetector(projectName, account);
|
|
121
|
+
if (existing?.isActive()) {
|
|
122
|
+
existing.stop();
|
|
123
|
+
}
|
|
124
|
+
(0, cdpBridgeManager_1.ensureUserMessageDetector)(deps.bridge, cdp, projectName, (info) => {
|
|
125
|
+
routeMirroredMessage(deps, cdp, resolvedWorkspacePath, info, message.channel).catch((err) => {
|
|
126
|
+
logger_1.logger.error('[TelegramMirror] Error routing mirrored message:', err);
|
|
127
|
+
});
|
|
128
|
+
}, account);
|
|
129
|
+
await message.reply({ text: '📡 Mirroring ON\nMessages typed in Antigravity on your PC will now appear here.' }).catch(logger_1.logger.error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function routeMirroredMessage(deps, cdp, workspacePath, info, channel) {
|
|
133
|
+
const chatTitle = await (0, cdpBridgeManager_1.getCurrentChatTitle)(cdp);
|
|
134
|
+
await channel.send({
|
|
135
|
+
text: `🖥️ <b>User typed in Antigravity:</b>\n<pre>${(0, telegramFormatter_1.escapeHtml)(info.text)}</pre>\n<i>Session: ${(0, telegramFormatter_1.escapeHtml)(chatTitle || 'Unknown')}</i>`
|
|
136
|
+
}).catch((err) => logger_1.logger.error('[TelegramMirror] Failed to send user message:', err));
|
|
137
|
+
startResponseMirror(deps, cdp, workspacePath, channel, chatTitle || 'Unknown');
|
|
138
|
+
}
|
|
139
|
+
function startResponseMirror(deps, cdp, workspacePath, channel, chatTitle) {
|
|
140
|
+
const prev = activeResponseMonitors.get(workspacePath);
|
|
141
|
+
if (prev?.isActive()) {
|
|
142
|
+
prev.stop().catch(() => { });
|
|
143
|
+
}
|
|
144
|
+
const monitor = new responseMonitor_1.ResponseMonitor({
|
|
145
|
+
cdpService: cdp,
|
|
146
|
+
pollIntervalMs: 2000,
|
|
147
|
+
maxDurationMs: 300000,
|
|
148
|
+
extractionMode: deps.extractionMode,
|
|
149
|
+
onComplete: (finalText) => {
|
|
150
|
+
activeResponseMonitors.delete(workspacePath);
|
|
151
|
+
if (!finalText || finalText.trim().length === 0)
|
|
152
|
+
return;
|
|
153
|
+
const maxLen = 3000;
|
|
154
|
+
const text = finalText.length > maxLen
|
|
155
|
+
? finalText.slice(0, maxLen) + '\n...(truncated)'
|
|
156
|
+
: finalText;
|
|
157
|
+
channel.send({
|
|
158
|
+
text: `🤖 <b>Antigravity Response:</b>\n${(0, telegramFormatter_1.escapeHtml)(text)}\n\n<i>Session: ${(0, telegramFormatter_1.escapeHtml)(chatTitle)}</i>`
|
|
159
|
+
}).catch((err) => logger_1.logger.error('[TelegramMirror] Failed to send AI response:', err));
|
|
160
|
+
},
|
|
161
|
+
onTimeout: () => {
|
|
162
|
+
activeResponseMonitors.delete(workspacePath);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
activeResponseMonitors.set(workspacePath, monitor);
|
|
166
|
+
monitor.startPassive().catch((err) => {
|
|
167
|
+
logger_1.logger.error('[TelegramMirror] Failed to start response monitor:', err);
|
|
168
|
+
activeResponseMonitors.delete(workspacePath);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
@@ -22,6 +22,7 @@ const defaultModelApplicator_1 = require("../services/defaultModelApplicator");
|
|
|
22
22
|
const logger_1 = require("../utils/logger");
|
|
23
23
|
const telegramImageHandler_1 = require("../utils/telegramImageHandler");
|
|
24
24
|
const imageHandler_1 = require("../utils/imageHandler");
|
|
25
|
+
const accountUtils_1 = require("../utils/accountUtils");
|
|
25
26
|
/**
|
|
26
27
|
* Create a handler for Telegram messages.
|
|
27
28
|
* Returns an async function that processes a single PlatformMessage.
|
|
@@ -29,6 +30,16 @@ const imageHandler_1 = require("../utils/imageHandler");
|
|
|
29
30
|
function createTelegramMessageHandler(deps) {
|
|
30
31
|
// Per-workspace prompt queue to serialize messages
|
|
31
32
|
const workspaceQueues = new Map();
|
|
33
|
+
function resolveAccount(chatId, userId) {
|
|
34
|
+
return (0, accountUtils_1.resolveScopedAccountName)({
|
|
35
|
+
channelId: chatId,
|
|
36
|
+
userId,
|
|
37
|
+
selectedAccountByChannel: deps.bridge.selectedAccountByChannel,
|
|
38
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
39
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
40
|
+
accounts: deps.antigravityAccounts,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
function enqueueForWorkspace(workspacePath, task) {
|
|
33
44
|
const current = (workspaceQueues.get(workspacePath) ?? Promise.resolve()).catch(() => { });
|
|
34
45
|
const next = current.then(async () => {
|
|
@@ -65,6 +76,10 @@ function createTelegramMessageHandler(deps) {
|
|
|
65
76
|
fetchQuota: deps.fetchQuota,
|
|
66
77
|
activeMonitors: deps.activeMonitors,
|
|
67
78
|
chatSessionService: deps.chatSessionService,
|
|
79
|
+
accountPrefRepo: deps.accountPrefRepo,
|
|
80
|
+
channelPrefRepo: deps.channelPrefRepo,
|
|
81
|
+
antigravityAccounts: deps.antigravityAccounts,
|
|
82
|
+
botApi: deps.botApi,
|
|
68
83
|
}, message, cmd);
|
|
69
84
|
return;
|
|
70
85
|
}
|
|
@@ -77,10 +92,10 @@ function createTelegramMessageHandler(deps) {
|
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
94
|
// Resolve workspace binding for this Telegram chat
|
|
80
|
-
const binding = deps.telegramBindingRepo.
|
|
95
|
+
const binding = deps.telegramBindingRepo.findByChatIdWithParentFallback(chatId);
|
|
81
96
|
if (!binding) {
|
|
82
97
|
await message.reply({
|
|
83
|
-
text: 'No project is linked to this chat. Use /project to bind a workspace.',
|
|
98
|
+
text: 'No project is linked to this chat. Use /project to bind a workspace, or /project_reopen if this is a previously used session.',
|
|
84
99
|
}).catch(logger_1.logger.error);
|
|
85
100
|
return;
|
|
86
101
|
}
|
|
@@ -91,11 +106,13 @@ function createTelegramMessageHandler(deps) {
|
|
|
91
106
|
? deps.workspaceService.getWorkspacePath(binding.workspacePath)
|
|
92
107
|
: binding.workspacePath;
|
|
93
108
|
await enqueueForWorkspace(workspacePath, async () => {
|
|
109
|
+
const selectedAccount = resolveAccount(chatId, message.author.id);
|
|
110
|
+
deps.bridge.selectedAccountByChannel?.set(chatId, selectedAccount);
|
|
94
111
|
const cdpStartTime = Date.now();
|
|
95
112
|
logger_1.logger.debug(`[TelegramHandler] getOrConnect start (elapsed=${cdpStartTime - handlerEntryTime}ms)`);
|
|
96
113
|
let cdp;
|
|
97
114
|
try {
|
|
98
|
-
cdp = await deps.bridge.pool.getOrConnect(workspacePath);
|
|
115
|
+
cdp = await deps.bridge.pool.getOrConnect(workspacePath, { name: selectedAccount });
|
|
99
116
|
}
|
|
100
117
|
catch (e) {
|
|
101
118
|
await message.reply({
|
|
@@ -131,10 +148,10 @@ function createTelegramMessageHandler(deps) {
|
|
|
131
148
|
}
|
|
132
149
|
}
|
|
133
150
|
// Start detectors (platform-agnostic now)
|
|
134
|
-
(0, cdpBridgeManager_1.ensureApprovalDetector)(deps.bridge, cdp, projectName);
|
|
135
|
-
(0, cdpBridgeManager_1.ensureErrorPopupDetector)(deps.bridge, cdp, projectName);
|
|
136
|
-
(0, cdpBridgeManager_1.ensurePlanningDetector)(deps.bridge, cdp, projectName);
|
|
137
|
-
(0, cdpBridgeManager_1.ensureRunCommandDetector)(deps.bridge, cdp, projectName);
|
|
151
|
+
(0, cdpBridgeManager_1.ensureApprovalDetector)(deps.bridge, cdp, projectName, selectedAccount);
|
|
152
|
+
(0, cdpBridgeManager_1.ensureErrorPopupDetector)(deps.bridge, cdp, projectName, selectedAccount);
|
|
153
|
+
(0, cdpBridgeManager_1.ensurePlanningDetector)(deps.bridge, cdp, projectName, selectedAccount);
|
|
154
|
+
(0, cdpBridgeManager_1.ensureRunCommandDetector)(deps.bridge, cdp, projectName, selectedAccount);
|
|
138
155
|
// Acknowledge receipt
|
|
139
156
|
await message.react('\u{1F440}').catch(() => { });
|
|
140
157
|
// Download image attachments if present
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Telegram /project command handler.
|
|
4
|
-
*
|
|
5
|
-
* Allows users to bind a Telegram chat to an Antigravity workspace
|
|
6
|
-
* via inline keyboard buttons, similar to Discord's /project slash command.
|
|
7
|
-
*
|
|
8
|
-
* User flow:
|
|
9
|
-
* /project → show workspace list as buttons → user taps → chat bound
|
|
10
|
-
* /project list → show workspace list (same as bare /project)
|
|
11
|
-
* /project unbind → remove current binding
|
|
12
|
-
*/
|
|
13
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
3
|
exports.TG_PROJECT_SELECT_ID = void 0;
|
|
15
4
|
exports.parseTelegramProjectCommand = parseTelegramProjectCommand;
|
|
16
5
|
exports.handleTelegramProjectCommand = handleTelegramProjectCommand;
|
|
6
|
+
exports.tryCreateTopicAndBind = tryCreateTopicAndBind;
|
|
17
7
|
exports.handleTelegramProjectSelect = handleTelegramProjectSelect;
|
|
18
8
|
exports.createTelegramSelectHandler = createTelegramSelectHandler;
|
|
19
9
|
const logger_1 = require("../utils/logger");
|
|
@@ -45,6 +35,7 @@ function parseTelegramProjectCommand(text) {
|
|
|
45
35
|
// Default (no subcommand or "list") → show workspace list
|
|
46
36
|
return { subcommand: 'list' };
|
|
47
37
|
}
|
|
38
|
+
const telegramFormatter_1 = require("../platform/telegram/telegramFormatter");
|
|
48
39
|
// ---------------------------------------------------------------------------
|
|
49
40
|
// Command handler
|
|
50
41
|
// ---------------------------------------------------------------------------
|
|
@@ -96,6 +87,55 @@ async function handleTelegramProjectCommand(deps, message, parsed) {
|
|
|
96
87
|
/**
|
|
97
88
|
* Handle a workspace selection callback from inline keyboard.
|
|
98
89
|
*/
|
|
90
|
+
async function tryCreateTopicAndBind(botApi, originalChannelId, workspacePath, telegramBindingRepo, pool) {
|
|
91
|
+
const baseChatId = originalChannelId.split('_')[0];
|
|
92
|
+
const isExistingTopic = originalChannelId.includes('_');
|
|
93
|
+
if (isExistingTopic) {
|
|
94
|
+
telegramBindingRepo.upsert({
|
|
95
|
+
chatId: originalChannelId,
|
|
96
|
+
workspacePath,
|
|
97
|
+
});
|
|
98
|
+
return originalChannelId;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const chat = await botApi.getChat(baseChatId);
|
|
102
|
+
logger_1.logger.debug(`[Telegram] getChat(${baseChatId}) returned:`, chat);
|
|
103
|
+
if (chat?.is_forum) {
|
|
104
|
+
const projectName = pool.extractProjectName(workspacePath) || 'Project';
|
|
105
|
+
const topicName = `[Session] ${projectName}`.substring(0, 128);
|
|
106
|
+
const topic = await botApi.createForumTopic(baseChatId, topicName);
|
|
107
|
+
const threadId = topic.message_thread_id;
|
|
108
|
+
const newChannelId = `${baseChatId}_${threadId}`;
|
|
109
|
+
telegramBindingRepo.upsert({
|
|
110
|
+
chatId: newChannelId,
|
|
111
|
+
workspacePath,
|
|
112
|
+
});
|
|
113
|
+
await botApi.sendMessage(baseChatId, `✅ <b>${(0, telegramFormatter_1.escapeHtml)(projectName)}</b> session started in this topic.`, {
|
|
114
|
+
message_thread_id: threadId,
|
|
115
|
+
parse_mode: 'HTML'
|
|
116
|
+
}).catch((e) => logger_1.logger.warn('Failed to send welcome message to new topic', e));
|
|
117
|
+
return newChannelId;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
logger_1.logger.debug(`[Telegram] Could not create forum topic for chat ${baseChatId}`, error);
|
|
122
|
+
// If the error is specifically a permissions error for creating topics
|
|
123
|
+
const errStr = String(error);
|
|
124
|
+
if (errStr.includes('not enough rights') || errStr.includes('400')) {
|
|
125
|
+
await botApi.sendMessage(baseChatId, '⚠️ <b>Permission Error:</b> I do not have permission to create a Topic (Forum) in this group.\n\n' +
|
|
126
|
+
'Please follow these steps to fix:\n' +
|
|
127
|
+
'1. Go to Group Info -> Edit -> Administrators\n' +
|
|
128
|
+
'2. Select this bot (<code>@' + (botApi.me?.username || 'bot') + '</code>)\n' +
|
|
129
|
+
'3. Enable the <b>"Manage Topics"</b> (or "Change Group Info") permission\n' +
|
|
130
|
+
'4. Try binding the project or using /new again.', { parse_mode: 'HTML' }).catch((e) => logger_1.logger.warn('Failed to send permission error message', e));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
telegramBindingRepo.upsert({
|
|
134
|
+
chatId: originalChannelId,
|
|
135
|
+
workspacePath,
|
|
136
|
+
});
|
|
137
|
+
return originalChannelId;
|
|
138
|
+
}
|
|
99
139
|
async function handleTelegramProjectSelect(deps, interaction) {
|
|
100
140
|
const selectedWorkspace = interaction.values[0];
|
|
101
141
|
if (!selectedWorkspace)
|
|
@@ -109,13 +149,26 @@ async function handleTelegramProjectSelect(deps, interaction) {
|
|
|
109
149
|
}).catch(logger_1.logger.error);
|
|
110
150
|
return;
|
|
111
151
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
152
|
+
let finalChannelId = chatId;
|
|
153
|
+
if (deps.botApi && deps.bridge) {
|
|
154
|
+
finalChannelId = await tryCreateTopicAndBind(deps.botApi, chatId, selectedWorkspace, deps.telegramBindingRepo, deps.bridge.pool);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
deps.telegramBindingRepo.upsert({
|
|
158
|
+
chatId,
|
|
159
|
+
workspacePath: selectedWorkspace,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (finalChannelId !== chatId) {
|
|
163
|
+
await interaction.update({
|
|
164
|
+
text: `✅ Workspace bound to new topic: <b>${(0, telegramFormatter_1.escapeHtml)(selectedWorkspace)}</b>`,
|
|
165
|
+
}).catch(logger_1.logger.error);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
await interaction.update({
|
|
169
|
+
text: `Workspace bound: <b>${(0, telegramFormatter_1.escapeHtml)(selectedWorkspace)}</b>\nSend a message to start chatting with Antigravity.`,
|
|
170
|
+
}).catch(logger_1.logger.error);
|
|
171
|
+
}
|
|
119
172
|
logger_1.logger.info(`[TelegramProject] Chat ${chatId} bound to workspace: ${selectedWorkspace}`);
|
|
120
173
|
}
|
|
121
174
|
// ---------------------------------------------------------------------------
|