lazy-gravity 0.6.1 → 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 +416 -45
- package/dist/bot/telegramCommands.js +175 -48
- package/dist/bot/telegramJoinCommand.js +170 -0
- package/dist/bot/telegramMessageHandler.js +27 -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/responseMonitor.js +235 -48
- 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
package/dist/bot/index.js
CHANGED
|
@@ -38,6 +38,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.startBot = exports.getResponseDeliveryModeForTest = void 0;
|
|
40
40
|
exports.createSerialTaskQueueForTest = createSerialTaskQueueForTest;
|
|
41
|
+
exports.handleSlashInteraction = handleSlashInteraction;
|
|
42
|
+
const sessionPickerUi_1 = require("../ui/sessionPickerUi");
|
|
43
|
+
const telegramJoinCommand_1 = require("./telegramJoinCommand");
|
|
41
44
|
const i18n_1 = require("../utils/i18n");
|
|
42
45
|
const logger_1 = require("../utils/logger");
|
|
43
46
|
const logBuffer_1 = require("../utils/logBuffer");
|
|
@@ -51,7 +54,9 @@ const modeService_1 = require("../services/modeService");
|
|
|
51
54
|
const modelService_1 = require("../services/modelService");
|
|
52
55
|
const defaultModelApplicator_1 = require("../services/defaultModelApplicator");
|
|
53
56
|
const templateRepository_1 = require("../database/templateRepository");
|
|
57
|
+
const accountPreferenceRepository_1 = require("../database/accountPreferenceRepository");
|
|
54
58
|
const workspaceBindingRepository_1 = require("../database/workspaceBindingRepository");
|
|
59
|
+
const channelPreferenceRepository_1 = require("../database/channelPreferenceRepository");
|
|
55
60
|
const chatSessionRepository_1 = require("../database/chatSessionRepository");
|
|
56
61
|
const workspaceService_1 = require("../services/workspaceService");
|
|
57
62
|
const workspaceCommandHandler_1 = require("../commands/workspaceCommandHandler");
|
|
@@ -60,6 +65,8 @@ const cleanupCommandHandler_1 = require("../commands/cleanupCommandHandler");
|
|
|
60
65
|
const channelManager_1 = require("../services/channelManager");
|
|
61
66
|
const titleGeneratorService_1 = require("../services/titleGeneratorService");
|
|
62
67
|
const joinCommandHandler_1 = require("../commands/joinCommandHandler");
|
|
68
|
+
// CDP integration services
|
|
69
|
+
const cdpService_1 = require("../services/cdpService");
|
|
63
70
|
const chatSessionService_1 = require("../services/chatSessionService");
|
|
64
71
|
const responseMonitor_1 = require("../services/responseMonitor");
|
|
65
72
|
const antigravityLauncher_1 = require("../services/antigravityLauncher");
|
|
@@ -74,9 +81,11 @@ const modeUi_1 = require("../ui/modeUi");
|
|
|
74
81
|
const modelsUi_1 = require("../ui/modelsUi");
|
|
75
82
|
const templateUi_1 = require("../ui/templateUi");
|
|
76
83
|
const autoAcceptUi_1 = require("../ui/autoAcceptUi");
|
|
84
|
+
const accountUi_1 = require("../ui/accountUi");
|
|
77
85
|
const outputUi_1 = require("../ui/outputUi");
|
|
78
86
|
const screenshotUi_1 = require("../ui/screenshotUi");
|
|
79
87
|
const userPreferenceRepository_1 = require("../database/userPreferenceRepository");
|
|
88
|
+
const accountUtils_1 = require("../utils/accountUtils");
|
|
80
89
|
const plainTextFormatter_1 = require("../utils/plainTextFormatter");
|
|
81
90
|
const interactionCreateHandler_1 = require("../events/interactionCreateHandler");
|
|
82
91
|
const messageCreateHandler_1 = require("../events/messageCreateHandler");
|
|
@@ -97,6 +106,8 @@ const modelButtonAction_1 = require("../handlers/modelButtonAction");
|
|
|
97
106
|
const autoAcceptButtonAction_1 = require("../handlers/autoAcceptButtonAction");
|
|
98
107
|
const templateButtonAction_1 = require("../handlers/templateButtonAction");
|
|
99
108
|
const modeSelectAction_1 = require("../handlers/modeSelectAction");
|
|
109
|
+
const accountSelectAction_1 = require("../handlers/accountSelectAction");
|
|
110
|
+
const telegramStartupTarget_1 = require("./telegramStartupTarget");
|
|
100
111
|
const channelManager_2 = require("../services/channelManager");
|
|
101
112
|
/**
|
|
102
113
|
* Normalize a candidate startup channel name for preference checks.
|
|
@@ -184,13 +195,19 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
184
195
|
const enqueueGeneral = createSerialTaskQueueForTest('general', monitorTraceId);
|
|
185
196
|
const enqueueResponse = createSerialTaskQueueForTest('response', monitorTraceId);
|
|
186
197
|
const enqueueActivity = createSerialTaskQueueForTest('activity', monitorTraceId);
|
|
198
|
+
const logDeliveryError = (scope, error) => {
|
|
199
|
+
const messageText = error instanceof Error ? error.message : String(error);
|
|
200
|
+
logger_1.logger.warn(`[DiscordDelivery:${monitorTraceId}] ${scope} failed: ${messageText}`);
|
|
201
|
+
};
|
|
187
202
|
const sendEmbed = (title, description, color, fields, footerText) => enqueueGeneral(async () => {
|
|
188
203
|
if (!channel)
|
|
189
204
|
return;
|
|
190
205
|
if (outputFormat === 'plain') {
|
|
191
206
|
const chunks = (0, plainTextFormatter_1.formatAsPlainText)({ title, description, fields, footerText });
|
|
192
207
|
for (const chunk of chunks) {
|
|
193
|
-
await channel.send({ content: chunk }).catch(() => {
|
|
208
|
+
await channel.send({ content: chunk }).catch((error) => {
|
|
209
|
+
logDeliveryError('sendEmbed/plain/send', error);
|
|
210
|
+
});
|
|
194
211
|
}
|
|
195
212
|
return;
|
|
196
213
|
}
|
|
@@ -205,7 +222,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
205
222
|
if (footerText) {
|
|
206
223
|
embed.setFooter({ text: footerText });
|
|
207
224
|
}
|
|
208
|
-
await channel.send({ embeds: [embed] }).catch(() => {
|
|
225
|
+
await channel.send({ embeds: [embed] }).catch((error) => {
|
|
226
|
+
logDeliveryError('sendEmbed/embed/send', error);
|
|
227
|
+
});
|
|
209
228
|
}, 'send-embed');
|
|
210
229
|
const shouldTryGeneratedImages = (inputPrompt, responseText) => {
|
|
211
230
|
const prompt = (inputPrompt || '').toLowerCase();
|
|
@@ -238,7 +257,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
238
257
|
await channel.send({
|
|
239
258
|
content: (0, i18n_1.t)(`🖼️ Detected generated images (${files.length})`),
|
|
240
259
|
files,
|
|
241
|
-
}).catch(() => {
|
|
260
|
+
}).catch((error) => {
|
|
261
|
+
logDeliveryError('sendGeneratedImages/send', error);
|
|
262
|
+
});
|
|
242
263
|
}, 'send-generated-images');
|
|
243
264
|
};
|
|
244
265
|
const tryEmergencyExtractText = async () => {
|
|
@@ -322,7 +343,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
322
343
|
// Apply default model preference on CDP connect
|
|
323
344
|
const defaultModelResult = await (0, defaultModelApplicator_1.applyDefaultModel)(cdp, modelService);
|
|
324
345
|
if (defaultModelResult.stale && defaultModelResult.staleMessage && channel) {
|
|
325
|
-
await channel.send(defaultModelResult.staleMessage).catch(() => {
|
|
346
|
+
await channel.send(defaultModelResult.staleMessage).catch((error) => {
|
|
347
|
+
logDeliveryError('defaultModelResult/send', error);
|
|
348
|
+
});
|
|
326
349
|
}
|
|
327
350
|
const localMode = modeService.getCurrentMode();
|
|
328
351
|
const modeName = modeService_1.MODE_UI_NAMES[localMode] || localMode;
|
|
@@ -383,11 +406,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
383
406
|
lastLiveResponseKey = renderKey;
|
|
384
407
|
for (let i = 0; i < plainChunks.length; i++) {
|
|
385
408
|
if (!liveResponseMessages[i]) {
|
|
386
|
-
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch(() =>
|
|
409
|
+
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch((error) => {
|
|
410
|
+
logDeliveryError('liveResponse/plain/send', error);
|
|
411
|
+
return null;
|
|
412
|
+
});
|
|
387
413
|
continue;
|
|
388
414
|
}
|
|
389
|
-
await liveResponseMessages[i].edit({ content: plainChunks[i] }).catch(async () => {
|
|
390
|
-
|
|
415
|
+
await liveResponseMessages[i].edit({ content: plainChunks[i] }).catch(async (error) => {
|
|
416
|
+
logDeliveryError('liveResponse/plain/edit', error);
|
|
417
|
+
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch((sendError) => {
|
|
418
|
+
logDeliveryError('liveResponse/plain/resend', sendError);
|
|
419
|
+
return null;
|
|
420
|
+
});
|
|
391
421
|
});
|
|
392
422
|
}
|
|
393
423
|
while (liveResponseMessages.length > plainChunks.length) {
|
|
@@ -412,11 +442,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
412
442
|
.setFooter({ text: footerText })
|
|
413
443
|
.setTimestamp();
|
|
414
444
|
if (!liveResponseMessages[i]) {
|
|
415
|
-
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch(() =>
|
|
445
|
+
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch((error) => {
|
|
446
|
+
logDeliveryError('liveResponse/embed/send', error);
|
|
447
|
+
return null;
|
|
448
|
+
});
|
|
416
449
|
continue;
|
|
417
450
|
}
|
|
418
|
-
await liveResponseMessages[i].edit({ embeds: [embed] }).catch(async () => {
|
|
419
|
-
|
|
451
|
+
await liveResponseMessages[i].edit({ embeds: [embed] }).catch(async (error) => {
|
|
452
|
+
logDeliveryError('liveResponse/embed/edit', error);
|
|
453
|
+
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch((sendError) => {
|
|
454
|
+
logDeliveryError('liveResponse/embed/resend', sendError);
|
|
455
|
+
return null;
|
|
456
|
+
});
|
|
420
457
|
});
|
|
421
458
|
}
|
|
422
459
|
// Delete excess messages if page count decreased
|
|
@@ -444,11 +481,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
444
481
|
lastLiveActivityKey = renderKey;
|
|
445
482
|
for (let i = 0; i < plainChunks.length; i++) {
|
|
446
483
|
if (!liveActivityMessages[i]) {
|
|
447
|
-
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch(() =>
|
|
484
|
+
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch((error) => {
|
|
485
|
+
logDeliveryError('liveActivity/plain/send', error);
|
|
486
|
+
return null;
|
|
487
|
+
});
|
|
448
488
|
continue;
|
|
449
489
|
}
|
|
450
|
-
await liveActivityMessages[i].edit({ content: plainChunks[i] }).catch(async () => {
|
|
451
|
-
|
|
490
|
+
await liveActivityMessages[i].edit({ content: plainChunks[i] }).catch(async (error) => {
|
|
491
|
+
logDeliveryError('liveActivity/plain/edit', error);
|
|
492
|
+
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch((sendError) => {
|
|
493
|
+
logDeliveryError('liveActivity/plain/resend', sendError);
|
|
494
|
+
return null;
|
|
495
|
+
});
|
|
452
496
|
});
|
|
453
497
|
}
|
|
454
498
|
while (liveActivityMessages.length > plainChunks.length) {
|
|
@@ -473,11 +517,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
473
517
|
.setFooter({ text: footerText })
|
|
474
518
|
.setTimestamp();
|
|
475
519
|
if (!liveActivityMessages[i]) {
|
|
476
|
-
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch(() =>
|
|
520
|
+
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch((error) => {
|
|
521
|
+
logDeliveryError('liveActivity/embed/send', error);
|
|
522
|
+
return null;
|
|
523
|
+
});
|
|
477
524
|
continue;
|
|
478
525
|
}
|
|
479
|
-
await liveActivityMessages[i].edit({ embeds: [embed] }).catch(async () => {
|
|
480
|
-
|
|
526
|
+
await liveActivityMessages[i].edit({ embeds: [embed] }).catch(async (error) => {
|
|
527
|
+
logDeliveryError('liveActivity/embed/edit', error);
|
|
528
|
+
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch((sendError) => {
|
|
529
|
+
logDeliveryError('liveActivity/embed/resend', sendError);
|
|
530
|
+
return null;
|
|
531
|
+
});
|
|
481
532
|
});
|
|
482
533
|
}
|
|
483
534
|
while (liveActivityMessages.length > descriptions.length) {
|
|
@@ -488,6 +539,7 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
488
539
|
}
|
|
489
540
|
}, `upsert-activity:${opts?.source ?? 'unknown'}`);
|
|
490
541
|
try {
|
|
542
|
+
const baseline = await (0, responseMonitor_1.captureResponseMonitorBaseline)(cdp);
|
|
491
543
|
logger_1.logger.prompt(prompt);
|
|
492
544
|
let injectResult;
|
|
493
545
|
if (inboundImages.length > 0) {
|
|
@@ -516,6 +568,8 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
516
568
|
maxDurationMs: options?.responseTimeoutMs,
|
|
517
569
|
stopGoneConfirmCount: 3,
|
|
518
570
|
extractionMode: options?.extractionMode,
|
|
571
|
+
initialBaselineText: baseline.text,
|
|
572
|
+
initialSeenProcessLogKeys: baseline.processLogKeys,
|
|
519
573
|
onPhaseChange: (_phase, _text) => {
|
|
520
574
|
// Phase transitions are already logged inside ResponseMonitor.setPhase()
|
|
521
575
|
},
|
|
@@ -578,7 +632,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
578
632
|
try {
|
|
579
633
|
const modelsPayload = await (0, modelsUi_1.buildModelsUI)(cdp, () => bridge.quota.fetchQuota());
|
|
580
634
|
if (modelsPayload && channel) {
|
|
581
|
-
await channel.send({ ...modelsPayload })
|
|
635
|
+
await channel.send({ ...modelsPayload }).catch((error) => {
|
|
636
|
+
logDeliveryError('quota/modelsPayload/send', error);
|
|
637
|
+
});
|
|
582
638
|
}
|
|
583
639
|
}
|
|
584
640
|
catch (e) {
|
|
@@ -741,6 +797,8 @@ const startBot = async (cliLogLevel) => {
|
|
|
741
797
|
const modelService = new modelService_1.ModelService();
|
|
742
798
|
const templateRepo = new templateRepository_1.TemplateRepository(db);
|
|
743
799
|
const userPrefRepo = new userPreferenceRepository_1.UserPreferenceRepository(db);
|
|
800
|
+
const accountPrefRepo = new accountPreferenceRepository_1.AccountPreferenceRepository(db);
|
|
801
|
+
const channelPrefRepo = new channelPreferenceRepository_1.ChannelPreferenceRepository(db);
|
|
744
802
|
// Eagerly load default model from DB (single-user bot optimization)
|
|
745
803
|
try {
|
|
746
804
|
const firstUser = db.prepare('SELECT user_id FROM user_preferences LIMIT 1').get();
|
|
@@ -759,7 +817,11 @@ const startBot = async (cliLogLevel) => {
|
|
|
759
817
|
// Auto-launch Antigravity with CDP port if not already running
|
|
760
818
|
await (0, antigravityLauncher_1.ensureAntigravityRunning)();
|
|
761
819
|
// Initialize CDP bridge (lazy connection: pool creation only)
|
|
762
|
-
const
|
|
820
|
+
const accountPorts = Object.fromEntries((config.antigravityAccounts ?? []).map((account) => [account.name, account.cdpPort]));
|
|
821
|
+
const accountUserDataDirs = Object.fromEntries((config.antigravityAccounts ?? [])
|
|
822
|
+
.filter((account) => typeof account.userDataDir === 'string' && account.userDataDir.trim().length > 0)
|
|
823
|
+
.map((account) => [account.name, account.userDataDir.trim()]));
|
|
824
|
+
const bridge = (0, cdpBridgeManager_1.initCdpBridge)(config.autoApproveFileEdits, accountPorts, accountUserDataDirs);
|
|
763
825
|
// Initialize CDP-dependent services (constructor CDP dependency removed)
|
|
764
826
|
const chatSessionService = new chatSessionService_1.ChatSessionService();
|
|
765
827
|
const titleGenerator = new titleGeneratorService_1.TitleGeneratorService();
|
|
@@ -770,8 +832,46 @@ const startBot = async (cliLogLevel) => {
|
|
|
770
832
|
sendPromptImpl: sendPromptToAntigravity,
|
|
771
833
|
});
|
|
772
834
|
// Initialize command handlers (joinHandler is created after client, see below)
|
|
773
|
-
const wsHandler = new workspaceCommandHandler_1.WorkspaceCommandHandler(workspaceBindingRepo, chatSessionRepo, workspaceService, channelManager)
|
|
774
|
-
|
|
835
|
+
const wsHandler = new workspaceCommandHandler_1.WorkspaceCommandHandler(workspaceBindingRepo, chatSessionRepo, workspaceService, channelManager, async (workspaceName, newChannelId, sourceChannelId, userId) => {
|
|
836
|
+
const workspacePath = workspaceService.getWorkspacePath(workspaceName);
|
|
837
|
+
const selectedAccount = (0, accountUtils_1.resolveScopedAccountName)({
|
|
838
|
+
channelId: sourceChannelId,
|
|
839
|
+
userId,
|
|
840
|
+
sessionAccountName: chatSessionRepo.findByChannelId(sourceChannelId)?.activeAccountName ?? null,
|
|
841
|
+
parentChannelId: null,
|
|
842
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
843
|
+
channelPrefRepo,
|
|
844
|
+
accountPrefRepo,
|
|
845
|
+
accounts: config.antigravityAccounts,
|
|
846
|
+
});
|
|
847
|
+
chatSessionRepo.setActiveAccountName(newChannelId, selectedAccount);
|
|
848
|
+
bridge.selectedAccountByChannel?.set(newChannelId, selectedAccount);
|
|
849
|
+
bridge.pool.setPreferredAccountForWorkspace(workspacePath, selectedAccount);
|
|
850
|
+
const cdp = new cdpService_1.CdpService({
|
|
851
|
+
accountName: selectedAccount,
|
|
852
|
+
accountPorts,
|
|
853
|
+
accountUserDataDirs,
|
|
854
|
+
cdpCallTimeout: 15000,
|
|
855
|
+
maxReconnectAttempts: 0,
|
|
856
|
+
});
|
|
857
|
+
try {
|
|
858
|
+
await cdp.openWorkspace(workspacePath);
|
|
859
|
+
}
|
|
860
|
+
finally {
|
|
861
|
+
await cdp.disconnect().catch(() => { });
|
|
862
|
+
}
|
|
863
|
+
await bridge.pool.getOrConnect(workspacePath, { name: selectedAccount });
|
|
864
|
+
});
|
|
865
|
+
const chatHandler = new chatCommandHandler_1.ChatCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, workspaceService, bridge.pool, (channelId, userId) => (0, accountUtils_1.resolveScopedAccountName)({
|
|
866
|
+
channelId,
|
|
867
|
+
userId,
|
|
868
|
+
sessionAccountName: chatSessionRepo.findByChannelId(channelId)?.activeAccountName ?? null,
|
|
869
|
+
parentChannelId: null,
|
|
870
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
871
|
+
channelPrefRepo,
|
|
872
|
+
accountPrefRepo,
|
|
873
|
+
accounts: config.antigravityAccounts,
|
|
874
|
+
}));
|
|
775
875
|
const cleanupHandler = new cleanupCommandHandler_1.CleanupCommandHandler(chatSessionRepo, workspaceBindingRepo);
|
|
776
876
|
const slashCommandHandler = new slashCommandHandler_1.SlashCommandHandler(templateRepo);
|
|
777
877
|
// Discord platform — only initialise the Discord client when the platform is enabled
|
|
@@ -789,7 +889,16 @@ const startBot = async (cliLogLevel) => {
|
|
|
789
889
|
discord_js_1.GatewayIntentBits.MessageContent,
|
|
790
890
|
]
|
|
791
891
|
});
|
|
792
|
-
const joinHandler = new joinCommandHandler_1.JoinCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, bridge.pool, workspaceService, client, config.extractionMode, config.responseTimeoutMs)
|
|
892
|
+
const joinHandler = new joinCommandHandler_1.JoinCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, bridge.pool, workspaceService, client, config.extractionMode, config.responseTimeoutMs, (channelId, userId) => (0, accountUtils_1.resolveScopedAccountName)({
|
|
893
|
+
channelId,
|
|
894
|
+
userId,
|
|
895
|
+
sessionAccountName: chatSessionRepo.findByChannelId(channelId)?.activeAccountName ?? null,
|
|
896
|
+
parentChannelId: null,
|
|
897
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
898
|
+
channelPrefRepo,
|
|
899
|
+
accountPrefRepo,
|
|
900
|
+
accounts: config.antigravityAccounts,
|
|
901
|
+
}));
|
|
793
902
|
client.once(discord_js_1.Events.ClientReady, async (readyClient) => {
|
|
794
903
|
logger_1.logger.info(`Ready! Logged in as ${readyClient.user.tag} | extractionMode=${config.extractionMode}`);
|
|
795
904
|
try {
|
|
@@ -854,7 +963,12 @@ const startBot = async (cliLogLevel) => {
|
|
|
854
963
|
parseRunCommandCustomId: cdpBridgeManager_1.parseRunCommandCustomId,
|
|
855
964
|
joinHandler,
|
|
856
965
|
userPrefRepo,
|
|
857
|
-
|
|
966
|
+
accountPrefRepo,
|
|
967
|
+
channelPrefRepo,
|
|
968
|
+
chatSessionRepo,
|
|
969
|
+
chatSessionService,
|
|
970
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
971
|
+
handleSlashInteraction: async (interaction, handler, bridgeArg, wsHandlerArg, chatHandlerArg, cleanupHandlerArg, modeServiceArg, modelServiceArg, autoAcceptServiceArg, clientArg, accountPrefRepoArg, channelPrefRepoArg, antigravityAccountsArg) => handleSlashInteraction(interaction, handler, bridgeArg, wsHandlerArg, chatHandlerArg, cleanupHandlerArg, chatSessionService, modeServiceArg, modelServiceArg, autoAcceptServiceArg, clientArg, promptDispatcher, templateRepo, joinHandler, userPrefRepo, accountPrefRepoArg, channelPrefRepoArg, antigravityAccountsArg, chatSessionRepo),
|
|
858
972
|
handleTemplateUse: async (interaction, templateId) => {
|
|
859
973
|
const template = templateRepo.findById(templateId);
|
|
860
974
|
if (!template) {
|
|
@@ -870,7 +984,18 @@ const startBot = async (cliLogLevel) => {
|
|
|
870
984
|
let cdp = null;
|
|
871
985
|
if (workspacePath) {
|
|
872
986
|
try {
|
|
873
|
-
|
|
987
|
+
const selectedAccount = (0, accountUtils_1.resolveScopedAccountName)({
|
|
988
|
+
channelId,
|
|
989
|
+
userId: interaction.user.id,
|
|
990
|
+
sessionAccountName: chatSessionRepo.findByChannelId(channelId)?.activeAccountName ?? null,
|
|
991
|
+
parentChannelId: (0, accountUtils_1.inferParentScopeChannelId)(channelId, interaction.channel?.parentId ?? null),
|
|
992
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
993
|
+
channelPrefRepo,
|
|
994
|
+
accountPrefRepo,
|
|
995
|
+
accounts: config.antigravityAccounts,
|
|
996
|
+
});
|
|
997
|
+
bridge.selectedAccountByChannel?.set(channelId, selectedAccount);
|
|
998
|
+
cdp = await bridge.pool.getOrConnect(workspacePath, { name: selectedAccount });
|
|
874
999
|
const projectName = bridge.pool.extractProjectName(workspacePath);
|
|
875
1000
|
bridge.lastActiveWorkspace = projectName;
|
|
876
1001
|
const platformCh = (0, wrappers_1.wrapDiscordChannel)(interaction.channel);
|
|
@@ -880,10 +1005,10 @@ const startBot = async (cliLogLevel) => {
|
|
|
880
1005
|
if (session?.displayName) {
|
|
881
1006
|
(0, cdpBridgeManager_1.registerApprovalSessionChannel)(bridge, projectName, session.displayName, platformCh);
|
|
882
1007
|
}
|
|
883
|
-
(0, cdpBridgeManager_1.ensureApprovalDetector)(bridge, cdp, projectName);
|
|
884
|
-
(0, cdpBridgeManager_1.ensureErrorPopupDetector)(bridge, cdp, projectName);
|
|
885
|
-
(0, cdpBridgeManager_1.ensurePlanningDetector)(bridge, cdp, projectName);
|
|
886
|
-
(0, cdpBridgeManager_1.ensureRunCommandDetector)(bridge, cdp, projectName);
|
|
1008
|
+
(0, cdpBridgeManager_1.ensureApprovalDetector)(bridge, cdp, projectName, selectedAccount);
|
|
1009
|
+
(0, cdpBridgeManager_1.ensureErrorPopupDetector)(bridge, cdp, projectName, selectedAccount);
|
|
1010
|
+
(0, cdpBridgeManager_1.ensurePlanningDetector)(bridge, cdp, projectName, selectedAccount);
|
|
1011
|
+
(0, cdpBridgeManager_1.ensureRunCommandDetector)(bridge, cdp, projectName, selectedAccount);
|
|
887
1012
|
}
|
|
888
1013
|
catch (e) {
|
|
889
1014
|
await interaction.followUp({
|
|
@@ -894,7 +1019,19 @@ const startBot = async (cliLogLevel) => {
|
|
|
894
1019
|
}
|
|
895
1020
|
}
|
|
896
1021
|
else {
|
|
897
|
-
|
|
1022
|
+
const selectedAccount = (0, accountUtils_1.resolveScopedAccountName)({
|
|
1023
|
+
channelId,
|
|
1024
|
+
userId: interaction.user.id,
|
|
1025
|
+
sessionAccountName: chatSessionRepo.findByChannelId(channelId)?.activeAccountName ?? null,
|
|
1026
|
+
parentChannelId: (0, accountUtils_1.inferParentScopeChannelId)(channelId, interaction.channel?.parentId ?? null),
|
|
1027
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
1028
|
+
channelPrefRepo,
|
|
1029
|
+
accountPrefRepo,
|
|
1030
|
+
accounts: config.antigravityAccounts,
|
|
1031
|
+
});
|
|
1032
|
+
cdp = bridge.lastActiveWorkspace
|
|
1033
|
+
? bridge.pool.getConnected(bridge.lastActiveWorkspace, selectedAccount)
|
|
1034
|
+
: null;
|
|
898
1035
|
}
|
|
899
1036
|
if (!cdp) {
|
|
900
1037
|
await interaction.followUp({
|
|
@@ -947,6 +1084,9 @@ const startBot = async (cliLogLevel) => {
|
|
|
947
1084
|
autoRenameChannel,
|
|
948
1085
|
handleScreenshot: screenshotUi_1.handleScreenshot,
|
|
949
1086
|
userPrefRepo,
|
|
1087
|
+
accountPrefRepo,
|
|
1088
|
+
channelPrefRepo,
|
|
1089
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
950
1090
|
}));
|
|
951
1091
|
await client.login(discordToken);
|
|
952
1092
|
} // end: else (credentials present)
|
|
@@ -989,21 +1129,55 @@ const startBot = async (cliLogLevel) => {
|
|
|
989
1129
|
botApi: telegramBot.api,
|
|
990
1130
|
chatSessionService,
|
|
991
1131
|
responseTimeoutMs: config.responseTimeoutMs,
|
|
1132
|
+
accountPrefRepo,
|
|
1133
|
+
channelPrefRepo,
|
|
1134
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
992
1135
|
});
|
|
993
1136
|
// Compose select handlers: project select + mode select
|
|
994
1137
|
const projectSelectHandler = (0, telegramProjectCommand_1.createTelegramSelectHandler)({
|
|
1138
|
+
botApi: telegramBot.api,
|
|
1139
|
+
bridge,
|
|
995
1140
|
workspaceService,
|
|
996
1141
|
telegramBindingRepo,
|
|
997
1142
|
});
|
|
998
1143
|
const modeSelectAction = (0, modeSelectAction_1.createModeSelectAction)({ bridge, modeService });
|
|
1144
|
+
const accountSelectAction = (0, accountSelectAction_1.createAccountSelectAction)({
|
|
1145
|
+
bridge,
|
|
1146
|
+
accountPrefRepo,
|
|
1147
|
+
channelPrefRepo,
|
|
1148
|
+
chatSessionRepo,
|
|
1149
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
1150
|
+
getWorkspacePathForChannel: (channelId) => {
|
|
1151
|
+
const binding = telegramBindingRepo.findByChatId(channelId);
|
|
1152
|
+
if (!binding)
|
|
1153
|
+
return null;
|
|
1154
|
+
return workspaceService
|
|
1155
|
+
? workspaceService.getWorkspacePath(binding.workspacePath)
|
|
1156
|
+
: binding.workspacePath;
|
|
1157
|
+
},
|
|
1158
|
+
});
|
|
999
1159
|
const telegramSelectHandler = (0, selectHandler_1.createPlatformSelectHandler)({
|
|
1000
1160
|
actions: [
|
|
1001
1161
|
modeSelectAction,
|
|
1162
|
+
accountSelectAction,
|
|
1002
1163
|
],
|
|
1003
1164
|
});
|
|
1004
1165
|
// Composite handler that routes to the right handler
|
|
1005
1166
|
const compositeSelectHandler = async (interaction) => {
|
|
1006
|
-
if (interaction.customId ===
|
|
1167
|
+
if (interaction.customId === sessionPickerUi_1.SESSION_SELECT_ID) {
|
|
1168
|
+
await (0, telegramJoinCommand_1.handleTelegramJoinSelect)({
|
|
1169
|
+
bridge,
|
|
1170
|
+
botApi: telegramBot.api,
|
|
1171
|
+
telegramBindingRepo,
|
|
1172
|
+
workspaceService,
|
|
1173
|
+
chatSessionService,
|
|
1174
|
+
accountPrefRepo,
|
|
1175
|
+
channelPrefRepo,
|
|
1176
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
1177
|
+
}, interaction);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (interaction.customId === 'mode_select' || interaction.customId === 'account_select') {
|
|
1007
1181
|
await telegramSelectHandler(interaction);
|
|
1008
1182
|
return;
|
|
1009
1183
|
}
|
|
@@ -1022,7 +1196,41 @@ const startBot = async (cliLogLevel) => {
|
|
|
1022
1196
|
(0, planningButtonAction_1.createPlanningButtonAction)({ bridge }),
|
|
1023
1197
|
(0, errorPopupButtonAction_1.createErrorPopupButtonAction)({ bridge }),
|
|
1024
1198
|
(0, runCommandButtonAction_1.createRunCommandButtonAction)({ bridge }),
|
|
1025
|
-
(0, modelButtonAction_1.createModelButtonAction)({
|
|
1199
|
+
(0, modelButtonAction_1.createModelButtonAction)({
|
|
1200
|
+
bridge,
|
|
1201
|
+
fetchQuota: () => bridge.quota.fetchQuota(),
|
|
1202
|
+
modelService,
|
|
1203
|
+
userPrefRepo,
|
|
1204
|
+
ensureSessionActivated: async (channelId, userId, cdp) => {
|
|
1205
|
+
const savedTitle = chatSessionRepo.findByChannelId(channelId)?.displayName?.trim() || '';
|
|
1206
|
+
if (!savedTitle || savedTitle === (0, i18n_1.t)('(Untitled)')) {
|
|
1207
|
+
return { ok: true };
|
|
1208
|
+
}
|
|
1209
|
+
const current = await chatSessionService.getCurrentSessionInfo(cdp);
|
|
1210
|
+
if (current.title.trim() === savedTitle) {
|
|
1211
|
+
return { ok: true };
|
|
1212
|
+
}
|
|
1213
|
+
logger_1.logger.info(`[ModelCommand] source=button channel=${channelId} user=${userId} ` +
|
|
1214
|
+
`restoringSession target="${savedTitle}" current="${current.title.trim() || '(unknown)'}"`);
|
|
1215
|
+
const activation = await chatSessionService.activateSessionByTitle(cdp, savedTitle, {
|
|
1216
|
+
maxWaitMs: 8000,
|
|
1217
|
+
retryIntervalMs: 300,
|
|
1218
|
+
allowVisibilityWarmupMs: 1000,
|
|
1219
|
+
});
|
|
1220
|
+
if (!activation.ok) {
|
|
1221
|
+
return {
|
|
1222
|
+
ok: false,
|
|
1223
|
+
error: `Failed to activate saved session "${savedTitle}" before model action: ${activation.error || 'unknown'}`,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
const refresh = await chatSessionService.refreshSessionViewIfStuck(cdp, savedTitle);
|
|
1227
|
+
if (!refresh.ok) {
|
|
1228
|
+
logger_1.logger.warn(`[ModelCommand] source=button channel=${channelId} user=${userId} ` +
|
|
1229
|
+
`sessionRefreshWarning target="${savedTitle}" error="${refresh.error || 'unknown'}"`);
|
|
1230
|
+
}
|
|
1231
|
+
return { ok: true };
|
|
1232
|
+
},
|
|
1233
|
+
}),
|
|
1026
1234
|
(0, autoAcceptButtonAction_1.createAutoAcceptButtonAction)({ autoAcceptService: bridge.autoAccept }),
|
|
1027
1235
|
(0, templateButtonAction_1.createTemplateButtonAction)({ bridge, templateRepo }),
|
|
1028
1236
|
],
|
|
@@ -1041,11 +1249,14 @@ const startBot = async (cliLogLevel) => {
|
|
|
1041
1249
|
{ command: 'model', description: 'Switch LLM model' },
|
|
1042
1250
|
{ command: 'screenshot', description: 'Capture Antigravity screenshot' },
|
|
1043
1251
|
{ command: 'autoaccept', description: 'Toggle auto-accept mode' },
|
|
1252
|
+
{ command: 'account', description: 'Switch Antigravity account' },
|
|
1044
1253
|
{ command: 'template', description: 'List prompt templates' },
|
|
1045
1254
|
{ command: 'template_add', description: 'Add a prompt template' },
|
|
1046
1255
|
{ command: 'template_delete', description: 'Delete a prompt template' },
|
|
1047
1256
|
{ command: 'project_create', description: 'Create a new workspace' },
|
|
1048
1257
|
{ command: 'new', description: 'Start a new chat session' },
|
|
1258
|
+
{ command: 'join', description: 'Take over an existing session' },
|
|
1259
|
+
{ command: 'mirror', description: 'Toggle PC-to-Telegram message mirroring' },
|
|
1049
1260
|
{ command: 'logs', description: 'Show recent log entries' },
|
|
1050
1261
|
{ command: 'stop', description: 'Interrupt active LLM generation' },
|
|
1051
1262
|
{ command: 'help', description: 'Show available commands' },
|
|
@@ -1056,7 +1267,8 @@ const startBot = async (cliLogLevel) => {
|
|
|
1056
1267
|
eventRouter.registerAdapter(telegramAdapter);
|
|
1057
1268
|
await eventRouter.startAll();
|
|
1058
1269
|
logger_1.logger.info(`Telegram bot started: @${botInfo.username} (${config.telegramAllowedUserIds?.length ?? 0} allowed users)`);
|
|
1059
|
-
// Send startup message to
|
|
1270
|
+
// Send startup message to one Telegram target:
|
|
1271
|
+
// prefer a group named "general", otherwise the first private chat.
|
|
1060
1272
|
const bindings = telegramBindingRepo.findAll();
|
|
1061
1273
|
if (bindings.length > 0) {
|
|
1062
1274
|
const os = await Promise.resolve().then(() => __importStar(require('os')));
|
|
@@ -1098,13 +1310,15 @@ const startBot = async (cliLogLevel) => {
|
|
|
1098
1310
|
}
|
|
1099
1311
|
}
|
|
1100
1312
|
};
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1313
|
+
const targetChatId = await (0, telegramStartupTarget_1.selectTelegramStartupChatId)(telegramBot.api, bindings);
|
|
1314
|
+
if (targetChatId) {
|
|
1315
|
+
try {
|
|
1316
|
+
await sendWithRetry(targetChatId, startupText);
|
|
1317
|
+
logger_1.logger.info(`Telegram startup message sent to chat ${targetChatId}.`);
|
|
1318
|
+
}
|
|
1319
|
+
catch (error) {
|
|
1320
|
+
logger_1.logger.warn(`[Telegram] Startup message failed for chat ${targetChatId} after retries: ${error?.message ?? 'unknown error'}`);
|
|
1321
|
+
}
|
|
1108
1322
|
}
|
|
1109
1323
|
}
|
|
1110
1324
|
}
|
|
@@ -1138,8 +1352,78 @@ async function autoRenameChannel(message, chatSessionRepo, titleGenerator, chann
|
|
|
1138
1352
|
/**
|
|
1139
1353
|
* Handle Discord Interactions API slash commands
|
|
1140
1354
|
*/
|
|
1141
|
-
async function handleSlashInteraction(interaction, handler, bridge, wsHandler, chatHandler, cleanupHandler, modeService, modelService, autoAcceptService, _client, promptDispatcher, templateRepo, joinHandler, userPrefRepo) {
|
|
1355
|
+
async function handleSlashInteraction(interaction, handler, bridge, wsHandler, chatHandler, cleanupHandler, chatSessionService, modeService, modelService, autoAcceptService, _client, promptDispatcher, templateRepo, joinHandler, userPrefRepo, accountPrefRepo, channelPrefRepo, antigravityAccounts = [{ name: 'default', cdpPort: 9222 }], chatSessionRepo) {
|
|
1142
1356
|
const commandName = interaction.commandName;
|
|
1357
|
+
const getAccountPort = (accountName) => {
|
|
1358
|
+
const match = antigravityAccounts.find((account) => account.name === accountName);
|
|
1359
|
+
return match ? match.cdpPort : null;
|
|
1360
|
+
};
|
|
1361
|
+
const parentChannelId = (0, accountUtils_1.inferParentScopeChannelId)(interaction.channelId, interaction.channel?.parentId ?? null);
|
|
1362
|
+
const getSessionAccountName = () => chatSessionRepo?.findByChannelId(interaction.channelId)?.activeAccountName ?? null;
|
|
1363
|
+
const resolveSelectedAccount = () => (0, accountUtils_1.resolveScopedAccountName)({
|
|
1364
|
+
channelId: interaction.channelId,
|
|
1365
|
+
userId: interaction.user.id,
|
|
1366
|
+
sessionAccountName: getSessionAccountName(),
|
|
1367
|
+
parentChannelId,
|
|
1368
|
+
selectedAccountByChannel: bridge.selectedAccountByChannel,
|
|
1369
|
+
channelPrefRepo,
|
|
1370
|
+
accountPrefRepo,
|
|
1371
|
+
accounts: antigravityAccounts,
|
|
1372
|
+
});
|
|
1373
|
+
const getChannelWorkspacePath = () => wsHandler.getWorkspaceForChannel(interaction.channelId);
|
|
1374
|
+
const getChannelCdp = () => (() => {
|
|
1375
|
+
const workspacePath = getChannelWorkspacePath();
|
|
1376
|
+
if (workspacePath) {
|
|
1377
|
+
const projectName = bridge.pool.extractProjectName(workspacePath);
|
|
1378
|
+
return bridge.pool.getConnected(projectName, resolveSelectedAccount());
|
|
1379
|
+
}
|
|
1380
|
+
return bridge.lastActiveWorkspace
|
|
1381
|
+
? bridge.pool.getConnected(bridge.lastActiveWorkspace, resolveSelectedAccount())
|
|
1382
|
+
: null;
|
|
1383
|
+
})();
|
|
1384
|
+
const ensureChannelCdp = async () => {
|
|
1385
|
+
const existing = getChannelCdp();
|
|
1386
|
+
if (existing)
|
|
1387
|
+
return existing;
|
|
1388
|
+
const workspacePath = getChannelWorkspacePath();
|
|
1389
|
+
if (!workspacePath)
|
|
1390
|
+
return null;
|
|
1391
|
+
try {
|
|
1392
|
+
return await bridge.pool.getOrConnect(workspacePath, { name: resolveSelectedAccount() });
|
|
1393
|
+
}
|
|
1394
|
+
catch {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
const ensureBoundSessionActive = async (cdp) => {
|
|
1399
|
+
const savedTitle = chatSessionRepo?.findByChannelId(interaction.channelId)?.displayName?.trim() || '';
|
|
1400
|
+
if (!savedTitle || savedTitle === (0, i18n_1.t)('(Untitled)')) {
|
|
1401
|
+
return { ok: true };
|
|
1402
|
+
}
|
|
1403
|
+
const current = await chatSessionService.getCurrentSessionInfo(cdp);
|
|
1404
|
+
if (current.title.trim() === savedTitle) {
|
|
1405
|
+
return { ok: true };
|
|
1406
|
+
}
|
|
1407
|
+
logger_1.logger.info(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1408
|
+
`restoringSession target="${savedTitle}" current="${current.title.trim() || '(unknown)'}"`);
|
|
1409
|
+
const activation = await chatSessionService.activateSessionByTitle(cdp, savedTitle, {
|
|
1410
|
+
maxWaitMs: 8000,
|
|
1411
|
+
retryIntervalMs: 300,
|
|
1412
|
+
allowVisibilityWarmupMs: 1000,
|
|
1413
|
+
});
|
|
1414
|
+
if (!activation.ok) {
|
|
1415
|
+
return {
|
|
1416
|
+
ok: false,
|
|
1417
|
+
error: `Failed to activate saved session "${savedTitle}" before model action: ${activation.error || 'unknown'}`,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
const refresh = await chatSessionService.refreshSessionViewIfStuck(cdp, savedTitle);
|
|
1421
|
+
if (!refresh.ok) {
|
|
1422
|
+
logger_1.logger.warn(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1423
|
+
`sessionRefreshWarning target="${savedTitle}" error="${refresh.error || 'unknown'}"`);
|
|
1424
|
+
}
|
|
1425
|
+
return { ok: true };
|
|
1426
|
+
};
|
|
1143
1427
|
switch (commandName) {
|
|
1144
1428
|
case 'help': {
|
|
1145
1429
|
const helpFields = [
|
|
@@ -1172,6 +1456,7 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1172
1456
|
name: '📁 Projects', value: [
|
|
1173
1457
|
'`/project` — Display project list',
|
|
1174
1458
|
'`/project create <name>` — Create a new project',
|
|
1459
|
+
'`/project account [name]` — Show or change the project channel account',
|
|
1175
1460
|
].join('\n')
|
|
1176
1461
|
},
|
|
1177
1462
|
{
|
|
@@ -1185,6 +1470,7 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1185
1470
|
name: '🔧 System', value: [
|
|
1186
1471
|
'`/status` — Display overall bot status',
|
|
1187
1472
|
'`/autoaccept` — Toggle auto-approve mode for approval dialogs via buttons',
|
|
1473
|
+
'`/account` — Show and switch Antigravity account',
|
|
1188
1474
|
'`/logs [lines] [level]` — View recent bot logs',
|
|
1189
1475
|
'`/cleanup [days]` — Clean up unused channels/categories',
|
|
1190
1476
|
'`/help` — Show this help',
|
|
@@ -1213,24 +1499,47 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1213
1499
|
break;
|
|
1214
1500
|
}
|
|
1215
1501
|
case 'mode': {
|
|
1216
|
-
await (0, modeUi_1.sendModeUI)(interaction, modeService, { getCurrentCdp: () => (
|
|
1502
|
+
await (0, modeUi_1.sendModeUI)(interaction, modeService, { getCurrentCdp: () => getChannelCdp() });
|
|
1217
1503
|
break;
|
|
1218
1504
|
}
|
|
1219
1505
|
case 'model': {
|
|
1220
1506
|
const modelName = interaction.options.getString('name');
|
|
1507
|
+
logger_1.logger.info(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1508
|
+
`requested=${modelName ? `"${modelName}"` : 'ui'}`);
|
|
1221
1509
|
if (!modelName) {
|
|
1510
|
+
const cdp = await ensureChannelCdp();
|
|
1511
|
+
if (!cdp) {
|
|
1512
|
+
logger_1.logger.warn(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} cdp=unavailable`);
|
|
1513
|
+
await interaction.editReply({ content: 'Not connected to CDP.' });
|
|
1514
|
+
break;
|
|
1515
|
+
}
|
|
1516
|
+
const sessionReady = await ensureBoundSessionActive(cdp);
|
|
1517
|
+
if (!sessionReady.ok) {
|
|
1518
|
+
await interaction.editReply({ content: sessionReady.error });
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1222
1521
|
await (0, modelsUi_1.sendModelsUI)(interaction, {
|
|
1223
|
-
getCurrentCdp: () =>
|
|
1522
|
+
getCurrentCdp: () => cdp,
|
|
1224
1523
|
fetchQuota: async () => bridge.quota.fetchQuota(),
|
|
1225
1524
|
});
|
|
1226
1525
|
}
|
|
1227
1526
|
else {
|
|
1228
|
-
const cdp =
|
|
1527
|
+
const cdp = await ensureChannelCdp();
|
|
1229
1528
|
if (!cdp) {
|
|
1529
|
+
logger_1.logger.warn(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} target="${modelName}" cdp=unavailable`);
|
|
1230
1530
|
await interaction.editReply({ content: 'Not connected to CDP.' });
|
|
1231
1531
|
break;
|
|
1232
1532
|
}
|
|
1533
|
+
const sessionReady = await ensureBoundSessionActive(cdp);
|
|
1534
|
+
if (!sessionReady.ok) {
|
|
1535
|
+
await interaction.editReply({ content: sessionReady.error });
|
|
1536
|
+
break;
|
|
1537
|
+
}
|
|
1233
1538
|
const res = await cdp.setUiModel(modelName);
|
|
1539
|
+
logger_1.logger.info(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1540
|
+
`target="${modelName}" ok=${res.ok} applied=${res.model ? `"${res.model}"` : 'null'} ` +
|
|
1541
|
+
`verified=${res.verified === true} alreadySelected=${res.alreadySelected === true} ` +
|
|
1542
|
+
`error=${res.error ? `"${res.error}"` : 'null'}`);
|
|
1234
1543
|
if (res.ok) {
|
|
1235
1544
|
await interaction.editReply({ content: `Model changed to **${res.model}**.` });
|
|
1236
1545
|
}
|
|
@@ -1270,19 +1579,26 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1270
1579
|
case 'status': {
|
|
1271
1580
|
const activeNames = bridge.pool.getActiveWorkspaceNames();
|
|
1272
1581
|
const currentModel = (() => {
|
|
1273
|
-
const cdp = (
|
|
1582
|
+
const cdp = getChannelCdp();
|
|
1274
1583
|
return cdp ? 'CDP Connected' : 'Disconnected';
|
|
1275
1584
|
})();
|
|
1276
1585
|
const currentMode = modeService.getCurrentMode();
|
|
1586
|
+
const session = chatSessionRepo?.findByChannelId(interaction.channelId);
|
|
1277
1587
|
const mirroringWorkspaces = activeNames.filter((name) => bridge.pool.getUserMessageDetector(name)?.isActive());
|
|
1278
1588
|
const mirrorStatus = mirroringWorkspaces.length > 0
|
|
1279
1589
|
? `📡 ON (${mirroringWorkspaces.join(', ')})`
|
|
1280
1590
|
: '⚪ OFF';
|
|
1591
|
+
const currentAccount = resolveSelectedAccount();
|
|
1592
|
+
const originalAccount = session?.originAccountName ?? '(unset)';
|
|
1593
|
+
const conversationTitle = session?.displayName ?? '(New chat / no saved title)';
|
|
1281
1594
|
const statusFields = [
|
|
1282
1595
|
{ name: 'CDP Connection', value: activeNames.length > 0 ? `🟢 ${activeNames.length} project(s) connected` : '⚪ Disconnected', inline: true },
|
|
1283
1596
|
{ name: 'Mode', value: modeService_1.MODE_DISPLAY_NAMES[currentMode] || currentMode, inline: true },
|
|
1284
1597
|
{ name: 'Auto Approve', value: autoAcceptService.isEnabled() ? '🟢 ON' : '⚪ OFF', inline: true },
|
|
1285
1598
|
{ name: 'Mirroring', value: mirrorStatus, inline: true },
|
|
1599
|
+
{ name: 'Active Account', value: currentAccount, inline: true },
|
|
1600
|
+
{ name: 'Original Account', value: originalAccount, inline: true },
|
|
1601
|
+
{ name: 'Conversation Title', value: conversationTitle, inline: false },
|
|
1286
1602
|
];
|
|
1287
1603
|
let statusDescription = '';
|
|
1288
1604
|
if (activeNames.length > 0) {
|
|
@@ -1327,6 +1643,38 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1327
1643
|
await interaction.editReply({ content: result.message });
|
|
1328
1644
|
break;
|
|
1329
1645
|
}
|
|
1646
|
+
case 'account': {
|
|
1647
|
+
if (!accountPrefRepo) {
|
|
1648
|
+
await interaction.editReply({ content: 'Account preference service not available.' });
|
|
1649
|
+
break;
|
|
1650
|
+
}
|
|
1651
|
+
const requested = interaction.options.getString('name');
|
|
1652
|
+
if (!requested) {
|
|
1653
|
+
const current = resolveSelectedAccount();
|
|
1654
|
+
const names = (0, accountUtils_1.listAccountNames)(antigravityAccounts);
|
|
1655
|
+
await (0, accountUi_1.sendAccountUI)(interaction, current, names);
|
|
1656
|
+
break;
|
|
1657
|
+
}
|
|
1658
|
+
if (!(0, accountUtils_1.listAccountNames)(antigravityAccounts).includes(requested)) {
|
|
1659
|
+
await interaction.editReply({ content: `⚠️ Unknown account: **${requested}**` });
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
bridge.selectedAccountByChannel?.set(interaction.channelId, requested);
|
|
1663
|
+
const currentSession = chatSessionRepo?.findByChannelId(interaction.channelId);
|
|
1664
|
+
if (currentSession) {
|
|
1665
|
+
chatSessionRepo?.setActiveAccountName(interaction.channelId, requested);
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
accountPrefRepo.setAccountName(interaction.user.id, requested);
|
|
1669
|
+
channelPrefRepo?.setAccountName(interaction.channelId, requested);
|
|
1670
|
+
}
|
|
1671
|
+
const channelWorkspace = wsHandler.getWorkspaceForChannel(interaction.channelId);
|
|
1672
|
+
logger_1.logger.info(`[AccountSwitch] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1673
|
+
`account=${requested} port=${getAccountPort(requested) ?? 'unknown'} ` +
|
|
1674
|
+
`workspace=${channelWorkspace ?? 'unbound'}`);
|
|
1675
|
+
await interaction.editReply({ content: `✅ Switched session account to **${requested}**.` });
|
|
1676
|
+
break;
|
|
1677
|
+
}
|
|
1330
1678
|
case 'output': {
|
|
1331
1679
|
if (!userPrefRepo) {
|
|
1332
1680
|
await interaction.editReply({ content: 'Output preference service not available.' });
|
|
@@ -1345,11 +1693,11 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1345
1693
|
break;
|
|
1346
1694
|
}
|
|
1347
1695
|
case 'screenshot': {
|
|
1348
|
-
await (0, screenshotUi_1.handleScreenshot)(interaction, (
|
|
1696
|
+
await (0, screenshotUi_1.handleScreenshot)(interaction, getChannelCdp());
|
|
1349
1697
|
break;
|
|
1350
1698
|
}
|
|
1351
1699
|
case 'stop': {
|
|
1352
|
-
const cdp = (
|
|
1700
|
+
const cdp = getChannelCdp();
|
|
1353
1701
|
if (!cdp) {
|
|
1354
1702
|
await interaction.editReply({ content: '⚠️ Not connected to CDP. Please connect to a project first.' });
|
|
1355
1703
|
break;
|
|
@@ -1398,6 +1746,29 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1398
1746
|
}
|
|
1399
1747
|
await wsHandler.handleCreate(interaction, interaction.guild);
|
|
1400
1748
|
}
|
|
1749
|
+
else if (wsSub === 'account') {
|
|
1750
|
+
const requested = interaction.options.getString('name');
|
|
1751
|
+
const names = (0, accountUtils_1.listAccountNames)(antigravityAccounts);
|
|
1752
|
+
const currentProjectAccount = channelPrefRepo?.getAccountName(interaction.channelId) ?? null;
|
|
1753
|
+
if (!requested) {
|
|
1754
|
+
await interaction.editReply({
|
|
1755
|
+
content: `Project channel account: **${currentProjectAccount ?? 'unset'}**\nAvailable: ${names.join(', ')}`,
|
|
1756
|
+
});
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1759
|
+
if (!names.includes(requested)) {
|
|
1760
|
+
await interaction.editReply({ content: `⚠️ Unknown account: **${requested}**` });
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1763
|
+
channelPrefRepo?.setAccountName(interaction.channelId, requested);
|
|
1764
|
+
bridge.selectedAccountByChannel?.set(interaction.channelId, requested);
|
|
1765
|
+
const channelWorkspace = wsHandler.getWorkspaceForChannel(interaction.channelId);
|
|
1766
|
+
logger_1.logger.info(`[ProjectAccountSwitch] source=slash channel=${interaction.channelId} user=${interaction.user.id} ` +
|
|
1767
|
+
`account=${requested} port=${getAccountPort(requested) ?? 'unknown'} ` +
|
|
1768
|
+
`workspace=${channelWorkspace ?? 'unbound'}`);
|
|
1769
|
+
await interaction.editReply({ content: `✅ Bound this project channel to account **${requested}**.` });
|
|
1770
|
+
break;
|
|
1771
|
+
}
|
|
1401
1772
|
else {
|
|
1402
1773
|
// /project list or /project (default)
|
|
1403
1774
|
await wsHandler.handleShow(interaction);
|