lazy-gravity 0.2.0 → 0.3.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/README.md +76 -15
- package/dist/bin/commands/doctor.js +19 -2
- package/dist/bin/commands/setup.js +286 -70
- package/dist/bot/eventRouter.js +70 -0
- package/dist/bot/index.js +353 -147
- package/dist/bot/telegramCommands.js +428 -0
- package/dist/bot/telegramMessageHandler.js +304 -0
- package/dist/bot/telegramProjectCommand.js +137 -0
- package/dist/bot/workspaceQueue.js +61 -0
- package/dist/commands/joinCommandHandler.js +4 -1
- package/dist/database/telegramBindingRepository.js +97 -0
- package/dist/database/userPreferenceRepository.js +46 -1
- package/dist/events/interactionCreateHandler.js +36 -0
- package/dist/events/messageCreateHandler.js +11 -7
- package/dist/handlers/approvalButtonAction.js +99 -0
- package/dist/handlers/autoAcceptButtonAction.js +43 -0
- package/dist/handlers/buttonHandler.js +55 -0
- package/dist/handlers/commandHandler.js +44 -0
- package/dist/handlers/errorPopupButtonAction.js +137 -0
- package/dist/handlers/messageHandler.js +70 -0
- package/dist/handlers/modeSelectAction.js +63 -0
- package/dist/handlers/modelButtonAction.js +102 -0
- package/dist/handlers/planningButtonAction.js +118 -0
- package/dist/handlers/selectHandler.js +41 -0
- package/dist/handlers/templateButtonAction.js +54 -0
- package/dist/platform/adapter.js +8 -0
- package/dist/platform/discord/discordAdapter.js +99 -0
- package/dist/platform/discord/index.js +15 -0
- package/dist/platform/discord/wrappers.js +331 -0
- package/dist/platform/index.js +18 -0
- package/dist/platform/richContentBuilder.js +76 -0
- package/dist/platform/telegram/index.js +16 -0
- package/dist/platform/telegram/telegramAdapter.js +195 -0
- package/dist/platform/telegram/telegramFormatter.js +134 -0
- package/dist/platform/telegram/wrappers.js +329 -0
- package/dist/platform/types.js +28 -0
- package/dist/services/approvalDetector.js +15 -2
- package/dist/services/cdpBridgeManager.js +91 -146
- package/dist/services/defaultModelApplicator.js +54 -0
- package/dist/services/modeService.js +16 -1
- package/dist/services/modelService.js +57 -16
- package/dist/services/notificationSender.js +149 -0
- package/dist/services/responseMonitor.js +1 -2
- package/dist/ui/autoAcceptUi.js +37 -0
- package/dist/ui/modeUi.js +38 -1
- package/dist/ui/modelsUi.js +96 -0
- package/dist/ui/outputUi.js +32 -0
- package/dist/ui/projectListUi.js +55 -0
- package/dist/ui/screenshotUi.js +26 -0
- package/dist/ui/sessionPickerUi.js +35 -1
- package/dist/ui/templateUi.js +41 -0
- package/dist/utils/configLoader.js +63 -12
- package/dist/utils/lockfile.js +5 -5
- package/dist/utils/logger.js +7 -0
- package/dist/utils/telegramImageHandler.js +127 -0
- package/package.json +4 -2
|
@@ -0,0 +1,428 @@
|
|
|
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
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
53
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
54
|
+
};
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.parseTelegramCommand = parseTelegramCommand;
|
|
57
|
+
exports.handleTelegramCommand = handleTelegramCommand;
|
|
58
|
+
const fs_1 = __importDefault(require("fs"));
|
|
59
|
+
const cdpBridgeManager_1 = require("../services/cdpBridgeManager");
|
|
60
|
+
const modeUi_1 = require("../ui/modeUi");
|
|
61
|
+
const modelsUi_1 = require("../ui/modelsUi");
|
|
62
|
+
const autoAcceptUi_1 = require("../ui/autoAcceptUi");
|
|
63
|
+
const templateUi_1 = require("../ui/templateUi");
|
|
64
|
+
const screenshotUi_1 = require("../ui/screenshotUi");
|
|
65
|
+
const logBuffer_1 = require("../utils/logBuffer");
|
|
66
|
+
const telegramFormatter_1 = require("../platform/telegram/telegramFormatter");
|
|
67
|
+
const logger_1 = require("../utils/logger");
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Known commands (used by both parser and /help output)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
const KNOWN_COMMANDS = ['start', 'help', 'status', 'stop', 'ping', 'mode', 'model', 'screenshot', 'autoaccept', 'template', 'template_add', 'template_delete', 'project_create', 'logs'];
|
|
72
|
+
/**
|
|
73
|
+
* Parse a Telegram command from message text.
|
|
74
|
+
*
|
|
75
|
+
* Accepted formats:
|
|
76
|
+
* /command
|
|
77
|
+
* /command args text
|
|
78
|
+
* /command@BotName
|
|
79
|
+
* /command@BotName args text
|
|
80
|
+
*
|
|
81
|
+
* Returns null if the text is not a known command (unknown commands
|
|
82
|
+
* are forwarded to Antigravity as normal messages).
|
|
83
|
+
*/
|
|
84
|
+
function parseTelegramCommand(text) {
|
|
85
|
+
const trimmed = text.trim();
|
|
86
|
+
const match = trimmed.match(/^\/(\w+)(?:@\S+)?(?:\s+(.*))?$/);
|
|
87
|
+
if (!match)
|
|
88
|
+
return null;
|
|
89
|
+
const command = match[1].toLowerCase();
|
|
90
|
+
if (!KNOWN_COMMANDS.includes(command))
|
|
91
|
+
return null;
|
|
92
|
+
return {
|
|
93
|
+
command,
|
|
94
|
+
args: (match[2] ?? '').trim(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Dispatcher
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* Handle a parsed Telegram command.
|
|
102
|
+
* Routes to the appropriate sub-handler based on command name.
|
|
103
|
+
*/
|
|
104
|
+
async function handleTelegramCommand(deps, message, parsed) {
|
|
105
|
+
const argsDisplay = parsed.args ? ` ${parsed.args}` : '';
|
|
106
|
+
logger_1.logger.info(`[TelegramCommand] /${parsed.command}${argsDisplay} (chat=${message.channel.id})`);
|
|
107
|
+
switch (parsed.command) {
|
|
108
|
+
case 'start':
|
|
109
|
+
await handleStart(message);
|
|
110
|
+
break;
|
|
111
|
+
case 'help':
|
|
112
|
+
await handleHelp(message);
|
|
113
|
+
break;
|
|
114
|
+
case 'status':
|
|
115
|
+
await handleStatus(deps, message);
|
|
116
|
+
break;
|
|
117
|
+
case 'stop':
|
|
118
|
+
await handleStop(deps, message);
|
|
119
|
+
break;
|
|
120
|
+
case 'ping':
|
|
121
|
+
await handlePing(message);
|
|
122
|
+
break;
|
|
123
|
+
case 'mode':
|
|
124
|
+
await handleMode(deps, message);
|
|
125
|
+
break;
|
|
126
|
+
case 'model':
|
|
127
|
+
await handleModel(deps, message);
|
|
128
|
+
break;
|
|
129
|
+
case 'screenshot':
|
|
130
|
+
await handleScreenshot(deps, message);
|
|
131
|
+
break;
|
|
132
|
+
case 'autoaccept':
|
|
133
|
+
await handleAutoAccept(deps, message, parsed.args);
|
|
134
|
+
break;
|
|
135
|
+
case 'template':
|
|
136
|
+
await handleTemplate(deps, message);
|
|
137
|
+
break;
|
|
138
|
+
case 'template_add':
|
|
139
|
+
await handleTemplateAdd(deps, message, parsed.args);
|
|
140
|
+
break;
|
|
141
|
+
case 'template_delete':
|
|
142
|
+
await handleTemplateDelete(deps, message, parsed.args);
|
|
143
|
+
break;
|
|
144
|
+
case 'project_create':
|
|
145
|
+
await handleProjectCreate(deps, message, parsed.args);
|
|
146
|
+
break;
|
|
147
|
+
case 'logs':
|
|
148
|
+
await handleLogs(message, parsed.args);
|
|
149
|
+
break;
|
|
150
|
+
default:
|
|
151
|
+
// Should not happen — parser filters unknowns
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Sub-handlers
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
async function handleStart(message) {
|
|
159
|
+
const text = [
|
|
160
|
+
'<b>Welcome to LazyGravity!</b>',
|
|
161
|
+
'',
|
|
162
|
+
'This bot connects you to Antigravity AI workspaces.',
|
|
163
|
+
'',
|
|
164
|
+
'Get started:',
|
|
165
|
+
'1. Use /project to bind this chat to a workspace',
|
|
166
|
+
'2. Send any message to start chatting with Antigravity',
|
|
167
|
+
'',
|
|
168
|
+
'Type /help for a list of available commands.',
|
|
169
|
+
].join('\n');
|
|
170
|
+
await message.reply({ text }).catch(logger_1.logger.error);
|
|
171
|
+
}
|
|
172
|
+
async function handleHelp(message) {
|
|
173
|
+
const text = [
|
|
174
|
+
'<b>Available Commands</b>',
|
|
175
|
+
'',
|
|
176
|
+
'/project — Manage workspace bindings',
|
|
177
|
+
'/status — Show bot status and connections',
|
|
178
|
+
'/mode — Switch execution mode',
|
|
179
|
+
'/model — Switch LLM model',
|
|
180
|
+
'/screenshot — Capture Antigravity screenshot',
|
|
181
|
+
'/autoaccept — Toggle auto-accept mode',
|
|
182
|
+
'/template — List prompt templates',
|
|
183
|
+
'/template_add — Add a prompt template',
|
|
184
|
+
'/template_delete — Delete a prompt template',
|
|
185
|
+
'/project_create — Create a new workspace',
|
|
186
|
+
'/logs — Show recent log entries',
|
|
187
|
+
'/stop — Interrupt active LLM generation',
|
|
188
|
+
'/ping — Check bot latency',
|
|
189
|
+
'/help — Show this help message',
|
|
190
|
+
'',
|
|
191
|
+
'Any other message is forwarded to Antigravity.',
|
|
192
|
+
].join('\n');
|
|
193
|
+
await message.reply({ text }).catch(logger_1.logger.error);
|
|
194
|
+
}
|
|
195
|
+
async function handleStatus(deps, message) {
|
|
196
|
+
const chatId = message.channel.id;
|
|
197
|
+
// Current chat binding
|
|
198
|
+
const binding = deps.telegramBindingRepo?.findByChatId(chatId);
|
|
199
|
+
const boundProject = binding?.workspacePath ?? '(none)';
|
|
200
|
+
// CDP connection status for this chat's project
|
|
201
|
+
const activeWorkspaces = deps.bridge.pool.getActiveWorkspaceNames();
|
|
202
|
+
const projectConnected = binding
|
|
203
|
+
? activeWorkspaces.some((name) => binding.workspacePath.includes(name) || name.includes(binding.workspacePath))
|
|
204
|
+
: false;
|
|
205
|
+
const mode = deps.modeService
|
|
206
|
+
? deps.modeService.getCurrentMode()
|
|
207
|
+
: 'unknown';
|
|
208
|
+
const lines = [
|
|
209
|
+
'<b>Bot Status</b>',
|
|
210
|
+
'',
|
|
211
|
+
`<b>This chat:</b>`,
|
|
212
|
+
` Project: ${(0, telegramFormatter_1.escapeHtml)(boundProject)}`,
|
|
213
|
+
` CDP: ${projectConnected ? 'Connected' : 'Not connected'}`,
|
|
214
|
+
'',
|
|
215
|
+
`Mode: ${(0, telegramFormatter_1.escapeHtml)(mode)}`,
|
|
216
|
+
`Active connections: ${activeWorkspaces.length > 0 ? activeWorkspaces.map(telegramFormatter_1.escapeHtml).join(', ') : 'none'}`,
|
|
217
|
+
];
|
|
218
|
+
await message.reply({ text: lines.join('\n') }).catch(logger_1.logger.error);
|
|
219
|
+
}
|
|
220
|
+
async function handleStop(deps, message) {
|
|
221
|
+
const workspace = deps.bridge.lastActiveWorkspace;
|
|
222
|
+
// Try to use the active ResponseMonitor first (it stops monitoring + clicks stop)
|
|
223
|
+
if (workspace && deps.activeMonitors) {
|
|
224
|
+
const monitor = deps.activeMonitors.get(workspace);
|
|
225
|
+
if (monitor && monitor.isActive()) {
|
|
226
|
+
logger_1.logger.info(`[TelegramCommand:stop] Stopping active monitor for ${workspace}...`);
|
|
227
|
+
const result = await monitor.clickStopButton();
|
|
228
|
+
if (result.ok) {
|
|
229
|
+
logger_1.logger.done(`[TelegramCommand:stop] Stopped via monitor (method=${result.method})`);
|
|
230
|
+
await message.reply({ text: 'Generation stopped.' }).catch(logger_1.logger.error);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
logger_1.logger.warn(`[TelegramCommand:stop] Monitor clickStopButton failed: ${result.error}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Fallback: try direct CDP call (no active monitor, or monitor click failed)
|
|
237
|
+
const cdp = (0, cdpBridgeManager_1.getCurrentCdp)(deps.bridge);
|
|
238
|
+
if (!cdp) {
|
|
239
|
+
logger_1.logger.warn('[TelegramCommand:stop] No CDP — lastActiveWorkspace:', workspace ?? '(null)');
|
|
240
|
+
await message.reply({ text: 'No active workspace connection.' }).catch(logger_1.logger.error);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
logger_1.logger.info('[TelegramCommand:stop] Clicking stop button via direct CDP...');
|
|
245
|
+
const { RESPONSE_SELECTORS } = await Promise.resolve().then(() => __importStar(require('../services/responseMonitor')));
|
|
246
|
+
const result = await cdp.call('Runtime.evaluate', { expression: RESPONSE_SELECTORS.CLICK_STOP_BUTTON, returnByValue: true });
|
|
247
|
+
const value = result?.result?.value;
|
|
248
|
+
if (value && typeof value === 'object' && value.ok) {
|
|
249
|
+
logger_1.logger.done(`[TelegramCommand:stop] Stop button clicked (method=${value.method})`);
|
|
250
|
+
await message.reply({ text: 'Generation stopped.' }).catch(logger_1.logger.error);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
logger_1.logger.warn('[TelegramCommand:stop] Stop button not found — value:', JSON.stringify(value));
|
|
254
|
+
await message.reply({ text: 'Stop button not found (generation may have already finished).' }).catch(logger_1.logger.error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
logger_1.logger.error('[TelegramCommand:stop]', err?.message || err);
|
|
259
|
+
await message.reply({ text: 'Failed to click stop button.' }).catch(logger_1.logger.error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async function handlePing(message) {
|
|
263
|
+
await message.reply({ text: 'Pong!' }).catch(logger_1.logger.error);
|
|
264
|
+
}
|
|
265
|
+
async function handleMode(deps, message) {
|
|
266
|
+
if (!deps.modeService) {
|
|
267
|
+
await message.reply({ text: 'Mode service not available.' }).catch(logger_1.logger.error);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const isPending = deps.modeService.isPendingSync();
|
|
271
|
+
const payload = (0, modeUi_1.buildModePayload)(deps.modeService.getCurrentMode(), isPending);
|
|
272
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
273
|
+
}
|
|
274
|
+
async function handleModel(deps, message) {
|
|
275
|
+
const cdp = (0, cdpBridgeManager_1.getCurrentCdp)(deps.bridge);
|
|
276
|
+
if (!cdp) {
|
|
277
|
+
await message.reply({ text: 'Not connected to Antigravity.' }).catch(logger_1.logger.error);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const models = await cdp.getUiModels();
|
|
281
|
+
const currentModel = await cdp.getCurrentModel();
|
|
282
|
+
const quotaData = deps.fetchQuota ? await deps.fetchQuota() : [];
|
|
283
|
+
const defaultModel = deps.modelService?.getDefaultModel() ?? null;
|
|
284
|
+
const payload = (0, modelsUi_1.buildModelsPayload)(models, currentModel, quotaData, defaultModel);
|
|
285
|
+
if (!payload) {
|
|
286
|
+
await message.reply({ text: 'No models available.' }).catch(logger_1.logger.error);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
290
|
+
}
|
|
291
|
+
async function handleScreenshot(deps, message) {
|
|
292
|
+
const cdp = (0, cdpBridgeManager_1.getCurrentCdp)(deps.bridge);
|
|
293
|
+
const payload = await (0, screenshotUi_1.buildScreenshotPayload)(cdp);
|
|
294
|
+
// If the payload contains files, send them as text (base64) since
|
|
295
|
+
// Telegram file sending requires special API calls handled by the adapter.
|
|
296
|
+
if (payload.files && payload.files.length > 0) {
|
|
297
|
+
await sendFilePayload(message, payload);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async function handleAutoAccept(deps, message, args) {
|
|
304
|
+
// If args are provided (e.g. /autoaccept on), handle directly
|
|
305
|
+
if (args) {
|
|
306
|
+
const result = deps.bridge.autoAccept.handle(args);
|
|
307
|
+
await message.reply({ text: result.message }).catch(logger_1.logger.error);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
// No args — show interactive UI with buttons
|
|
311
|
+
const payload = (0, autoAcceptUi_1.buildAutoAcceptPayload)(deps.bridge.autoAccept.isEnabled());
|
|
312
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
313
|
+
}
|
|
314
|
+
async function handleTemplate(deps, message) {
|
|
315
|
+
if (!deps.templateRepo) {
|
|
316
|
+
await message.reply({ text: 'Template service not available.' }).catch(logger_1.logger.error);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const templates = deps.templateRepo.findAll();
|
|
320
|
+
const payload = (0, templateUi_1.buildTemplatePayload)(templates);
|
|
321
|
+
await message.reply(payload).catch(logger_1.logger.error);
|
|
322
|
+
}
|
|
323
|
+
async function handleTemplateAdd(deps, message, args) {
|
|
324
|
+
if (!deps.templateRepo) {
|
|
325
|
+
await message.reply({ text: 'Template service not available.' }).catch(logger_1.logger.error);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
// Split args into name (first word) and prompt (rest)
|
|
329
|
+
const spaceIndex = args.indexOf(' ');
|
|
330
|
+
if (!args || spaceIndex === -1) {
|
|
331
|
+
await message.reply({
|
|
332
|
+
text: 'Usage: /template_add <name> <prompt>\nExample: /template_add daily-report Write a daily standup report',
|
|
333
|
+
}).catch(logger_1.logger.error);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const name = args.slice(0, spaceIndex);
|
|
337
|
+
const prompt = args.slice(spaceIndex + 1).trim();
|
|
338
|
+
try {
|
|
339
|
+
deps.templateRepo.create({ name, prompt });
|
|
340
|
+
await message.reply({ text: `Template '${(0, telegramFormatter_1.escapeHtml)(name)}' created.` }).catch(logger_1.logger.error);
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
if (err?.message?.includes('UNIQUE constraint')) {
|
|
344
|
+
await message.reply({ text: `Template '${(0, telegramFormatter_1.escapeHtml)(name)}' already exists.` }).catch(logger_1.logger.error);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
logger_1.logger.error('[TelegramCommand:template_add]', err?.message || err);
|
|
348
|
+
await message.reply({ text: 'Failed to create template.' }).catch(logger_1.logger.error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
async function handleTemplateDelete(deps, message, args) {
|
|
353
|
+
if (!deps.templateRepo) {
|
|
354
|
+
await message.reply({ text: 'Template service not available.' }).catch(logger_1.logger.error);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const name = args.trim();
|
|
358
|
+
if (!name) {
|
|
359
|
+
await message.reply({
|
|
360
|
+
text: 'Usage: /template_delete <name>\nExample: /template_delete daily-report',
|
|
361
|
+
}).catch(logger_1.logger.error);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const deleted = deps.templateRepo.deleteByName(name);
|
|
365
|
+
if (deleted) {
|
|
366
|
+
await message.reply({ text: `Template '${(0, telegramFormatter_1.escapeHtml)(name)}' deleted.` }).catch(logger_1.logger.error);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
await message.reply({ text: `Template '${(0, telegramFormatter_1.escapeHtml)(name)}' not found.` }).catch(logger_1.logger.error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function handleProjectCreate(deps, message, args) {
|
|
373
|
+
if (!deps.workspaceService) {
|
|
374
|
+
await message.reply({ text: 'Workspace service not available.' }).catch(logger_1.logger.error);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const name = args.trim();
|
|
378
|
+
if (!name) {
|
|
379
|
+
await message.reply({
|
|
380
|
+
text: 'Usage: /project_create <name>\nExample: /project_create NewProject',
|
|
381
|
+
}).catch(logger_1.logger.error);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
const safePath = deps.workspaceService.validatePath(name);
|
|
386
|
+
if (deps.workspaceService.exists(name)) {
|
|
387
|
+
await message.reply({ text: `Workspace '${(0, telegramFormatter_1.escapeHtml)(name)}' already exists.` }).catch(logger_1.logger.error);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
fs_1.default.mkdirSync(safePath, { recursive: true });
|
|
391
|
+
await message.reply({ text: `Workspace '${(0, telegramFormatter_1.escapeHtml)(name)}' created.` }).catch(logger_1.logger.error);
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
logger_1.logger.error('[TelegramCommand:project_create]', err?.message || err);
|
|
395
|
+
await message.reply({ text: `Failed to create workspace: ${(0, telegramFormatter_1.escapeHtml)(err?.message || 'unknown error')}` }).catch(logger_1.logger.error);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function handleLogs(message, args) {
|
|
399
|
+
const countArg = args ? parseInt(args, 10) : 20;
|
|
400
|
+
const count = isNaN(countArg) ? 20 : Math.min(Math.max(countArg, 1), 50);
|
|
401
|
+
const entries = logBuffer_1.logBuffer.getRecent(count);
|
|
402
|
+
if (entries.length === 0) {
|
|
403
|
+
await message.reply({ text: 'No log entries.' }).catch(logger_1.logger.error);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const lines = entries.map((e) => `<code>${e.timestamp.slice(11, 19)}</code> [${e.level.toUpperCase()}] ${(0, telegramFormatter_1.escapeHtml)(e.message)}`);
|
|
407
|
+
const text = `<b>Recent Logs (${entries.length})</b>\n\n${lines.join('\n')}`;
|
|
408
|
+
// Telegram message limit is 4096 chars
|
|
409
|
+
const truncated = text.length > 4096 ? text.slice(0, 4090) + '\n...' : text;
|
|
410
|
+
await message.reply({ text: truncated }).catch(logger_1.logger.error);
|
|
411
|
+
}
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// Helpers
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
/**
|
|
416
|
+
* Send a MessagePayload that contains file attachments.
|
|
417
|
+
* Falls back to a text reply if file sending is not supported.
|
|
418
|
+
*/
|
|
419
|
+
async function sendFilePayload(message, payload) {
|
|
420
|
+
// Try sending with files — the Telegram adapter supports this if sendPhoto is available
|
|
421
|
+
try {
|
|
422
|
+
await message.reply(payload);
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
logger_1.logger.warn('[TelegramCommand:screenshot] File sending failed:', err instanceof Error ? err.message : err);
|
|
426
|
+
await message.reply({ text: 'Screenshot captured but file sending failed.' }).catch(logger_1.logger.error);
|
|
427
|
+
}
|
|
428
|
+
}
|