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
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.
|
|
@@ -786,6 +797,8 @@ const startBot = async (cliLogLevel) => {
|
|
|
786
797
|
const modelService = new modelService_1.ModelService();
|
|
787
798
|
const templateRepo = new templateRepository_1.TemplateRepository(db);
|
|
788
799
|
const userPrefRepo = new userPreferenceRepository_1.UserPreferenceRepository(db);
|
|
800
|
+
const accountPrefRepo = new accountPreferenceRepository_1.AccountPreferenceRepository(db);
|
|
801
|
+
const channelPrefRepo = new channelPreferenceRepository_1.ChannelPreferenceRepository(db);
|
|
789
802
|
// Eagerly load default model from DB (single-user bot optimization)
|
|
790
803
|
try {
|
|
791
804
|
const firstUser = db.prepare('SELECT user_id FROM user_preferences LIMIT 1').get();
|
|
@@ -804,7 +817,11 @@ const startBot = async (cliLogLevel) => {
|
|
|
804
817
|
// Auto-launch Antigravity with CDP port if not already running
|
|
805
818
|
await (0, antigravityLauncher_1.ensureAntigravityRunning)();
|
|
806
819
|
// Initialize CDP bridge (lazy connection: pool creation only)
|
|
807
|
-
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);
|
|
808
825
|
// Initialize CDP-dependent services (constructor CDP dependency removed)
|
|
809
826
|
const chatSessionService = new chatSessionService_1.ChatSessionService();
|
|
810
827
|
const titleGenerator = new titleGeneratorService_1.TitleGeneratorService();
|
|
@@ -815,8 +832,46 @@ const startBot = async (cliLogLevel) => {
|
|
|
815
832
|
sendPromptImpl: sendPromptToAntigravity,
|
|
816
833
|
});
|
|
817
834
|
// Initialize command handlers (joinHandler is created after client, see below)
|
|
818
|
-
const wsHandler = new workspaceCommandHandler_1.WorkspaceCommandHandler(workspaceBindingRepo, chatSessionRepo, workspaceService, channelManager)
|
|
819
|
-
|
|
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
|
+
}));
|
|
820
875
|
const cleanupHandler = new cleanupCommandHandler_1.CleanupCommandHandler(chatSessionRepo, workspaceBindingRepo);
|
|
821
876
|
const slashCommandHandler = new slashCommandHandler_1.SlashCommandHandler(templateRepo);
|
|
822
877
|
// Discord platform — only initialise the Discord client when the platform is enabled
|
|
@@ -834,7 +889,16 @@ const startBot = async (cliLogLevel) => {
|
|
|
834
889
|
discord_js_1.GatewayIntentBits.MessageContent,
|
|
835
890
|
]
|
|
836
891
|
});
|
|
837
|
-
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
|
+
}));
|
|
838
902
|
client.once(discord_js_1.Events.ClientReady, async (readyClient) => {
|
|
839
903
|
logger_1.logger.info(`Ready! Logged in as ${readyClient.user.tag} | extractionMode=${config.extractionMode}`);
|
|
840
904
|
try {
|
|
@@ -899,7 +963,12 @@ const startBot = async (cliLogLevel) => {
|
|
|
899
963
|
parseRunCommandCustomId: cdpBridgeManager_1.parseRunCommandCustomId,
|
|
900
964
|
joinHandler,
|
|
901
965
|
userPrefRepo,
|
|
902
|
-
|
|
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),
|
|
903
972
|
handleTemplateUse: async (interaction, templateId) => {
|
|
904
973
|
const template = templateRepo.findById(templateId);
|
|
905
974
|
if (!template) {
|
|
@@ -915,7 +984,18 @@ const startBot = async (cliLogLevel) => {
|
|
|
915
984
|
let cdp = null;
|
|
916
985
|
if (workspacePath) {
|
|
917
986
|
try {
|
|
918
|
-
|
|
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 });
|
|
919
999
|
const projectName = bridge.pool.extractProjectName(workspacePath);
|
|
920
1000
|
bridge.lastActiveWorkspace = projectName;
|
|
921
1001
|
const platformCh = (0, wrappers_1.wrapDiscordChannel)(interaction.channel);
|
|
@@ -925,10 +1005,10 @@ const startBot = async (cliLogLevel) => {
|
|
|
925
1005
|
if (session?.displayName) {
|
|
926
1006
|
(0, cdpBridgeManager_1.registerApprovalSessionChannel)(bridge, projectName, session.displayName, platformCh);
|
|
927
1007
|
}
|
|
928
|
-
(0, cdpBridgeManager_1.ensureApprovalDetector)(bridge, cdp, projectName);
|
|
929
|
-
(0, cdpBridgeManager_1.ensureErrorPopupDetector)(bridge, cdp, projectName);
|
|
930
|
-
(0, cdpBridgeManager_1.ensurePlanningDetector)(bridge, cdp, projectName);
|
|
931
|
-
(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);
|
|
932
1012
|
}
|
|
933
1013
|
catch (e) {
|
|
934
1014
|
await interaction.followUp({
|
|
@@ -939,7 +1019,19 @@ const startBot = async (cliLogLevel) => {
|
|
|
939
1019
|
}
|
|
940
1020
|
}
|
|
941
1021
|
else {
|
|
942
|
-
|
|
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;
|
|
943
1035
|
}
|
|
944
1036
|
if (!cdp) {
|
|
945
1037
|
await interaction.followUp({
|
|
@@ -992,6 +1084,9 @@ const startBot = async (cliLogLevel) => {
|
|
|
992
1084
|
autoRenameChannel,
|
|
993
1085
|
handleScreenshot: screenshotUi_1.handleScreenshot,
|
|
994
1086
|
userPrefRepo,
|
|
1087
|
+
accountPrefRepo,
|
|
1088
|
+
channelPrefRepo,
|
|
1089
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
995
1090
|
}));
|
|
996
1091
|
await client.login(discordToken);
|
|
997
1092
|
} // end: else (credentials present)
|
|
@@ -1034,21 +1129,55 @@ const startBot = async (cliLogLevel) => {
|
|
|
1034
1129
|
botApi: telegramBot.api,
|
|
1035
1130
|
chatSessionService,
|
|
1036
1131
|
responseTimeoutMs: config.responseTimeoutMs,
|
|
1132
|
+
accountPrefRepo,
|
|
1133
|
+
channelPrefRepo,
|
|
1134
|
+
antigravityAccounts: config.antigravityAccounts,
|
|
1037
1135
|
});
|
|
1038
1136
|
// Compose select handlers: project select + mode select
|
|
1039
1137
|
const projectSelectHandler = (0, telegramProjectCommand_1.createTelegramSelectHandler)({
|
|
1138
|
+
botApi: telegramBot.api,
|
|
1139
|
+
bridge,
|
|
1040
1140
|
workspaceService,
|
|
1041
1141
|
telegramBindingRepo,
|
|
1042
1142
|
});
|
|
1043
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
|
+
});
|
|
1044
1159
|
const telegramSelectHandler = (0, selectHandler_1.createPlatformSelectHandler)({
|
|
1045
1160
|
actions: [
|
|
1046
1161
|
modeSelectAction,
|
|
1162
|
+
accountSelectAction,
|
|
1047
1163
|
],
|
|
1048
1164
|
});
|
|
1049
1165
|
// Composite handler that routes to the right handler
|
|
1050
1166
|
const compositeSelectHandler = async (interaction) => {
|
|
1051
|
-
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') {
|
|
1052
1181
|
await telegramSelectHandler(interaction);
|
|
1053
1182
|
return;
|
|
1054
1183
|
}
|
|
@@ -1067,7 +1196,41 @@ const startBot = async (cliLogLevel) => {
|
|
|
1067
1196
|
(0, planningButtonAction_1.createPlanningButtonAction)({ bridge }),
|
|
1068
1197
|
(0, errorPopupButtonAction_1.createErrorPopupButtonAction)({ bridge }),
|
|
1069
1198
|
(0, runCommandButtonAction_1.createRunCommandButtonAction)({ bridge }),
|
|
1070
|
-
(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
|
+
}),
|
|
1071
1234
|
(0, autoAcceptButtonAction_1.createAutoAcceptButtonAction)({ autoAcceptService: bridge.autoAccept }),
|
|
1072
1235
|
(0, templateButtonAction_1.createTemplateButtonAction)({ bridge, templateRepo }),
|
|
1073
1236
|
],
|
|
@@ -1086,11 +1249,14 @@ const startBot = async (cliLogLevel) => {
|
|
|
1086
1249
|
{ command: 'model', description: 'Switch LLM model' },
|
|
1087
1250
|
{ command: 'screenshot', description: 'Capture Antigravity screenshot' },
|
|
1088
1251
|
{ command: 'autoaccept', description: 'Toggle auto-accept mode' },
|
|
1252
|
+
{ command: 'account', description: 'Switch Antigravity account' },
|
|
1089
1253
|
{ command: 'template', description: 'List prompt templates' },
|
|
1090
1254
|
{ command: 'template_add', description: 'Add a prompt template' },
|
|
1091
1255
|
{ command: 'template_delete', description: 'Delete a prompt template' },
|
|
1092
1256
|
{ command: 'project_create', description: 'Create a new workspace' },
|
|
1093
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' },
|
|
1094
1260
|
{ command: 'logs', description: 'Show recent log entries' },
|
|
1095
1261
|
{ command: 'stop', description: 'Interrupt active LLM generation' },
|
|
1096
1262
|
{ command: 'help', description: 'Show available commands' },
|
|
@@ -1101,7 +1267,8 @@ const startBot = async (cliLogLevel) => {
|
|
|
1101
1267
|
eventRouter.registerAdapter(telegramAdapter);
|
|
1102
1268
|
await eventRouter.startAll();
|
|
1103
1269
|
logger_1.logger.info(`Telegram bot started: @${botInfo.username} (${config.telegramAllowedUserIds?.length ?? 0} allowed users)`);
|
|
1104
|
-
// Send startup message to
|
|
1270
|
+
// Send startup message to one Telegram target:
|
|
1271
|
+
// prefer a group named "general", otherwise the first private chat.
|
|
1105
1272
|
const bindings = telegramBindingRepo.findAll();
|
|
1106
1273
|
if (bindings.length > 0) {
|
|
1107
1274
|
const os = await Promise.resolve().then(() => __importStar(require('os')));
|
|
@@ -1143,13 +1310,15 @@ const startBot = async (cliLogLevel) => {
|
|
|
1143
1310
|
}
|
|
1144
1311
|
}
|
|
1145
1312
|
};
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
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
|
+
}
|
|
1153
1322
|
}
|
|
1154
1323
|
}
|
|
1155
1324
|
}
|
|
@@ -1183,8 +1352,78 @@ async function autoRenameChannel(message, chatSessionRepo, titleGenerator, chann
|
|
|
1183
1352
|
/**
|
|
1184
1353
|
* Handle Discord Interactions API slash commands
|
|
1185
1354
|
*/
|
|
1186
|
-
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) {
|
|
1187
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
|
+
};
|
|
1188
1427
|
switch (commandName) {
|
|
1189
1428
|
case 'help': {
|
|
1190
1429
|
const helpFields = [
|
|
@@ -1217,6 +1456,7 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1217
1456
|
name: '📁 Projects', value: [
|
|
1218
1457
|
'`/project` — Display project list',
|
|
1219
1458
|
'`/project create <name>` — Create a new project',
|
|
1459
|
+
'`/project account [name]` — Show or change the project channel account',
|
|
1220
1460
|
].join('\n')
|
|
1221
1461
|
},
|
|
1222
1462
|
{
|
|
@@ -1230,6 +1470,7 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1230
1470
|
name: '🔧 System', value: [
|
|
1231
1471
|
'`/status` — Display overall bot status',
|
|
1232
1472
|
'`/autoaccept` — Toggle auto-approve mode for approval dialogs via buttons',
|
|
1473
|
+
'`/account` — Show and switch Antigravity account',
|
|
1233
1474
|
'`/logs [lines] [level]` — View recent bot logs',
|
|
1234
1475
|
'`/cleanup [days]` — Clean up unused channels/categories',
|
|
1235
1476
|
'`/help` — Show this help',
|
|
@@ -1258,24 +1499,47 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1258
1499
|
break;
|
|
1259
1500
|
}
|
|
1260
1501
|
case 'mode': {
|
|
1261
|
-
await (0, modeUi_1.sendModeUI)(interaction, modeService, { getCurrentCdp: () => (
|
|
1502
|
+
await (0, modeUi_1.sendModeUI)(interaction, modeService, { getCurrentCdp: () => getChannelCdp() });
|
|
1262
1503
|
break;
|
|
1263
1504
|
}
|
|
1264
1505
|
case 'model': {
|
|
1265
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'}`);
|
|
1266
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
|
+
}
|
|
1267
1521
|
await (0, modelsUi_1.sendModelsUI)(interaction, {
|
|
1268
|
-
getCurrentCdp: () =>
|
|
1522
|
+
getCurrentCdp: () => cdp,
|
|
1269
1523
|
fetchQuota: async () => bridge.quota.fetchQuota(),
|
|
1270
1524
|
});
|
|
1271
1525
|
}
|
|
1272
1526
|
else {
|
|
1273
|
-
const cdp =
|
|
1527
|
+
const cdp = await ensureChannelCdp();
|
|
1274
1528
|
if (!cdp) {
|
|
1529
|
+
logger_1.logger.warn(`[ModelCommand] source=slash channel=${interaction.channelId} user=${interaction.user.id} target="${modelName}" cdp=unavailable`);
|
|
1275
1530
|
await interaction.editReply({ content: 'Not connected to CDP.' });
|
|
1276
1531
|
break;
|
|
1277
1532
|
}
|
|
1533
|
+
const sessionReady = await ensureBoundSessionActive(cdp);
|
|
1534
|
+
if (!sessionReady.ok) {
|
|
1535
|
+
await interaction.editReply({ content: sessionReady.error });
|
|
1536
|
+
break;
|
|
1537
|
+
}
|
|
1278
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'}`);
|
|
1279
1543
|
if (res.ok) {
|
|
1280
1544
|
await interaction.editReply({ content: `Model changed to **${res.model}**.` });
|
|
1281
1545
|
}
|
|
@@ -1315,19 +1579,26 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1315
1579
|
case 'status': {
|
|
1316
1580
|
const activeNames = bridge.pool.getActiveWorkspaceNames();
|
|
1317
1581
|
const currentModel = (() => {
|
|
1318
|
-
const cdp = (
|
|
1582
|
+
const cdp = getChannelCdp();
|
|
1319
1583
|
return cdp ? 'CDP Connected' : 'Disconnected';
|
|
1320
1584
|
})();
|
|
1321
1585
|
const currentMode = modeService.getCurrentMode();
|
|
1586
|
+
const session = chatSessionRepo?.findByChannelId(interaction.channelId);
|
|
1322
1587
|
const mirroringWorkspaces = activeNames.filter((name) => bridge.pool.getUserMessageDetector(name)?.isActive());
|
|
1323
1588
|
const mirrorStatus = mirroringWorkspaces.length > 0
|
|
1324
1589
|
? `📡 ON (${mirroringWorkspaces.join(', ')})`
|
|
1325
1590
|
: '⚪ OFF';
|
|
1591
|
+
const currentAccount = resolveSelectedAccount();
|
|
1592
|
+
const originalAccount = session?.originAccountName ?? '(unset)';
|
|
1593
|
+
const conversationTitle = session?.displayName ?? '(New chat / no saved title)';
|
|
1326
1594
|
const statusFields = [
|
|
1327
1595
|
{ name: 'CDP Connection', value: activeNames.length > 0 ? `🟢 ${activeNames.length} project(s) connected` : '⚪ Disconnected', inline: true },
|
|
1328
1596
|
{ name: 'Mode', value: modeService_1.MODE_DISPLAY_NAMES[currentMode] || currentMode, inline: true },
|
|
1329
1597
|
{ name: 'Auto Approve', value: autoAcceptService.isEnabled() ? '🟢 ON' : '⚪ OFF', inline: true },
|
|
1330
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 },
|
|
1331
1602
|
];
|
|
1332
1603
|
let statusDescription = '';
|
|
1333
1604
|
if (activeNames.length > 0) {
|
|
@@ -1372,6 +1643,38 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1372
1643
|
await interaction.editReply({ content: result.message });
|
|
1373
1644
|
break;
|
|
1374
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
|
+
}
|
|
1375
1678
|
case 'output': {
|
|
1376
1679
|
if (!userPrefRepo) {
|
|
1377
1680
|
await interaction.editReply({ content: 'Output preference service not available.' });
|
|
@@ -1390,11 +1693,11 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1390
1693
|
break;
|
|
1391
1694
|
}
|
|
1392
1695
|
case 'screenshot': {
|
|
1393
|
-
await (0, screenshotUi_1.handleScreenshot)(interaction, (
|
|
1696
|
+
await (0, screenshotUi_1.handleScreenshot)(interaction, getChannelCdp());
|
|
1394
1697
|
break;
|
|
1395
1698
|
}
|
|
1396
1699
|
case 'stop': {
|
|
1397
|
-
const cdp = (
|
|
1700
|
+
const cdp = getChannelCdp();
|
|
1398
1701
|
if (!cdp) {
|
|
1399
1702
|
await interaction.editReply({ content: '⚠️ Not connected to CDP. Please connect to a project first.' });
|
|
1400
1703
|
break;
|
|
@@ -1443,6 +1746,29 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
|
|
|
1443
1746
|
}
|
|
1444
1747
|
await wsHandler.handleCreate(interaction, interaction.guild);
|
|
1445
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
|
+
}
|
|
1446
1772
|
else {
|
|
1447
1773
|
// /project list or /project (default)
|
|
1448
1774
|
await wsHandler.handleShow(interaction);
|