@pixelbyte-software/pixcode 1.53.4 → 1.53.5
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/assets/{index-DlwfTw9d.js → index-CyeODv14.js} +155 -155
- package/dist/index.html +1 -1
- package/dist-server/server/database/db.js +28 -0
- package/dist-server/server/database/db.js.map +1 -1
- package/dist-server/server/routes/telegram.js +16 -2
- package/dist-server/server/routes/telegram.js.map +1 -1
- package/dist-server/server/services/telegram/bot.js +9 -4
- package/dist-server/server/services/telegram/bot.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +390 -67
- package/dist-server/server/services/telegram/control-center.js.map +1 -1
- package/dist-server/server/services/telegram/telegram-gateway.js +220 -0
- package/dist-server/server/services/telegram/telegram-gateway.js.map +1 -0
- package/dist-server/server/services/telegram/telegram-http-client.js +10 -8
- package/dist-server/server/services/telegram/telegram-http-client.js.map +1 -1
- package/dist-server/server/services/telegram/translations.js +18 -0
- package/dist-server/server/services/telegram/translations.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/telegram-control.mjs +10 -1
- package/server/database/db.js +28 -0
- package/server/routes/telegram.js +25 -2
- package/server/services/telegram/bot.js +8 -4
- package/server/services/telegram/control-center.js +410 -66
- package/server/services/telegram/telegram-gateway.js +250 -0
- package/server/services/telegram/telegram-http-client.js +7 -5
- package/server/services/telegram/translations.js +18 -0
|
@@ -2,8 +2,9 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import { apiKeysDb, telegramLinksDb } from '../../database/db.js';
|
|
3
3
|
import { getProjects } from '../../projects.js';
|
|
4
4
|
import { getStaticProviderModels } from '../model-registry.js';
|
|
5
|
+
import { TELEGRAM_CONTROL_SCOPES, TELEGRAM_PROVIDERS, buildTelegramAgentPrompt, classifyTelegramIntent, clearTelegramConfirmation, consumeTelegramConfirmation, createTelegramConfirmation, enqueueTelegramJob, resolveTelegramModel, resolveTelegramProvider, retryWithBackoff, runTelegramTool, splitTelegramText, } from './telegram-gateway.js';
|
|
5
6
|
import { SUPPORTED_LANGUAGES, t } from './translations.js';
|
|
6
|
-
const PROVIDERS =
|
|
7
|
+
const PROVIDERS = TELEGRAM_PROVIDERS;
|
|
7
8
|
const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'canceled']);
|
|
8
9
|
const CALLBACK_TTL_MS = 10 * 60 * 1000;
|
|
9
10
|
const MAX_CALLBACK_ACTIONS = 1000;
|
|
@@ -113,7 +114,11 @@ function readAction(data) {
|
|
|
113
114
|
callbackActions.delete(id);
|
|
114
115
|
return null;
|
|
115
116
|
}
|
|
116
|
-
return entry;
|
|
117
|
+
return { id, ...entry };
|
|
118
|
+
}
|
|
119
|
+
function forgetAction(id) {
|
|
120
|
+
if (id)
|
|
121
|
+
callbackActions.delete(id);
|
|
117
122
|
}
|
|
118
123
|
function button(text, action, payload = {}) {
|
|
119
124
|
return { text, callback_data: registerAction(action, payload) };
|
|
@@ -131,30 +136,46 @@ async function send(bot, chatId, text, options = {}) {
|
|
|
131
136
|
disable_web_page_preview: true,
|
|
132
137
|
...telegramOptions,
|
|
133
138
|
};
|
|
134
|
-
const
|
|
139
|
+
const chunks = splitTelegramText(text, MAX_TELEGRAM_TEXT);
|
|
140
|
+
const [firstChunk = ''] = chunks;
|
|
141
|
+
let startIndex = 0;
|
|
135
142
|
if (editMessageId && typeof bot.editMessageText === 'function') {
|
|
136
143
|
try {
|
|
137
|
-
|
|
144
|
+
const edited = await bot.editMessageText(firstChunk, {
|
|
138
145
|
chat_id: chatId,
|
|
139
146
|
message_id: editMessageId,
|
|
140
147
|
...extra,
|
|
141
148
|
});
|
|
149
|
+
startIndex = 1;
|
|
150
|
+
for (const chunk of chunks.slice(startIndex)) {
|
|
151
|
+
await send(bot, chatId, chunk, telegramOptions);
|
|
152
|
+
}
|
|
153
|
+
return edited;
|
|
142
154
|
}
|
|
143
155
|
catch (err) {
|
|
144
156
|
const description = err?.response?.body?.description || err?.message || '';
|
|
145
|
-
if (/message is not modified/i.test(description))
|
|
157
|
+
if (/message is not modified/i.test(description)) {
|
|
158
|
+
startIndex = 1;
|
|
159
|
+
for (const chunk of chunks.slice(startIndex)) {
|
|
160
|
+
await send(bot, chatId, chunk, telegramOptions);
|
|
161
|
+
}
|
|
146
162
|
return null;
|
|
163
|
+
}
|
|
147
164
|
console.warn('[telegram-control] editMessageText failed:', description || err);
|
|
148
165
|
}
|
|
149
166
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
let result = null;
|
|
168
|
+
for (const chunk of chunks.slice(startIndex)) {
|
|
169
|
+
try {
|
|
170
|
+
result = await bot.sendMessage(chatId, chunk, extra);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
const fallback = { ...extra };
|
|
174
|
+
delete fallback.parse_mode;
|
|
175
|
+
result = await bot.sendMessage(chatId, chunk, fallback);
|
|
176
|
+
}
|
|
157
177
|
}
|
|
178
|
+
return result;
|
|
158
179
|
}
|
|
159
180
|
function localApiBase() {
|
|
160
181
|
const port = process.env.SERVER_PORT || process.env.PORT || '3001';
|
|
@@ -164,19 +185,34 @@ function getOrCreateTelegramApiKey(userId) {
|
|
|
164
185
|
const existing = apiKeysDb
|
|
165
186
|
.getApiKeys(userId)
|
|
166
187
|
.find((key) => key.key_name === 'Telegram Control' && Boolean(key.is_active));
|
|
167
|
-
if (existing?.api_key)
|
|
188
|
+
if (existing?.api_key) {
|
|
189
|
+
const existingScopes = apiKeysDb.normalizeScopes(existing.scopes || []);
|
|
190
|
+
const nextScopes = apiKeysDb.normalizeScopes([...existingScopes, ...TELEGRAM_CONTROL_SCOPES]);
|
|
191
|
+
const hasAllScopes = TELEGRAM_CONTROL_SCOPES.every((scope) => existingScopes.includes(scope));
|
|
192
|
+
if (!hasAllScopes || nextScopes.length !== existingScopes.length) {
|
|
193
|
+
apiKeysDb.updateApiKeyScopes(userId, existing.id, nextScopes);
|
|
194
|
+
}
|
|
168
195
|
return existing.api_key;
|
|
169
|
-
|
|
196
|
+
}
|
|
197
|
+
return apiKeysDb.createApiKey(userId, 'Telegram Control', TELEGRAM_CONTROL_SCOPES).apiKey;
|
|
170
198
|
}
|
|
171
199
|
async function localApi(userId, path, { method = 'GET', body } = {}) {
|
|
172
200
|
const apiKey = getOrCreateTelegramApiKey(userId);
|
|
173
|
-
const response = await
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
201
|
+
const response = await retryWithBackoff(async () => {
|
|
202
|
+
const res = await fetch(`${localApiBase()}${path}`, {
|
|
203
|
+
method,
|
|
204
|
+
headers: {
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
'X-API-Key': apiKey,
|
|
207
|
+
},
|
|
208
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
209
|
+
});
|
|
210
|
+
if ([408, 429, 500, 502, 503, 504].includes(res.status)) {
|
|
211
|
+
const error = new Error(`HTTP ${res.status}`);
|
|
212
|
+
error.status = res.status;
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
return res;
|
|
180
216
|
});
|
|
181
217
|
const data = await response.json().catch(() => ({}));
|
|
182
218
|
if (!response.ok) {
|
|
@@ -428,6 +464,36 @@ function extractAssistantText(response) {
|
|
|
428
464
|
}
|
|
429
465
|
return chunks.join('\n\n').trim();
|
|
430
466
|
}
|
|
467
|
+
function confirmationLabel(lang, action, payload = {}) {
|
|
468
|
+
if (action === 'install_provider') {
|
|
469
|
+
return t(lang, 'control.confirmInstall', { provider: payload.provider || '' });
|
|
470
|
+
}
|
|
471
|
+
if (action === 'run_cancel') {
|
|
472
|
+
return t(lang, 'control.confirmCancelRun', { runId: payload.runId || '' });
|
|
473
|
+
}
|
|
474
|
+
return t(lang, 'control.confirmationRequired');
|
|
475
|
+
}
|
|
476
|
+
async function requestConfirmation({ bot, chatId, link, action, payload = {}, editMessageId }) {
|
|
477
|
+
const lang = languageFor(link);
|
|
478
|
+
const pending = createTelegramConfirmation(link.user_id, action, payload);
|
|
479
|
+
await send(bot, chatId, confirmationLabel(lang, action, payload), {
|
|
480
|
+
editMessageId,
|
|
481
|
+
reply_markup: {
|
|
482
|
+
inline_keyboard: [[
|
|
483
|
+
button(t(lang, 'control.button.confirm'), 'confirm_action', { id: pending.id }),
|
|
484
|
+
button(t(lang, 'control.button.cancel'), 'cancel_confirmation', { id: pending.id }),
|
|
485
|
+
]],
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
return { ok: false, requiresConfirmation: true };
|
|
489
|
+
}
|
|
490
|
+
async function sendToolFailure({ bot, chatId, link, result, editMessageId }) {
|
|
491
|
+
const lang = languageFor(link);
|
|
492
|
+
const message = result?.message === 'REMOTE_CONTROL_DISABLED'
|
|
493
|
+
? t(lang, 'control.disabled')
|
|
494
|
+
: (result?.message || t(lang, 'error.generic'));
|
|
495
|
+
await send(bot, chatId, message, { editMessageId });
|
|
496
|
+
}
|
|
431
497
|
async function runAgent({ bot, chatId, link, prompt }) {
|
|
432
498
|
const lang = languageFor(link);
|
|
433
499
|
const state = getState(link.user_id);
|
|
@@ -440,33 +506,49 @@ async function runAgent({ bot, chatId, link, prompt }) {
|
|
|
440
506
|
await showProjectMenu({ bot, chatId, link });
|
|
441
507
|
return;
|
|
442
508
|
}
|
|
509
|
+
const provider = resolveTelegramProvider(state);
|
|
510
|
+
const model = resolveTelegramModel(state);
|
|
443
511
|
if (state.progressMode !== 'final') {
|
|
444
512
|
await send(bot, chatId, t(lang, 'control.agentStarted', {
|
|
445
|
-
provider
|
|
513
|
+
provider,
|
|
446
514
|
project: state.selectedProjectName || state.selectedProjectPath,
|
|
447
515
|
}));
|
|
448
516
|
}
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
517
|
+
const result = await runTelegramTool({
|
|
518
|
+
userId: link.user_id,
|
|
519
|
+
action: 'run_agent',
|
|
520
|
+
retries: 0,
|
|
521
|
+
execute: () => localApi(link.user_id, '/api/agent', {
|
|
522
|
+
method: 'POST',
|
|
523
|
+
body: {
|
|
524
|
+
projectPath: state.selectedProjectPath,
|
|
525
|
+
provider,
|
|
526
|
+
model: model || undefined,
|
|
527
|
+
message: buildTelegramAgentPrompt(prompt, state),
|
|
528
|
+
cleanup: false,
|
|
529
|
+
stream: false,
|
|
530
|
+
},
|
|
531
|
+
}),
|
|
459
532
|
});
|
|
533
|
+
if (!result.ok) {
|
|
534
|
+
await sendToolFailure({ bot, chatId, link, result });
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const response = result.data;
|
|
460
538
|
const assistantText = extractAssistantText(response);
|
|
461
539
|
const statusLine = response.success === false
|
|
462
540
|
? t(lang, 'control.agentFailed', { error: response.error || 'provider returned no answer' })
|
|
463
|
-
: t(lang, 'control.agentDone', { provider
|
|
541
|
+
: t(lang, 'control.agentDone', { provider, session: response.sessionId ? ` (${response.sessionId})` : '' });
|
|
464
542
|
await send(bot, chatId, `${statusLine}\n\n${assistantText || response.error || t(lang, 'control.noAssistantText')}`);
|
|
465
543
|
}
|
|
466
544
|
export async function runWorkflow({ bot, chatId, link, input }) {
|
|
467
545
|
const lang = languageFor(link);
|
|
468
546
|
const state = getState(link.user_id);
|
|
469
547
|
const workflowId = state.selectedWorkflowId;
|
|
548
|
+
if (!state.remoteControlEnabled) {
|
|
549
|
+
await send(bot, chatId, t(lang, 'control.disabled'));
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
470
552
|
if (!workflowId) {
|
|
471
553
|
await send(bot, chatId, t(lang, 'control.selectWorkflowFirst'));
|
|
472
554
|
await showWorkflowMenu({ bot, chatId, link });
|
|
@@ -477,26 +559,173 @@ export async function runWorkflow({ bot, chatId, link, input }) {
|
|
|
477
559
|
await showProjectMenu({ bot, chatId, link });
|
|
478
560
|
return;
|
|
479
561
|
}
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
562
|
+
const provider = resolveTelegramProvider(state);
|
|
563
|
+
const model = resolveTelegramModel(state);
|
|
564
|
+
const result = await runTelegramTool({
|
|
565
|
+
userId: link.user_id,
|
|
566
|
+
action: 'run_workflow',
|
|
567
|
+
execute: () => localApi(link.user_id, `/api/orchestration/workflows/${workflowId}/runs`, {
|
|
568
|
+
method: 'POST',
|
|
569
|
+
body: {
|
|
570
|
+
input,
|
|
571
|
+
metadata: {
|
|
572
|
+
projectId: state.selectedProjectName,
|
|
573
|
+
projectName: state.selectedProjectName,
|
|
574
|
+
projectPath: state.selectedProjectPath,
|
|
575
|
+
workspaceTarget: 'selected_project',
|
|
576
|
+
telegram: true,
|
|
577
|
+
preferredProvider: provider,
|
|
578
|
+
preferredModel: model,
|
|
579
|
+
},
|
|
492
580
|
},
|
|
493
|
-
},
|
|
581
|
+
}),
|
|
494
582
|
});
|
|
583
|
+
if (!result.ok) {
|
|
584
|
+
await sendToolFailure({ bot, chatId, link, result });
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
const run = result.data;
|
|
495
588
|
await send(bot, chatId, t(lang, 'control.workflowStarted', { runId: run.id, workflowId }));
|
|
496
589
|
monitorWorkflowRun({ bot, chatId, link, runId: run.id }).catch((error) => {
|
|
497
590
|
console.warn('[telegram-control] workflow monitor failed:', error?.message || error);
|
|
498
591
|
});
|
|
499
592
|
}
|
|
593
|
+
async function cancelRun({ bot, chatId, link, runId, editMessageId, confirmed = false }) {
|
|
594
|
+
const lang = languageFor(link);
|
|
595
|
+
if (!runId) {
|
|
596
|
+
await send(bot, chatId, t(lang, 'control.cancelUsage'), { editMessageId });
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const state = getState(link.user_id);
|
|
600
|
+
if (state.remoteControlEnabled === false) {
|
|
601
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId });
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (state.confirmationPolicy === 'strict' && !confirmed) {
|
|
605
|
+
await requestConfirmation({
|
|
606
|
+
bot,
|
|
607
|
+
chatId,
|
|
608
|
+
link,
|
|
609
|
+
action: 'run_cancel',
|
|
610
|
+
payload: { runId },
|
|
611
|
+
editMessageId,
|
|
612
|
+
});
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const result = await runTelegramTool({
|
|
616
|
+
userId: link.user_id,
|
|
617
|
+
action: 'run_cancel',
|
|
618
|
+
execute: () => localApi(link.user_id, `/api/orchestration/workflows/runs/${encodeURIComponent(runId)}/cancel`, {
|
|
619
|
+
method: 'POST',
|
|
620
|
+
}),
|
|
621
|
+
});
|
|
622
|
+
if (!result.ok) {
|
|
623
|
+
await sendToolFailure({ bot, chatId, link, result, editMessageId });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const run = result.data;
|
|
627
|
+
await send(bot, chatId, t(lang, 'control.runStatus', { runId: run.id, status: run.status }), { editMessageId });
|
|
628
|
+
}
|
|
629
|
+
async function executeConfirmedAction({ bot, chatId, link, confirmation, editMessageId }) {
|
|
630
|
+
if (confirmation.action === 'install_provider') {
|
|
631
|
+
await startCliInstall({
|
|
632
|
+
bot,
|
|
633
|
+
chatId,
|
|
634
|
+
link,
|
|
635
|
+
provider: confirmation.payload?.provider,
|
|
636
|
+
editMessageId,
|
|
637
|
+
confirmed: true,
|
|
638
|
+
});
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
if (confirmation.action === 'run_cancel') {
|
|
642
|
+
await cancelRun({
|
|
643
|
+
bot,
|
|
644
|
+
chatId,
|
|
645
|
+
link,
|
|
646
|
+
runId: confirmation.payload?.runId,
|
|
647
|
+
editMessageId,
|
|
648
|
+
confirmed: true,
|
|
649
|
+
});
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
async function findProjectByQuery(query) {
|
|
655
|
+
const projects = await listProjects();
|
|
656
|
+
const needle = String(query || '').trim().toLocaleLowerCase('tr');
|
|
657
|
+
if (!needle)
|
|
658
|
+
return null;
|
|
659
|
+
return projects.find((project) => {
|
|
660
|
+
const candidates = [project.name, project.displayName, project.fullPath, project.path]
|
|
661
|
+
.filter(Boolean)
|
|
662
|
+
.map((value) => String(value).toLocaleLowerCase('tr'));
|
|
663
|
+
return candidates.some((candidate) => candidate === needle || candidate.includes(needle));
|
|
664
|
+
}) || null;
|
|
665
|
+
}
|
|
666
|
+
async function handleRoutedIntent({ bot, chatId, link, text }) {
|
|
667
|
+
const state = getState(link.user_id);
|
|
668
|
+
const intent = classifyTelegramIntent(text, state);
|
|
669
|
+
if (!intent)
|
|
670
|
+
return false;
|
|
671
|
+
if (intent.action === 'projects') {
|
|
672
|
+
await showProjectMenu({ bot, chatId, link });
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
if (intent.action === 'project_select_query') {
|
|
676
|
+
const project = await findProjectByQuery(intent.query);
|
|
677
|
+
const lang = languageFor(link);
|
|
678
|
+
if (!project) {
|
|
679
|
+
await send(bot, chatId, t(lang, 'control.projectNotFound', { query: intent.query }));
|
|
680
|
+
await showProjectMenu({ bot, chatId, link });
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
updateTelegramControlState(link.user_id, {
|
|
684
|
+
selectedProjectName: project.name,
|
|
685
|
+
selectedProjectPath: project.fullPath || project.path,
|
|
686
|
+
});
|
|
687
|
+
await showMainMenu({
|
|
688
|
+
bot,
|
|
689
|
+
chatId,
|
|
690
|
+
link,
|
|
691
|
+
notice: t(lang, 'control.projectSelected', {
|
|
692
|
+
project: project.displayName || project.name,
|
|
693
|
+
path: project.fullPath || project.path,
|
|
694
|
+
}),
|
|
695
|
+
});
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
if (intent.action === 'provider_menu') {
|
|
699
|
+
await showProviderMenu({ bot, chatId, link });
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
if (intent.action === 'provider_select') {
|
|
703
|
+
updateTelegramControlState(link.user_id, { selectedProvider: intent.provider, selectedModel: null });
|
|
704
|
+
await showModelMenu({ bot, chatId, link });
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
if (intent.action === 'model_menu') {
|
|
708
|
+
await showModelMenu({ bot, chatId, link });
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
if (intent.action === 'runs') {
|
|
712
|
+
await showRuns({ bot, chatId, link });
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
if (intent.action === 'approvals') {
|
|
716
|
+
await showApprovalQueue({ bot, chatId, link });
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
if (intent.action === 'workflows') {
|
|
720
|
+
await showWorkflowMenu({ bot, chatId, link });
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
if (intent.action === 'new_chat') {
|
|
724
|
+
await startNewChat({ bot, chatId, link });
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
500
729
|
async function fetchRun(userId, runId) {
|
|
501
730
|
return localApi(userId, `/api/orchestration/workflows/runs/${runId}`);
|
|
502
731
|
}
|
|
@@ -560,18 +789,47 @@ async function monitorWorkflowRun({ bot, chatId, link, runId }) {
|
|
|
560
789
|
runMonitors.delete(runId);
|
|
561
790
|
}
|
|
562
791
|
}
|
|
563
|
-
export async function startCliInstall({ bot, chatId, link, provider }) {
|
|
792
|
+
export async function startCliInstall({ bot, chatId, link, provider, editMessageId, confirmed = false }) {
|
|
564
793
|
const lang = languageFor(link);
|
|
565
|
-
|
|
794
|
+
if (!PROVIDERS.includes(provider)) {
|
|
795
|
+
await send(bot, chatId, t(lang, 'control.providerAuthFallback'), { editMessageId });
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
const state = getState(link.user_id);
|
|
799
|
+
if (state.remoteControlEnabled === false) {
|
|
800
|
+
await send(bot, chatId, t(lang, 'control.disabled'), { editMessageId });
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (state.confirmationPolicy === 'strict' && !confirmed) {
|
|
804
|
+
await requestConfirmation({
|
|
805
|
+
bot,
|
|
806
|
+
chatId,
|
|
807
|
+
link,
|
|
808
|
+
action: 'install_provider',
|
|
809
|
+
payload: { provider },
|
|
810
|
+
editMessageId,
|
|
811
|
+
});
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const result = await runTelegramTool({
|
|
815
|
+
userId: link.user_id,
|
|
816
|
+
action: 'install_provider',
|
|
817
|
+
execute: () => localApi(link.user_id, `/api/providers/${provider}/install`, { method: 'POST' }),
|
|
818
|
+
});
|
|
819
|
+
if (!result.ok) {
|
|
820
|
+
await sendToolFailure({ bot, chatId, link, result, editMessageId });
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const data = result.data;
|
|
566
824
|
if (data?.manual) {
|
|
567
|
-
await send(bot, chatId, t(lang, 'control.manualInstall', { provider, manual: data.manual }));
|
|
825
|
+
await send(bot, chatId, t(lang, 'control.manualInstall', { provider, manual: data.manual }), { editMessageId });
|
|
568
826
|
return;
|
|
569
827
|
}
|
|
570
828
|
await send(bot, chatId, t(lang, 'control.installStarted', {
|
|
571
829
|
provider,
|
|
572
830
|
jobId: data.jobId,
|
|
573
831
|
command: data.installCmd || 'internal installer',
|
|
574
|
-
}));
|
|
832
|
+
}), { editMessageId });
|
|
575
833
|
}
|
|
576
834
|
async function showInstallMenu({ bot, chatId, link, editMessageId }) {
|
|
577
835
|
const lang = languageFor(link);
|
|
@@ -747,15 +1005,12 @@ async function handleCommand({ bot, chatId, link, text }) {
|
|
|
747
1005
|
await send(bot, chatId, t(lang, 'control.cancelUsage'));
|
|
748
1006
|
return true;
|
|
749
1007
|
}
|
|
750
|
-
|
|
751
|
-
method: 'POST',
|
|
752
|
-
});
|
|
753
|
-
await send(bot, chatId, t(lang, 'control.runStatus', { runId: run.id, status: run.status }));
|
|
1008
|
+
await cancelRun({ bot, chatId, link, runId: argText });
|
|
754
1009
|
return true;
|
|
755
1010
|
}
|
|
756
1011
|
return false;
|
|
757
1012
|
}
|
|
758
|
-
|
|
1013
|
+
async function handleTelegramControlMessageInternal({ bot, msg, link }) {
|
|
759
1014
|
const chatId = msg.chat.id;
|
|
760
1015
|
const text = String(msg.text || '').trim();
|
|
761
1016
|
if (!text)
|
|
@@ -772,7 +1027,11 @@ export async function handleTelegramControlMessage({ bot, msg, link }) {
|
|
|
772
1027
|
return true;
|
|
773
1028
|
if (await handleCommand({ bot, chatId, link, text }))
|
|
774
1029
|
return true;
|
|
1030
|
+
if (await handleRoutedIntent({ bot, chatId, link, text }))
|
|
1031
|
+
return true;
|
|
775
1032
|
const state = getState(link.user_id);
|
|
1033
|
+
if (state.routerEnabled === false)
|
|
1034
|
+
return false;
|
|
776
1035
|
if (!state.remoteControlEnabled) {
|
|
777
1036
|
await send(bot, chatId, t(languageFor(link), 'control.disabled'));
|
|
778
1037
|
return true;
|
|
@@ -780,6 +1039,21 @@ export async function handleTelegramControlMessage({ bot, msg, link }) {
|
|
|
780
1039
|
await runAgent({ bot, chatId, link, prompt: text });
|
|
781
1040
|
return true;
|
|
782
1041
|
}
|
|
1042
|
+
export async function handleTelegramControlMessage(args) {
|
|
1043
|
+
const chatId = args?.msg?.chat?.id;
|
|
1044
|
+
if (!chatId)
|
|
1045
|
+
return false;
|
|
1046
|
+
return enqueueTelegramJob(chatId, async () => {
|
|
1047
|
+
try {
|
|
1048
|
+
return await handleTelegramControlMessageInternal(args);
|
|
1049
|
+
}
|
|
1050
|
+
catch (error) {
|
|
1051
|
+
console.error('[telegram-control] message handler failed:', error);
|
|
1052
|
+
await send(args.bot, chatId, t(languageFor(args.link), 'error.generic')).catch(() => { });
|
|
1053
|
+
return true;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
783
1057
|
async function showRunDetail({ bot, chatId, link, runId, editMessageId }) {
|
|
784
1058
|
const lang = languageFor(link);
|
|
785
1059
|
const run = await fetchRun(link.user_id, runId);
|
|
@@ -793,7 +1067,7 @@ async function showRunDetail({ bot, chatId, link, runId, editMessageId }) {
|
|
|
793
1067
|
},
|
|
794
1068
|
});
|
|
795
1069
|
}
|
|
796
|
-
|
|
1070
|
+
async function handleTelegramControlCallbackInternal({ bot, query, link }) {
|
|
797
1071
|
const chatId = query.message?.chat?.id;
|
|
798
1072
|
if (!chatId)
|
|
799
1073
|
return;
|
|
@@ -807,6 +1081,33 @@ export async function handleTelegramControlCallback({ bot, query, link }) {
|
|
|
807
1081
|
}
|
|
808
1082
|
await bot.answerCallbackQuery(query.id).catch(() => { });
|
|
809
1083
|
const { action, payload } = entry;
|
|
1084
|
+
if (action === 'confirm_action') {
|
|
1085
|
+
forgetAction(entry.id);
|
|
1086
|
+
const lang = languageFor(link);
|
|
1087
|
+
const result = consumeTelegramConfirmation(link.user_id, payload.id);
|
|
1088
|
+
if (!result.ok) {
|
|
1089
|
+
await send(bot, chatId, t(lang, result.reason === 'expired'
|
|
1090
|
+
? 'control.confirmationExpired'
|
|
1091
|
+
: 'control.confirmationMissing'), { editMessageId });
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (await executeConfirmedAction({
|
|
1095
|
+
bot,
|
|
1096
|
+
chatId,
|
|
1097
|
+
link,
|
|
1098
|
+
confirmation: result.confirmation,
|
|
1099
|
+
editMessageId,
|
|
1100
|
+
}))
|
|
1101
|
+
return;
|
|
1102
|
+
await send(bot, chatId, t(lang, 'error.generic'), { editMessageId });
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
if (action === 'cancel_confirmation') {
|
|
1106
|
+
forgetAction(entry.id);
|
|
1107
|
+
clearTelegramConfirmation(link.user_id, payload.id);
|
|
1108
|
+
await send(bot, chatId, t(languageFor(link), 'control.confirmationCanceled'), { editMessageId });
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
810
1111
|
if (action === 'menu')
|
|
811
1112
|
return showMainMenu({ bot, chatId, link, editMessageId });
|
|
812
1113
|
if (action === 'projects')
|
|
@@ -889,20 +1190,26 @@ export async function handleTelegramControlCallback({ bot, query, link }) {
|
|
|
889
1190
|
if (action === 'run_detail')
|
|
890
1191
|
return showRunDetail({ bot, chatId, link, runId: payload.runId, editMessageId });
|
|
891
1192
|
if (action === 'run_cancel') {
|
|
892
|
-
|
|
893
|
-
method: 'POST',
|
|
894
|
-
});
|
|
895
|
-
await send(bot, chatId, t(languageFor(link), 'control.runStatus', { runId: run.id, status: run.status }), { editMessageId });
|
|
1193
|
+
await cancelRun({ bot, chatId, link, runId: payload.runId, editMessageId });
|
|
896
1194
|
return;
|
|
897
1195
|
}
|
|
898
1196
|
if (action === 'approval_decide') {
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1197
|
+
const toolResult = await runTelegramTool({
|
|
1198
|
+
userId: link.user_id,
|
|
1199
|
+
action: 'approval_decide',
|
|
1200
|
+
execute: () => localApi(link.user_id, `/api/orchestration/workflows/approvals/${encodeURIComponent(payload.approvalId)}`, {
|
|
1201
|
+
method: 'POST',
|
|
1202
|
+
body: {
|
|
1203
|
+
allow: payload.allow === true,
|
|
1204
|
+
source: 'telegram',
|
|
1205
|
+
},
|
|
1206
|
+
}),
|
|
905
1207
|
});
|
|
1208
|
+
if (!toolResult.ok) {
|
|
1209
|
+
await sendToolFailure({ bot, chatId, link, result: toolResult, editMessageId });
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const result = toolResult.data;
|
|
906
1213
|
const lang = languageFor(link);
|
|
907
1214
|
await send(bot, chatId, t(lang, 'control.approvalDecided', {
|
|
908
1215
|
approvalId: payload.approvalId,
|
|
@@ -912,7 +1219,7 @@ export async function handleTelegramControlCallback({ bot, query, link }) {
|
|
|
912
1219
|
return showApprovalQueue({ bot, chatId, link });
|
|
913
1220
|
}
|
|
914
1221
|
if (action === 'install_provider')
|
|
915
|
-
return startCliInstall({ bot, chatId, link, provider: payload.provider });
|
|
1222
|
+
return startCliInstall({ bot, chatId, link, provider: payload.provider, editMessageId });
|
|
916
1223
|
if (action === 'auth_provider') {
|
|
917
1224
|
await send(bot, chatId, `${payload.provider} login:\n${AUTH_HELP[payload.provider] || t(languageFor(link), 'control.providerAuthFallback')}`, { editMessageId });
|
|
918
1225
|
return;
|
|
@@ -937,4 +1244,20 @@ export async function handleTelegramControlCallback({ bot, query, link }) {
|
|
|
937
1244
|
});
|
|
938
1245
|
}
|
|
939
1246
|
}
|
|
1247
|
+
export async function handleTelegramControlCallback(args) {
|
|
1248
|
+
const chatId = args?.query?.message?.chat?.id;
|
|
1249
|
+
if (!chatId)
|
|
1250
|
+
return;
|
|
1251
|
+
return enqueueTelegramJob(chatId, async () => {
|
|
1252
|
+
try {
|
|
1253
|
+
await handleTelegramControlCallbackInternal(args);
|
|
1254
|
+
}
|
|
1255
|
+
catch (error) {
|
|
1256
|
+
console.error('[telegram-control] callback failed:', error);
|
|
1257
|
+
const lang = languageFor(args.link);
|
|
1258
|
+
await args.bot?.answerCallbackQuery(args.query?.id, { text: t(lang, 'error.generic') }).catch(() => { });
|
|
1259
|
+
await send(args.bot, chatId, t(lang, 'error.generic')).catch(() => { });
|
|
1260
|
+
}
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
940
1263
|
//# sourceMappingURL=control-center.js.map
|