claude-tg 1.0.4 → 1.0.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/package.json +1 -1
- package/src/daemon.js +42 -32
package/package.json
CHANGED
package/src/daemon.js
CHANGED
|
@@ -127,28 +127,33 @@ end tell`;
|
|
|
127
127
|
return true;
|
|
128
128
|
} catch {}
|
|
129
129
|
|
|
130
|
-
// Try Terminal.app — focus the tab,
|
|
130
|
+
// Try Terminal.app — focus the tab, paste text from clipboard, press Enter
|
|
131
131
|
try {
|
|
132
|
+
// Use clipboard paste instead of keystroke (reliable for any text length)
|
|
133
|
+
const tmpTextFile = '/tmp/claude-tg-input.txt';
|
|
134
|
+
fs.writeFileSync(tmpTextFile, text.trim());
|
|
135
|
+
|
|
132
136
|
const script = [
|
|
133
137
|
'tell application "Terminal"',
|
|
138
|
+
' activate',
|
|
134
139
|
' repeat with w in windows',
|
|
135
140
|
' repeat with t in tabs of w',
|
|
136
141
|
` if tty of t is "${ttyPath}" then`,
|
|
137
142
|
' set selected tab of w to t',
|
|
138
|
-
' set
|
|
139
|
-
' delay 0.3',
|
|
140
|
-
' tell application "System Events"',
|
|
141
|
-
' tell process "Terminal"',
|
|
142
|
-
` keystroke "${escaped}"`,
|
|
143
|
-
' delay 0.2',
|
|
144
|
-
' keystroke return',
|
|
145
|
-
' end tell',
|
|
146
|
-
' end tell',
|
|
147
|
-
' return "ok"',
|
|
143
|
+
' set index of w to 1',
|
|
148
144
|
' end if',
|
|
149
145
|
' end repeat',
|
|
150
146
|
' end repeat',
|
|
151
147
|
'end tell',
|
|
148
|
+
`do shell script "cat ${tmpTextFile} | /usr/bin/pbcopy"`,
|
|
149
|
+
'delay 0.5',
|
|
150
|
+
'tell application "System Events"',
|
|
151
|
+
' tell process "Terminal"',
|
|
152
|
+
' keystroke "v" using command down',
|
|
153
|
+
' delay 0.3',
|
|
154
|
+
' key code 36',
|
|
155
|
+
' end tell',
|
|
156
|
+
'end tell',
|
|
152
157
|
].join('\n');
|
|
153
158
|
|
|
154
159
|
fs.writeFileSync('/tmp/claude-tg-input.scpt', script);
|
|
@@ -482,7 +487,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
482
487
|
}
|
|
483
488
|
|
|
484
489
|
if (!elicId) {
|
|
485
|
-
ctx.answerCbQuery('Expired or already answered.');
|
|
490
|
+
try { await ctx.answerCbQuery('Expired or already answered.'); } catch {}
|
|
486
491
|
return;
|
|
487
492
|
}
|
|
488
493
|
|
|
@@ -550,7 +555,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
550
555
|
const q = elic.questions[qIdx];
|
|
551
556
|
|
|
552
557
|
if (!q) {
|
|
553
|
-
ctx.answerCbQuery('Invalid question.');
|
|
558
|
+
try { await ctx.answerCbQuery('Invalid question.'); } catch {}
|
|
554
559
|
return;
|
|
555
560
|
}
|
|
556
561
|
|
|
@@ -578,7 +583,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
578
583
|
const selected = toggles.get(qIdx) || new Set();
|
|
579
584
|
|
|
580
585
|
if (selected.size === 0) {
|
|
581
|
-
ctx.answerCbQuery('Select at least one option.');
|
|
586
|
+
try { await ctx.answerCbQuery('Select at least one option.'); } catch {}
|
|
582
587
|
return;
|
|
583
588
|
}
|
|
584
589
|
|
|
@@ -610,7 +615,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
610
615
|
const opt = q.options[optIdx];
|
|
611
616
|
|
|
612
617
|
if (!opt) {
|
|
613
|
-
ctx.answerCbQuery('Invalid option.');
|
|
618
|
+
try { await ctx.answerCbQuery('Invalid option.'); } catch {}
|
|
614
619
|
return;
|
|
615
620
|
}
|
|
616
621
|
|
|
@@ -622,10 +627,10 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
622
627
|
|
|
623
628
|
if (selected.has(optIdx)) {
|
|
624
629
|
selected.delete(optIdx);
|
|
625
|
-
ctx.answerCbQuery(`Deselected: ${opt.label}`);
|
|
630
|
+
try { await ctx.answerCbQuery(`Deselected: ${opt.label}`); } catch {}
|
|
626
631
|
} else {
|
|
627
632
|
selected.add(optIdx);
|
|
628
|
-
ctx.answerCbQuery(`Selected: ${opt.label}`);
|
|
633
|
+
try { await ctx.answerCbQuery(`Selected: ${opt.label}`); } catch {}
|
|
629
634
|
}
|
|
630
635
|
|
|
631
636
|
// Rebuild buttons with selection indicators
|
|
@@ -715,8 +720,8 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
715
720
|
}
|
|
716
721
|
|
|
717
722
|
// Submit the form
|
|
718
|
-
events.push({ type: 'delay', value: 0.
|
|
719
|
-
events.push({ type: '
|
|
723
|
+
events.push({ type: 'delay', value: 0.3 });
|
|
724
|
+
events.push({ type: 'key_code', value: 36 }); // Return key
|
|
720
725
|
|
|
721
726
|
// Build key action lines for AppleScript
|
|
722
727
|
const keyLines = events.map((e) => {
|
|
@@ -797,13 +802,13 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
797
802
|
function startBot() {
|
|
798
803
|
bot = new Telegraf(config.botToken);
|
|
799
804
|
|
|
800
|
-
bot.command('start', (ctx) => {
|
|
805
|
+
bot.command('start', async (ctx) => {
|
|
801
806
|
const chatId = ctx.chat.id.toString();
|
|
802
|
-
ctx.reply(`Chat ID registered: ${chatId}\n\nThis chat will receive Claude Code permission requests.`);
|
|
807
|
+
try { await ctx.reply(`Chat ID registered: ${chatId}\n\nThis chat will receive Claude Code permission requests.`); } catch {}
|
|
803
808
|
log(`/start from chat ${chatId}`);
|
|
804
809
|
});
|
|
805
810
|
|
|
806
|
-
bot.command('status', (ctx) => {
|
|
811
|
+
bot.command('status', async (ctx) => {
|
|
807
812
|
const pendingCount = pendingQuestions.size;
|
|
808
813
|
const activeSessions = [...sessions.entries()].filter(([, s]) => {
|
|
809
814
|
if (s.ttyPath) return isTtyAlive(s.ttyPath);
|
|
@@ -812,7 +817,7 @@ function startBot() {
|
|
|
812
817
|
|
|
813
818
|
let msg = '';
|
|
814
819
|
if (activeSessions.length === 0 && pendingCount === 0) {
|
|
815
|
-
ctx.reply('No active sessions or pending requests.');
|
|
820
|
+
try { await ctx.reply('No active sessions or pending requests.'); } catch {}
|
|
816
821
|
return;
|
|
817
822
|
}
|
|
818
823
|
|
|
@@ -842,7 +847,7 @@ function startBot() {
|
|
|
842
847
|
}
|
|
843
848
|
}
|
|
844
849
|
|
|
845
|
-
ctx.reply(msg);
|
|
850
|
+
try { await ctx.reply(msg); } catch {}
|
|
846
851
|
});
|
|
847
852
|
|
|
848
853
|
// Handle inline keyboard button presses (permission decisions + elicitations)
|
|
@@ -866,7 +871,7 @@ function startBot() {
|
|
|
866
871
|
}
|
|
867
872
|
|
|
868
873
|
if (!requestId) {
|
|
869
|
-
ctx.answerCbQuery('Request expired or already answered.');
|
|
874
|
+
try { await ctx.answerCbQuery('Request expired or already answered.'); } catch {}
|
|
870
875
|
return;
|
|
871
876
|
}
|
|
872
877
|
|
|
@@ -917,7 +922,7 @@ function startBot() {
|
|
|
917
922
|
elic.customWaitingMessageId = null;
|
|
918
923
|
elic.customWaitingQIdx = null;
|
|
919
924
|
|
|
920
|
-
ctx.reply(`✅ Custom answer for "${q.question}": ${text}`);
|
|
925
|
+
try { await ctx.reply(`✅ Custom answer for "${q.question}": ${text}`); } catch {}
|
|
921
926
|
log(`Elicitation ${elicId}: q${qIdx} custom = "${text}"`);
|
|
922
927
|
|
|
923
928
|
await sendElicitationQuestion(elicId);
|
|
@@ -933,7 +938,7 @@ function startBot() {
|
|
|
933
938
|
const mapping = messageToSession.get(replyTo);
|
|
934
939
|
if (mapping) {
|
|
935
940
|
if (mapping.type === 'permission') {
|
|
936
|
-
ctx.reply('⚠️ That was a permission request — use the buttons above.\nTo send text input, reply to a notification message.');
|
|
941
|
+
try { await ctx.reply('⚠️ That was a permission request — use the buttons above.\nTo send text input, reply to a notification message.'); } catch {}
|
|
937
942
|
return;
|
|
938
943
|
}
|
|
939
944
|
targetSessionId = mapping.sessionId;
|
|
@@ -971,7 +976,7 @@ function startBot() {
|
|
|
971
976
|
if (uniqueSessions.length === 1) {
|
|
972
977
|
targetSessionId = uniqueSessions[0];
|
|
973
978
|
} else if (uniqueSessions.length === 0) {
|
|
974
|
-
ctx.reply('No idle Claude sessions to send input to.');
|
|
979
|
+
try { await ctx.reply('No idle Claude sessions to send input to.'); } catch {}
|
|
975
980
|
return;
|
|
976
981
|
} else {
|
|
977
982
|
// Multiple sessions — ask user to be specific
|
|
@@ -980,7 +985,7 @@ function startBot() {
|
|
|
980
985
|
const num = getSessionLabel(sid);
|
|
981
986
|
return ` #${num} ${s?.label || 'unknown'}`;
|
|
982
987
|
}).join('\n');
|
|
983
|
-
ctx.reply(`Multiple sessions are waiting. Reply to a specific notification message to choose:\n\n${labels}`);
|
|
988
|
+
try { await ctx.reply(`Multiple sessions are waiting. Reply to a specific notification message to choose:\n\n${labels}`); } catch {}
|
|
984
989
|
return;
|
|
985
990
|
}
|
|
986
991
|
}
|
|
@@ -988,16 +993,16 @@ function startBot() {
|
|
|
988
993
|
// Send the text to the terminal
|
|
989
994
|
const session = sessions.get(targetSessionId);
|
|
990
995
|
if (!session || !session.ttyPath) {
|
|
991
|
-
ctx.reply(`⚠️ No TTY for session #${getSessionLabel(targetSessionId)}. Open the terminal to respond.`);
|
|
996
|
+
try { await ctx.reply(`⚠️ No TTY for session #${getSessionLabel(targetSessionId)}. Open the terminal to respond.`); } catch {}
|
|
992
997
|
return;
|
|
993
998
|
}
|
|
994
999
|
|
|
995
1000
|
const ok = sendInputToTerminal(session.ttyPath, text);
|
|
996
1001
|
if (ok) {
|
|
997
1002
|
const num = getSessionLabel(targetSessionId);
|
|
998
|
-
ctx.reply(`➡️ Sent to #${num} ${session.label}`);
|
|
1003
|
+
try { await ctx.reply(`➡️ Sent to #${num} ${session.label}`); } catch {}
|
|
999
1004
|
} else {
|
|
1000
|
-
ctx.reply(`⚠️ Could not send to terminal. Session may have ended, or terminal app not recognized.`);
|
|
1005
|
+
try { await ctx.reply(`⚠️ Could not send to terminal. Session may have ended, or terminal app not recognized.`); } catch {}
|
|
1001
1006
|
}
|
|
1002
1007
|
});
|
|
1003
1008
|
|
|
@@ -1269,6 +1274,11 @@ function main() {
|
|
|
1269
1274
|
log(`Unhandled rejection: ${err?.message || err}`);
|
|
1270
1275
|
});
|
|
1271
1276
|
|
|
1277
|
+
process.on('uncaughtException', (err) => {
|
|
1278
|
+
log(`Uncaught exception: ${err?.message || err}`);
|
|
1279
|
+
// Don't crash — log and continue
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1272
1282
|
process.on('SIGTERM', () => {
|
|
1273
1283
|
log('Received SIGTERM, shutting down');
|
|
1274
1284
|
bot.stop('SIGTERM');
|