@pixelbyte-software/pixcode 1.53.12 → 1.53.14
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-kP_gZ-wL.js → index-CV_x2mrI.js} +173 -173
- package/dist/index.html +1 -1
- package/dist-server/server/index.js +51 -5
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/routes/auth.js +41 -1
- package/dist-server/server/routes/auth.js.map +1 -1
- package/dist-server/server/routes/git.js +60 -3
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/routes/network.js +17 -1
- package/dist-server/server/routes/network.js.map +1 -1
- package/dist-server/server/services/external-access.js +45 -21
- package/dist-server/server/services/external-access.js.map +1 -1
- package/dist-server/server/services/qr-login.js +115 -0
- package/dist-server/server/services/qr-login.js.map +1 -0
- package/dist-server/server/services/telegram/bot.js +18 -1
- package/dist-server/server/services/telegram/bot.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +225 -9
- package/dist-server/server/services/telegram/control-center.js.map +1 -1
- package/dist-server/server/services/telegram/translations.js +22 -2
- package/dist-server/server/services/telegram/translations.js.map +1 -1
- package/package.json +1 -1
- package/server/index.js +59 -5
- package/server/routes/auth.js +47 -1
- package/server/routes/git.js +70 -3
- package/server/routes/network.js +18 -1
- package/server/services/external-access.js +49 -21
- package/server/services/qr-login.js +126 -0
- package/server/services/telegram/bot.js +15 -0
- package/server/services/telegram/control-center.js +241 -9
- package/server/services/telegram/translations.js +22 -2
|
@@ -32,9 +32,14 @@ const MAX_SSE_BUFFER_CHARS = 256_000;
|
|
|
32
32
|
const ACTIVITY_EDIT_THROTTLE_MS = 1200;
|
|
33
33
|
const ACTIVITY_HEARTBEAT_MS = 8000;
|
|
34
34
|
const INTENT_ROUTER_TIMEOUT_MS = 45_000;
|
|
35
|
+
const TERMINAL_BRIDGE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
36
|
+
const TERMINAL_BRIDGE_POLL_MS = 1500;
|
|
37
|
+
const TERMINAL_BRIDGE_EDIT_THROTTLE_MS = 3500;
|
|
38
|
+
const TERMINAL_BRIDGE_SETTLE_MS = 6000;
|
|
35
39
|
const callbackActions = new Map();
|
|
36
40
|
const runMonitors = new Map();
|
|
37
41
|
const activeLongTasks = new Map();
|
|
42
|
+
const terminalBridgeMonitors = new Map();
|
|
38
43
|
|
|
39
44
|
const MODEL_FALLBACKS = Object.fromEntries(
|
|
40
45
|
PROVIDERS.map((provider) => [provider, getStaticProviderModels(provider)]),
|
|
@@ -587,7 +592,7 @@ function terminalProjectLabel(terminal) {
|
|
|
587
592
|
return terminal?.projectLabel || terminal?.projectName || terminal?.projectPath || '-';
|
|
588
593
|
}
|
|
589
594
|
|
|
590
|
-
function terminalOutputUrl(terminal, maxChars = 3200) {
|
|
595
|
+
function terminalOutputUrl(terminal, maxChars = 3200, sinceCursor = null) {
|
|
591
596
|
const params = new URLSearchParams({
|
|
592
597
|
provider: terminal.provider,
|
|
593
598
|
projectPath: terminal.projectPath,
|
|
@@ -595,10 +600,11 @@ function terminalOutputUrl(terminal, maxChars = 3200) {
|
|
|
595
600
|
});
|
|
596
601
|
if (terminal.tabId) params.set('tabId', terminal.tabId);
|
|
597
602
|
if (terminal.sessionId) params.set('sessionId', terminal.sessionId);
|
|
603
|
+
if (Number.isFinite(sinceCursor)) params.set('sinceCursor', String(sinceCursor));
|
|
598
604
|
return `/api/shell/sessions/provider-output?${params.toString()}`;
|
|
599
605
|
}
|
|
600
606
|
|
|
601
|
-
function renderTerminalSnapshot(lang, terminal, data, { prefix = '' } = {}) {
|
|
607
|
+
function renderTerminalSnapshot(lang, terminal, data, { prefix = '', includeOutput = false } = {}) {
|
|
602
608
|
const active = data?.active !== false;
|
|
603
609
|
const lifecycle = data?.terminalState || data?.lifecycleState || (active ? 'running' : 'not running');
|
|
604
610
|
const output = String(data?.output || '').trim();
|
|
@@ -612,15 +618,221 @@ function renderTerminalSnapshot(lang, terminal, data, { prefix = '' } = {}) {
|
|
|
612
618
|
if (terminal.sessionId || data?.sessionId) {
|
|
613
619
|
lines.push(`🧵 ${t(lang, 'control.activity.session')}: ${terminal.sessionId || data.sessionId}`);
|
|
614
620
|
}
|
|
615
|
-
if (output) {
|
|
621
|
+
if (includeOutput && output) {
|
|
616
622
|
lines.push('', `💬 ${t(lang, 'control.activity.output')}:`);
|
|
617
623
|
lines.push(truncate(output, 2400));
|
|
618
624
|
} else {
|
|
619
|
-
lines.push('', t(lang, 'control.
|
|
625
|
+
lines.push('', t(lang, 'control.terminalOutputHidden'));
|
|
620
626
|
}
|
|
621
627
|
return truncate(lines.join('\n'), 3400);
|
|
622
628
|
}
|
|
623
629
|
|
|
630
|
+
function terminalBridgeMonitorKey(chatId, terminal) {
|
|
631
|
+
return [
|
|
632
|
+
chatId,
|
|
633
|
+
terminal.provider,
|
|
634
|
+
terminal.projectPath,
|
|
635
|
+
terminal.tabId || '-',
|
|
636
|
+
terminal.sessionId || '-',
|
|
637
|
+
].join(':');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function normalizeBridgeLine(value) {
|
|
641
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function isNoisyTerminalBridgeLine(line, prompt) {
|
|
645
|
+
const normalized = normalizeBridgeLine(line);
|
|
646
|
+
if (!normalized || normalized.length <= 1) return true;
|
|
647
|
+
if (/^[╭╮╰╯│─═┌┐└┘├┤┬┴┼\s]+$/u.test(normalized)) return true;
|
|
648
|
+
if (/^[●✶✻✽✢✳⠂⠐⏵·\s0;:()\-|/\\]+$/u.test(normalized)) return true;
|
|
649
|
+
|
|
650
|
+
const lower = normalized.toLowerCase();
|
|
651
|
+
const promptNeedle = normalizeBridgeLine(prompt).toLowerCase();
|
|
652
|
+
if (promptNeedle && lower.includes(promptNeedle.slice(0, 120))) return true;
|
|
653
|
+
if (/^[›❯>]\s*/u.test(normalized)) return true;
|
|
654
|
+
if (lower.includes('welcome back')) return true;
|
|
655
|
+
if (lower.includes('api usage billing')) return true;
|
|
656
|
+
if (lower.includes('bypass permissions')) return true;
|
|
657
|
+
if (lower.includes('try "')) return true;
|
|
658
|
+
if (lower.includes('esc to interrupt') || lower.includes('press esc')) return true;
|
|
659
|
+
if (lower.includes('/effort')) return true;
|
|
660
|
+
if (lower.includes('determining')) return true;
|
|
661
|
+
if (/^(?:claude code|codex)\b/i.test(normalized) && normalized.length < 80) return true;
|
|
662
|
+
if (/^\(?\d+s\s*·\s*[↓↑]?\d*\s*tokens?\)?$/iu.test(normalized)) return true;
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function cleanTerminalBridgeOutput(output, prompt) {
|
|
667
|
+
const text = String(output || '')
|
|
668
|
+
.replace(/\r/g, '\n')
|
|
669
|
+
.replace(/[\x00-\x08\x0B-\x1F\x7F]/g, '')
|
|
670
|
+
.replace(/\u00a0/g, ' ');
|
|
671
|
+
const lines = text
|
|
672
|
+
.split('\n')
|
|
673
|
+
.map((line) => line.replace(/[ \t]+/g, ' ').trim())
|
|
674
|
+
.filter((line) => !isNoisyTerminalBridgeLine(line, prompt));
|
|
675
|
+
const deduped = [];
|
|
676
|
+
for (const line of lines) {
|
|
677
|
+
if (deduped.at(-1) === line) continue;
|
|
678
|
+
deduped.push(line);
|
|
679
|
+
}
|
|
680
|
+
return deduped.join('\n').trim();
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function renderTerminalBridgeProgress(lang, terminal, {
|
|
684
|
+
output = '',
|
|
685
|
+
statusKey = 'control.terminalWaiting',
|
|
686
|
+
startedAt = Date.now(),
|
|
687
|
+
final = false,
|
|
688
|
+
terminalState = null,
|
|
689
|
+
} = {}) {
|
|
690
|
+
const lines = [
|
|
691
|
+
`${final ? '✅' : '⏳'} ${t(lang, statusKey)}`,
|
|
692
|
+
'',
|
|
693
|
+
`🤖 ${t(lang, 'control.activity.provider')}: ${terminal.provider}`,
|
|
694
|
+
`📁 ${t(lang, 'control.activity.project')}: ${compact(terminalProjectLabel(terminal), 90)}`,
|
|
695
|
+
];
|
|
696
|
+
if (terminal.sessionId) {
|
|
697
|
+
lines.push(`🧵 ${t(lang, 'control.activity.session')}: ${terminal.sessionId}`);
|
|
698
|
+
}
|
|
699
|
+
if (terminalState) {
|
|
700
|
+
lines.push(`📌 ${t(lang, 'control.activity.status')}: ${terminalState}`);
|
|
701
|
+
}
|
|
702
|
+
if (output) {
|
|
703
|
+
lines.push('', `💬 ${t(lang, 'control.activity.output')}:`);
|
|
704
|
+
lines.push(truncate(output, final ? 2800 : 2200));
|
|
705
|
+
} else {
|
|
706
|
+
lines.push('', t(lang, 'control.terminalWaitingHint'));
|
|
707
|
+
}
|
|
708
|
+
if (!final) {
|
|
709
|
+
lines.push('', `⏱ ${t(lang, 'control.activity.elapsed')}: ${formatElapsed(startedAt)}`);
|
|
710
|
+
}
|
|
711
|
+
return truncate(lines.join('\n'), 3400);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async function monitorTerminalBridgeResponse({
|
|
715
|
+
bot,
|
|
716
|
+
chatId,
|
|
717
|
+
link,
|
|
718
|
+
terminal,
|
|
719
|
+
prompt,
|
|
720
|
+
sinceCursor,
|
|
721
|
+
editMessageId,
|
|
722
|
+
monitorKey,
|
|
723
|
+
monitorToken,
|
|
724
|
+
}) {
|
|
725
|
+
const lang = languageFor(link);
|
|
726
|
+
const startedAt = Date.now();
|
|
727
|
+
let lastCleanOutput = '';
|
|
728
|
+
let lastOutputChangeAt = startedAt;
|
|
729
|
+
let lastEditAt = 0;
|
|
730
|
+
|
|
731
|
+
const isCurrent = () => terminalBridgeMonitors.get(monitorKey) === monitorToken;
|
|
732
|
+
|
|
733
|
+
try {
|
|
734
|
+
while (isCurrent() && Date.now() - startedAt < TERMINAL_BRIDGE_TIMEOUT_MS) {
|
|
735
|
+
await new Promise((resolve) => setTimeout(resolve, TERMINAL_BRIDGE_POLL_MS));
|
|
736
|
+
if (!isCurrent()) return;
|
|
737
|
+
|
|
738
|
+
const data = await localApi(
|
|
739
|
+
link.user_id,
|
|
740
|
+
terminalOutputUrl(terminal, 12000, sinceCursor),
|
|
741
|
+
{ timeoutMs: 12_000 },
|
|
742
|
+
);
|
|
743
|
+
if (data?.active === false) {
|
|
744
|
+
await send(bot, chatId, renderTerminalBridgeProgress(lang, terminal, {
|
|
745
|
+
output: lastCleanOutput,
|
|
746
|
+
statusKey: 'control.terminalNotRunning',
|
|
747
|
+
startedAt,
|
|
748
|
+
final: true,
|
|
749
|
+
terminalState: 'not running',
|
|
750
|
+
}), {
|
|
751
|
+
editMessageId,
|
|
752
|
+
parse_mode: undefined,
|
|
753
|
+
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
754
|
+
});
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const cleanOutput = cleanTerminalBridgeOutput(data?.output, prompt);
|
|
758
|
+
const terminalState = data?.terminalState || data?.lifecycleState || 'unknown';
|
|
759
|
+
const now = Date.now();
|
|
760
|
+
|
|
761
|
+
if (cleanOutput && cleanOutput !== lastCleanOutput) {
|
|
762
|
+
lastCleanOutput = cleanOutput;
|
|
763
|
+
lastOutputChangeAt = now;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const finishedByState = ['idle', 'completed', 'failed', 'exited'].includes(terminalState);
|
|
767
|
+
const finishedByQuietOutput = Boolean(lastCleanOutput) && now - lastOutputChangeAt >= TERMINAL_BRIDGE_SETTLE_MS;
|
|
768
|
+
const shouldFinish = Boolean(lastCleanOutput) && (finishedByState || finishedByQuietOutput);
|
|
769
|
+
|
|
770
|
+
if (shouldFinish) {
|
|
771
|
+
await send(bot, chatId, renderTerminalBridgeProgress(lang, terminal, {
|
|
772
|
+
output: lastCleanOutput,
|
|
773
|
+
statusKey: 'control.terminalResponseReady',
|
|
774
|
+
startedAt,
|
|
775
|
+
final: true,
|
|
776
|
+
terminalState,
|
|
777
|
+
}), {
|
|
778
|
+
editMessageId,
|
|
779
|
+
parse_mode: undefined,
|
|
780
|
+
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
781
|
+
});
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (now - lastEditAt >= TERMINAL_BRIDGE_EDIT_THROTTLE_MS) {
|
|
786
|
+
lastEditAt = now;
|
|
787
|
+
await send(bot, chatId, renderTerminalBridgeProgress(lang, terminal, {
|
|
788
|
+
output: lastCleanOutput,
|
|
789
|
+
statusKey: lastCleanOutput ? 'control.terminalResponding' : 'control.terminalWaiting',
|
|
790
|
+
startedAt,
|
|
791
|
+
terminalState,
|
|
792
|
+
}), {
|
|
793
|
+
editMessageId,
|
|
794
|
+
parse_mode: undefined,
|
|
795
|
+
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (!isCurrent()) return;
|
|
801
|
+
await send(bot, chatId, renderTerminalBridgeProgress(lang, terminal, {
|
|
802
|
+
output: lastCleanOutput,
|
|
803
|
+
statusKey: lastCleanOutput ? 'control.terminalStillRunning' : 'control.terminalNoReadableOutput',
|
|
804
|
+
startedAt,
|
|
805
|
+
final: Boolean(lastCleanOutput),
|
|
806
|
+
terminalState: 'running',
|
|
807
|
+
}), {
|
|
808
|
+
editMessageId,
|
|
809
|
+
parse_mode: undefined,
|
|
810
|
+
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
811
|
+
});
|
|
812
|
+
} finally {
|
|
813
|
+
if (isCurrent()) terminalBridgeMonitors.delete(monitorKey);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
export async function sendActiveTerminalAttachedNotice({ bot, chatId, link, terminal }) {
|
|
818
|
+
const lang = languageFor(link);
|
|
819
|
+
const lines = [
|
|
820
|
+
t(lang, 'control.terminalAttached'),
|
|
821
|
+
'',
|
|
822
|
+
`🤖 ${t(lang, 'control.activity.provider')}: ${terminal.provider}`,
|
|
823
|
+
`📁 ${t(lang, 'control.activity.project')}: ${compact(terminalProjectLabel(terminal), 90)}`,
|
|
824
|
+
`📌 ${t(lang, 'control.activity.status')}: ${t(lang, 'control.terminalReadyStatus')}`,
|
|
825
|
+
];
|
|
826
|
+
if (terminal.sessionId) {
|
|
827
|
+
lines.push(`🧵 ${t(lang, 'control.activity.session')}: ${terminal.sessionId}`);
|
|
828
|
+
}
|
|
829
|
+
lines.push('', t(lang, 'control.terminalReadyPrompt'));
|
|
830
|
+
await send(bot, chatId, truncate(lines.join('\n'), 3400), {
|
|
831
|
+
parse_mode: undefined,
|
|
832
|
+
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
624
836
|
async function showActiveTerminalStatus({ bot, chatId, link, editMessageId }) {
|
|
625
837
|
const lang = languageFor(link);
|
|
626
838
|
const state = getState(link.user_id);
|
|
@@ -676,7 +888,7 @@ async function sendToActiveTerminal({ bot, chatId, link, text }) {
|
|
|
676
888
|
const editMessageId = sent?.message_id || sent?.message?.message_id || null;
|
|
677
889
|
|
|
678
890
|
try {
|
|
679
|
-
await localApi(link.user_id, '/api/shell/sessions/provider-input', {
|
|
891
|
+
const inputResult = await localApi(link.user_id, '/api/shell/sessions/provider-input', {
|
|
680
892
|
method: 'POST',
|
|
681
893
|
timeoutMs: 15_000,
|
|
682
894
|
body: {
|
|
@@ -688,15 +900,35 @@ async function sendToActiveTerminal({ bot, chatId, link, text }) {
|
|
|
688
900
|
submit: true,
|
|
689
901
|
},
|
|
690
902
|
});
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
903
|
+
const sinceCursor = Number.isFinite(inputResult?.outputCursorBefore)
|
|
904
|
+
? inputResult.outputCursorBefore
|
|
905
|
+
: null;
|
|
906
|
+
const monitorKey = terminalBridgeMonitorKey(chatId, terminal);
|
|
907
|
+
const monitorToken = crypto.randomUUID();
|
|
908
|
+
terminalBridgeMonitors.set(monitorKey, monitorToken);
|
|
909
|
+
|
|
910
|
+
await send(bot, chatId, renderTerminalBridgeProgress(lang, terminal, {
|
|
911
|
+
statusKey: 'control.terminalWaiting',
|
|
912
|
+
startedAt: Date.now(),
|
|
913
|
+
terminalState: 'running',
|
|
695
914
|
}), {
|
|
696
915
|
editMessageId,
|
|
697
916
|
parse_mode: undefined,
|
|
698
917
|
reply_markup: { inline_keyboard: terminalControlKeyboard(lang) },
|
|
699
918
|
});
|
|
919
|
+
monitorTerminalBridgeResponse({
|
|
920
|
+
bot,
|
|
921
|
+
chatId,
|
|
922
|
+
link,
|
|
923
|
+
terminal,
|
|
924
|
+
prompt: text,
|
|
925
|
+
sinceCursor,
|
|
926
|
+
editMessageId,
|
|
927
|
+
monitorKey,
|
|
928
|
+
monitorToken,
|
|
929
|
+
}).catch((error) => {
|
|
930
|
+
console.warn('[telegram-control] terminal bridge monitor failed:', error?.message || error);
|
|
931
|
+
});
|
|
700
932
|
} catch (error) {
|
|
701
933
|
await send(bot, chatId, t(lang, 'control.terminalSendFailed', {
|
|
702
934
|
error: error?.message || String(error),
|
|
@@ -79,10 +79,20 @@ const EN = {
|
|
|
79
79
|
'control.recentSessions': 'Recent sessions:',
|
|
80
80
|
'control.noActiveTerminal': 'No web CLI session is attached to this Telegram chat. Open a CLI tab in Pixcode and press the Telegram button.',
|
|
81
81
|
'control.terminalAttached': '📲 Telegram is attached to this web CLI session. New plain messages will be sent to the terminal.',
|
|
82
|
+
'control.terminalReadyStatus': 'attached',
|
|
83
|
+
'control.terminalReadyPrompt': 'Write your next message here to continue this web CLI session. Use /detach to return Telegram to the normal AI router.',
|
|
82
84
|
'control.terminalNotRunning': 'The attached CLI terminal is not running anymore.',
|
|
83
85
|
'control.terminalNoOutput': 'No terminal output yet.',
|
|
86
|
+
'control.terminalOutputHidden': 'Live CLI screen output is not dumped into Telegram because full-screen terminal UIs are noisy. Keep sending messages here, or open Pixcode for the live terminal.',
|
|
84
87
|
'control.terminalSending': '📲 Sending to {{provider}} terminal on {{project}}...',
|
|
85
|
-
'control.terminalSent': '📲 Sent to the active terminal.
|
|
88
|
+
'control.terminalSent': '📲 Sent to the active terminal.',
|
|
89
|
+
'control.terminalSentHint': 'I will not dump the live terminal screen here because full-screen CLI UIs are noisy. Keep sending messages here, or open Pixcode for the live terminal.',
|
|
90
|
+
'control.terminalWaiting': 'Message sent to the active terminal. Waiting for the CLI response...',
|
|
91
|
+
'control.terminalWaitingHint': 'The same web CLI session is still attached. I will edit this message when readable output arrives.',
|
|
92
|
+
'control.terminalResponding': 'CLI response is streaming.',
|
|
93
|
+
'control.terminalResponseReady': 'CLI response is ready.',
|
|
94
|
+
'control.terminalStillRunning': 'The CLI is still running. I am leaving this Telegram bridge attached.',
|
|
95
|
+
'control.terminalNoReadableOutput': 'The message was sent, but I could not extract a clean Telegram reply yet.',
|
|
86
96
|
'control.terminalSendFailed': 'Could not send to the active terminal: {{error}}',
|
|
87
97
|
'control.terminalStatusFailed': 'Could not read the active terminal: {{error}}',
|
|
88
98
|
'control.terminalDetached': 'Telegram is detached from the web CLI session. Plain messages will use the normal AI router again.',
|
|
@@ -236,10 +246,20 @@ const TR = {
|
|
|
236
246
|
'control.recentSessions': 'Son oturumlar:',
|
|
237
247
|
'control.noActiveTerminal': 'Bu Telegram sohbetine bağlı web CLI oturumu yok. Pixcode içinde CLI tabı açıp Telegram butonuna bas.',
|
|
238
248
|
'control.terminalAttached': '📲 Telegram bu web CLI oturumuna bağlı. Düz mesajlar terminale gönderilecek.',
|
|
249
|
+
'control.terminalReadyStatus': 'bağlandı',
|
|
250
|
+
'control.terminalReadyPrompt': 'Bu web CLI oturumuna devam etmek için sonraki mesajını buraya yaz. Telegramı normal AI router akışına döndürmek için /detach kullan.',
|
|
239
251
|
'control.terminalNotRunning': 'Bağlı CLI terminali artık çalışmıyor.',
|
|
240
252
|
'control.terminalNoOutput': 'Henüz terminal çıktısı yok.',
|
|
253
|
+
'control.terminalOutputHidden': 'Canlı CLI ekran çıktısı Telegrama dökülmez; tam ekran terminal arayüzleri burada gürültülü görünür. Buradan mesaj göndermeye devam et veya canlı terminal için Pixcodeu aç.',
|
|
241
254
|
'control.terminalSending': '📲 {{project}} üzerinde {{provider}} terminaline gönderiliyor...',
|
|
242
|
-
'control.terminalSent': '📲 Aktif terminale gönderildi.
|
|
255
|
+
'control.terminalSent': '📲 Aktif terminale gönderildi.',
|
|
256
|
+
'control.terminalSentHint': 'Canlı terminal ekranını buraya dökmeyeceğim; tam ekran CLI arayüzleri Telegramda bozuk görünebilir. Buradan mesaj göndermeye devam et veya canlı terminal için Pixcodeu aç.',
|
|
257
|
+
'control.terminalWaiting': 'Mesaj aktif terminale gönderildi. CLI yanıtı bekleniyor...',
|
|
258
|
+
'control.terminalWaitingHint': 'Aynı web CLI oturumu bağlı kalıyor. Okunabilir çıktı gelince bu mesajı düzenleyeceğim.',
|
|
259
|
+
'control.terminalResponding': 'CLI yanıtı geliyor.',
|
|
260
|
+
'control.terminalResponseReady': 'CLI yanıtı hazır.',
|
|
261
|
+
'control.terminalStillRunning': 'CLI hâlâ çalışıyor. Telegram köprüsünü bağlı bırakıyorum.',
|
|
262
|
+
'control.terminalNoReadableOutput': 'Mesaj gönderildi ama henüz temiz bir Telegram yanıtı çıkaramadım.',
|
|
243
263
|
'control.terminalSendFailed': 'Aktif terminale gönderilemedi: {{error}}',
|
|
244
264
|
'control.terminalStatusFailed': 'Aktif terminal okunamadı: {{error}}',
|
|
245
265
|
'control.terminalDetached': 'Telegram web CLI oturumundan ayrıldı. Düz mesajlar tekrar normal AI router akışına gidecek.',
|