claude-tg 1.0.5 → 1.0.7
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/bin/claude-tg.js +12 -6
- package/package.json +1 -1
- package/src/daemon.js +140 -89
- package/src/hooks/notification.js +1 -1
- package/src/hooks/permission-request.js +1 -1
- package/src/setup.js +3 -2
package/bin/claude-tg.js
CHANGED
|
@@ -276,15 +276,21 @@ program
|
|
|
276
276
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
277
277
|
const hooks = settings.hooks || {};
|
|
278
278
|
|
|
279
|
+
// Extract script path from hook command (handles optional env prefix)
|
|
280
|
+
function extractScriptPath(cmd) {
|
|
281
|
+
const match = cmd.match(/node\s+(.+)$/);
|
|
282
|
+
return match ? match[1].trim() : null;
|
|
283
|
+
}
|
|
284
|
+
|
|
279
285
|
const permHooks = hooks.PermissionRequest || [];
|
|
280
286
|
const permCmd = permHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
281
287
|
console.log(` PermissionRequest: ${permCmd}`);
|
|
282
288
|
if (permCmd !== 'NOT FOUND') {
|
|
283
|
-
const scriptPath = permCmd
|
|
284
|
-
if (fs.existsSync(scriptPath)) {
|
|
289
|
+
const scriptPath = extractScriptPath(permCmd);
|
|
290
|
+
if (scriptPath && fs.existsSync(scriptPath)) {
|
|
285
291
|
console.log(' File exists: YES');
|
|
286
292
|
} else {
|
|
287
|
-
console.log(` File exists: NO — ${scriptPath}`);
|
|
293
|
+
console.log(` File exists: NO — ${scriptPath || permCmd}`);
|
|
288
294
|
ok = false;
|
|
289
295
|
}
|
|
290
296
|
}
|
|
@@ -293,11 +299,11 @@ program
|
|
|
293
299
|
const notifCmd = notifHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
294
300
|
console.log(` Notification: ${notifCmd}`);
|
|
295
301
|
if (notifCmd !== 'NOT FOUND') {
|
|
296
|
-
const scriptPath = notifCmd
|
|
297
|
-
if (fs.existsSync(scriptPath)) {
|
|
302
|
+
const scriptPath = extractScriptPath(notifCmd);
|
|
303
|
+
if (scriptPath && fs.existsSync(scriptPath)) {
|
|
298
304
|
console.log(' File exists: YES');
|
|
299
305
|
} else {
|
|
300
|
-
console.log(` File exists: NO — ${scriptPath}`);
|
|
306
|
+
console.log(` File exists: NO — ${scriptPath || notifCmd}`);
|
|
301
307
|
ok = false;
|
|
302
308
|
}
|
|
303
309
|
}
|
package/package.json
CHANGED
package/src/daemon.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
2
|
const crypto = require('crypto');
|
|
3
|
-
const { execSync } = require('child_process');
|
|
3
|
+
const { execSync, exec } = require('child_process');
|
|
4
|
+
const util = require('util');
|
|
5
|
+
const execAsync = util.promisify(exec);
|
|
4
6
|
const { Telegraf, Markup } = require('telegraf');
|
|
5
7
|
const { loadConfig, LOG_PATH } = require('./config');
|
|
6
8
|
const fs = require('fs');
|
|
@@ -91,25 +93,46 @@ function isTtyAlive(ttyPath) {
|
|
|
91
93
|
* Escape a string for use inside AppleScript double-quoted strings.
|
|
92
94
|
*/
|
|
93
95
|
function escapeAppleScript(str) {
|
|
94
|
-
return str
|
|
96
|
+
return str
|
|
97
|
+
.replace(/\\/g, '\\\\')
|
|
98
|
+
.replace(/"/g, '\\"')
|
|
99
|
+
.replace(/\n/g, '\\n')
|
|
100
|
+
.replace(/\r/g, '\\r')
|
|
101
|
+
.replace(/\t/g, '\\t');
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
/**
|
|
98
105
|
* Send text as input to a terminal session identified by its TTY path.
|
|
99
106
|
* Uses osascript to type into the correct terminal tab/session.
|
|
100
|
-
* Tries iTerm2 first, then Terminal.app.
|
|
107
|
+
* Tries iTerm2 first, then Terminal.app. Non-blocking (async).
|
|
108
|
+
* Returns { ok: true } on success, { ok: false, error: string } on failure.
|
|
101
109
|
*/
|
|
102
|
-
function sendInputToTerminal(ttyPath, text) {
|
|
110
|
+
async function sendInputToTerminal(ttyPath, text) {
|
|
103
111
|
if (!ttyPath) {
|
|
104
112
|
log('sendInput: no TTY path');
|
|
105
|
-
return false;
|
|
113
|
+
return { ok: false, error: 'No TTY path for this session' };
|
|
106
114
|
}
|
|
107
115
|
|
|
108
|
-
const
|
|
116
|
+
const trimmed = text.trim();
|
|
117
|
+
const escaped = escapeAppleScript(trimmed);
|
|
109
118
|
|
|
110
|
-
//
|
|
119
|
+
// Check which terminal apps are running (fast, non-blocking check)
|
|
120
|
+
let itermRunning = false;
|
|
121
|
+
let terminalRunning = false;
|
|
111
122
|
try {
|
|
112
|
-
const
|
|
123
|
+
const { stdout } = await execAsync('ps -c -o comm= | grep -E "^(iTerm2|Terminal)$"', { timeout: 2000 });
|
|
124
|
+
itermRunning = stdout.includes('iTerm2');
|
|
125
|
+
terminalRunning = stdout.includes('Terminal');
|
|
126
|
+
} catch {
|
|
127
|
+
// ps/grep failed, try both
|
|
128
|
+
itermRunning = true;
|
|
129
|
+
terminalRunning = true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Try iTerm2 — write text targets a specific session by TTY, no focus needed
|
|
133
|
+
if (itermRunning) {
|
|
134
|
+
try {
|
|
135
|
+
const script = `
|
|
113
136
|
tell application "iTerm2"
|
|
114
137
|
repeat with w in windows
|
|
115
138
|
repeat with t in tabs of w
|
|
@@ -122,50 +145,58 @@ tell application "iTerm2"
|
|
|
122
145
|
end repeat
|
|
123
146
|
end repeat
|
|
124
147
|
end tell`;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
await execAsync(`osascript -e '${script.replace(/'/g, "'\\''")}'`, { timeout: 5000 });
|
|
149
|
+
log(`Sent via iTerm2 to ${ttyPath}: ${truncate(text, 80)}`);
|
|
150
|
+
return { ok: true };
|
|
151
|
+
} catch (err) {
|
|
152
|
+
log(`iTerm2 attempt: ${(err.message || '').slice(0, 100)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
129
155
|
|
|
130
156
|
// Try Terminal.app — focus the tab, paste text from clipboard, press Enter
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
if (terminalRunning) {
|
|
158
|
+
try {
|
|
159
|
+
const tmpTextFile = '/tmp/claude-tg-input.txt';
|
|
160
|
+
fs.writeFileSync(tmpTextFile, trimmed);
|
|
161
|
+
|
|
162
|
+
const script = [
|
|
163
|
+
'tell application "Terminal"',
|
|
164
|
+
' activate',
|
|
165
|
+
' repeat with w in windows',
|
|
166
|
+
' repeat with t in tabs of w',
|
|
167
|
+
` if tty of t is "${ttyPath}" then`,
|
|
168
|
+
' set selected tab of w to t',
|
|
169
|
+
' set index of w to 1',
|
|
170
|
+
' end if',
|
|
171
|
+
' end repeat',
|
|
172
|
+
' end repeat',
|
|
173
|
+
'end tell',
|
|
174
|
+
`do shell script "cat ${tmpTextFile} | /usr/bin/pbcopy"`,
|
|
175
|
+
'delay 0.5',
|
|
176
|
+
'tell application "System Events"',
|
|
177
|
+
' tell process "Terminal"',
|
|
178
|
+
' keystroke "v" using command down',
|
|
179
|
+
' delay 0.3',
|
|
180
|
+
' key code 36',
|
|
181
|
+
' end tell',
|
|
182
|
+
'end tell',
|
|
183
|
+
].join('\n');
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync('/tmp/claude-tg-input.scpt', script);
|
|
186
|
+
await execAsync('osascript /tmp/claude-tg-input.scpt', { timeout: 15000 });
|
|
187
|
+
log(`Sent via Terminal.app to ${ttyPath}: ${truncate(text, 80)}`);
|
|
188
|
+
return { ok: true };
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const msg = err.message || err.stderr || '';
|
|
191
|
+
log(`Terminal.app send error: ${msg.slice(0, 200)}`);
|
|
192
|
+
if (msg.includes('assistive') || msg.includes('accessibility') || msg.includes('not allowed')) {
|
|
193
|
+
return { ok: false, error: 'Accessibility permission needed.\nSystem Settings → Privacy & Security → Accessibility → enable Terminal/iTerm2.' };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
165
196
|
}
|
|
166
197
|
|
|
167
198
|
log(`sendInput failed: no terminal found for ${ttyPath}`);
|
|
168
|
-
return false;
|
|
199
|
+
return { ok: false, error: `Could not send to terminal (${ttyPath}). Make sure iTerm2 or Terminal.app is open.` };
|
|
169
200
|
}
|
|
170
201
|
|
|
171
202
|
// --- Transcript reading ---
|
|
@@ -424,9 +455,13 @@ async function sendElicitationQuestion(elicId) {
|
|
|
424
455
|
}
|
|
425
456
|
|
|
426
457
|
const keyboard = Markup.inlineKeyboard(rows);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
458
|
+
try {
|
|
459
|
+
const sent = await bot.telegram.sendMessage(config.chatId, msg, keyboard);
|
|
460
|
+
elic.telegramMessageIds.push(sent.message_id);
|
|
461
|
+
elic.currentMessageId = sent.message_id;
|
|
462
|
+
} catch (err) {
|
|
463
|
+
log(`Elicitation question send failed: ${err.message}`);
|
|
464
|
+
}
|
|
430
465
|
}
|
|
431
466
|
|
|
432
467
|
/**
|
|
@@ -467,8 +502,12 @@ async function sendElicitationSummary(elicId) {
|
|
|
467
502
|
}
|
|
468
503
|
const keyboard = Markup.inlineKeyboard(summaryButtons);
|
|
469
504
|
|
|
470
|
-
|
|
471
|
-
|
|
505
|
+
try {
|
|
506
|
+
const sent = await bot.telegram.sendMessage(config.chatId, msg, keyboard);
|
|
507
|
+
elic.telegramMessageIds.push(sent.message_id);
|
|
508
|
+
} catch (err) {
|
|
509
|
+
log(`Elicitation summary send failed: ${err.message}`);
|
|
510
|
+
}
|
|
472
511
|
}
|
|
473
512
|
|
|
474
513
|
/**
|
|
@@ -503,8 +542,8 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
503
542
|
elic.permissionResolve({ decision: 'allow' });
|
|
504
543
|
try { await ctx.editMessageText(ctx.callbackQuery.message.text + '\n\n✅ Answers submitted — injecting...'); } catch {}
|
|
505
544
|
|
|
506
|
-
setTimeout(() => {
|
|
507
|
-
const ok = injectElicitationAnswers(elic.ttyPath, elic.questions, elic.answers);
|
|
545
|
+
setTimeout(async () => {
|
|
546
|
+
const ok = await injectElicitationAnswers(elic.ttyPath, elic.questions, elic.answers);
|
|
508
547
|
log(`Elicitation ${elicId}: ${ok ? 'keystrokes injected' : 'injection failed'}`);
|
|
509
548
|
if (!ok) {
|
|
510
549
|
bot.telegram.sendMessage(config.chatId, '⚠️ Could not inject answers into terminal').catch(() => {});
|
|
@@ -512,7 +551,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
512
551
|
}, 2000);
|
|
513
552
|
} else {
|
|
514
553
|
// From notification flow — inject immediately
|
|
515
|
-
const ok = injectElicitationAnswers(elic.ttyPath, elic.questions, elic.answers);
|
|
554
|
+
const ok = await injectElicitationAnswers(elic.ttyPath, elic.questions, elic.answers);
|
|
516
555
|
try {
|
|
517
556
|
await ctx.editMessageText(ctx.callbackQuery.message.text +
|
|
518
557
|
(ok ? '\n\n✅ Answers submitted' : '\n\n⚠️ Could not send to terminal'));
|
|
@@ -565,14 +604,18 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
565
604
|
try { await ctx.editMessageReplyMarkup(undefined); } catch {}
|
|
566
605
|
try { await ctx.editMessageText(ctx.callbackQuery.message.text + '\n\n✏️ Selected: Custom'); } catch {}
|
|
567
606
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
607
|
+
try {
|
|
608
|
+
const prompt = await bot.telegram.sendMessage(
|
|
609
|
+
config.chatId,
|
|
610
|
+
`✏️ Type your custom answer for: "${q.question}"\n\nReply to this message with your answer.`
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
elic.customWaitingMessageId = prompt.message_id;
|
|
614
|
+
elic.customWaitingQIdx = qIdx;
|
|
615
|
+
elic.telegramMessageIds.push(prompt.message_id);
|
|
616
|
+
} catch (err) {
|
|
617
|
+
log(`Elicitation custom prompt send failed: ${err.message}`);
|
|
618
|
+
}
|
|
576
619
|
log(`Elicitation ${elicId}: waiting for custom answer to q${qIdx}`);
|
|
577
620
|
return;
|
|
578
621
|
}
|
|
@@ -671,7 +714,7 @@ async function handleElicitationCallback(ctx, cbData) {
|
|
|
671
714
|
* Inject elicitation answers into the terminal via osascript keystrokes.
|
|
672
715
|
* Navigates the AskUserQuestion form using arrow keys, space, tab, and enter.
|
|
673
716
|
*/
|
|
674
|
-
function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
717
|
+
async function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
675
718
|
if (!ttyPath) {
|
|
676
719
|
log('injectElicitation: no TTY path');
|
|
677
720
|
return false;
|
|
@@ -686,44 +729,38 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
686
729
|
if (!answer) continue;
|
|
687
730
|
|
|
688
731
|
if (answer.isCustom) {
|
|
689
|
-
// Navigate to "Other" (last position, after all defined options)
|
|
690
732
|
const otherPos = q.options.length;
|
|
691
733
|
for (let i = 0; i < otherPos; i++) {
|
|
692
734
|
events.push({ type: 'key_code', value: 125 }); // arrow down
|
|
693
735
|
}
|
|
694
736
|
events.push({ type: 'key_code', value: 36 }); // enter to select Other
|
|
695
737
|
events.push({ type: 'delay', value: 0.3 });
|
|
696
|
-
events.push({ type: 'keystroke', value: answer.customText });
|
|
738
|
+
events.push({ type: 'keystroke', value: answer.customText });
|
|
697
739
|
} else if (answer.multiSelections) {
|
|
698
|
-
// MultiSelect: walk through all options, space on selected ones
|
|
699
740
|
const selectedSet = new Set(answer.multiSelections.map((s) => s.optionIndex));
|
|
700
741
|
for (let i = 0; i < q.options.length; i++) {
|
|
701
742
|
if (selectedSet.has(i)) {
|
|
702
|
-
events.push({ type: 'keystroke', value: ' ' });
|
|
743
|
+
events.push({ type: 'keystroke', value: ' ' });
|
|
703
744
|
}
|
|
704
745
|
if (i < q.options.length - 1) {
|
|
705
|
-
events.push({ type: 'key_code', value: 125 });
|
|
746
|
+
events.push({ type: 'key_code', value: 125 });
|
|
706
747
|
}
|
|
707
748
|
}
|
|
708
749
|
} else {
|
|
709
|
-
// Single select: navigate to selected option
|
|
710
750
|
for (let i = 0; i < answer.optionIndex; i++) {
|
|
711
|
-
events.push({ type: 'key_code', value: 125 });
|
|
751
|
+
events.push({ type: 'key_code', value: 125 });
|
|
712
752
|
}
|
|
713
753
|
}
|
|
714
754
|
|
|
715
|
-
// Tab to next question, or nothing for the last one
|
|
716
755
|
if (qIdx < questions.length - 1) {
|
|
717
756
|
events.push({ type: 'key_code', value: 48 }); // tab
|
|
718
757
|
events.push({ type: 'delay', value: 0.15 });
|
|
719
758
|
}
|
|
720
759
|
}
|
|
721
760
|
|
|
722
|
-
// Submit the form
|
|
723
761
|
events.push({ type: 'delay', value: 0.3 });
|
|
724
762
|
events.push({ type: 'key_code', value: 36 }); // Return key
|
|
725
763
|
|
|
726
|
-
// Build key action lines for AppleScript
|
|
727
764
|
const keyLines = events.map((e) => {
|
|
728
765
|
if (e.type === 'key_code') return ` key code ${e.value}`;
|
|
729
766
|
if (e.type === 'keystroke') {
|
|
@@ -735,7 +772,7 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
735
772
|
return '';
|
|
736
773
|
}).join('\n');
|
|
737
774
|
|
|
738
|
-
// Try iTerm2 first
|
|
775
|
+
// Try iTerm2 first
|
|
739
776
|
try {
|
|
740
777
|
const script = [
|
|
741
778
|
'tell application "iTerm2"',
|
|
@@ -759,7 +796,7 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
759
796
|
].join('\n');
|
|
760
797
|
|
|
761
798
|
fs.writeFileSync('/tmp/claude-tg-elicit.scpt', script);
|
|
762
|
-
|
|
799
|
+
await execAsync('osascript /tmp/claude-tg-elicit.scpt', { timeout: 30000 });
|
|
763
800
|
log(`Elicitation keystrokes sent via iTerm2 to ${ttyPath}`);
|
|
764
801
|
return true;
|
|
765
802
|
} catch {}
|
|
@@ -768,11 +805,12 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
768
805
|
try {
|
|
769
806
|
const script = [
|
|
770
807
|
'tell application "Terminal"',
|
|
808
|
+
' activate',
|
|
771
809
|
' repeat with w in windows',
|
|
772
810
|
' repeat with t in tabs of w',
|
|
773
811
|
` if tty of t is "${ttyPath}" then`,
|
|
774
812
|
' set selected tab of w to t',
|
|
775
|
-
' set
|
|
813
|
+
' set index of w to 1',
|
|
776
814
|
' end if',
|
|
777
815
|
' end repeat',
|
|
778
816
|
' end repeat',
|
|
@@ -786,7 +824,7 @@ function injectElicitationAnswers(ttyPath, questions, answers) {
|
|
|
786
824
|
].join('\n');
|
|
787
825
|
|
|
788
826
|
fs.writeFileSync('/tmp/claude-tg-elicit.scpt', script);
|
|
789
|
-
|
|
827
|
+
await execAsync('osascript /tmp/claude-tg-elicit.scpt', { timeout: 30000 });
|
|
790
828
|
log(`Elicitation keystrokes sent via Terminal.app to ${ttyPath}`);
|
|
791
829
|
return true;
|
|
792
830
|
} catch (err) {
|
|
@@ -888,6 +926,9 @@ function startBot() {
|
|
|
888
926
|
} else if (action === 'always') {
|
|
889
927
|
label = '✅ Always Allowed';
|
|
890
928
|
pending.resolve({ decision: 'always' });
|
|
929
|
+
} else {
|
|
930
|
+
label = '❌ Denied';
|
|
931
|
+
pending.resolve({ decision: 'deny' });
|
|
891
932
|
}
|
|
892
933
|
|
|
893
934
|
try { await ctx.answerCbQuery(label); } catch {}
|
|
@@ -997,12 +1038,12 @@ function startBot() {
|
|
|
997
1038
|
return;
|
|
998
1039
|
}
|
|
999
1040
|
|
|
1000
|
-
const
|
|
1001
|
-
if (ok) {
|
|
1041
|
+
const result = await sendInputToTerminal(session.ttyPath, text);
|
|
1042
|
+
if (result.ok) {
|
|
1002
1043
|
const num = getSessionLabel(targetSessionId);
|
|
1003
1044
|
try { await ctx.reply(`➡️ Sent to #${num} ${session.label}`); } catch {}
|
|
1004
1045
|
} else {
|
|
1005
|
-
try { await ctx.reply(`⚠️
|
|
1046
|
+
try { await ctx.reply(`⚠️ ${result.error}`); } catch {}
|
|
1006
1047
|
}
|
|
1007
1048
|
});
|
|
1008
1049
|
|
|
@@ -1035,7 +1076,13 @@ async function sendPermissionRequest(data) {
|
|
|
1035
1076
|
Markup.button.callback('Always Allow', `${rid}:always`),
|
|
1036
1077
|
]);
|
|
1037
1078
|
|
|
1038
|
-
|
|
1079
|
+
let sent;
|
|
1080
|
+
try {
|
|
1081
|
+
sent = await bot.telegram.sendMessage(config.chatId, msg, keyboard);
|
|
1082
|
+
} catch (err) {
|
|
1083
|
+
log(`Permission send failed: ${err.message}`);
|
|
1084
|
+
return { decision: 'allow' }; // Fallback: allow if Telegram unreachable
|
|
1085
|
+
}
|
|
1039
1086
|
|
|
1040
1087
|
// Track this message for reply routing
|
|
1041
1088
|
messageToSession.set(sent.message_id, {
|
|
@@ -1134,15 +1181,19 @@ async function sendNotification(data) {
|
|
|
1134
1181
|
// Default notification flow
|
|
1135
1182
|
const context = extractContext(data.transcript_path);
|
|
1136
1183
|
const msg = formatNotification(data, context);
|
|
1137
|
-
|
|
1184
|
+
try {
|
|
1185
|
+
const sent = await bot.telegram.sendMessage(config.chatId, msg);
|
|
1138
1186
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1187
|
+
messageToSession.set(sent.message_id, {
|
|
1188
|
+
sessionId: data.session_id,
|
|
1189
|
+
type: 'notification',
|
|
1190
|
+
createdAt: Date.now(),
|
|
1191
|
+
});
|
|
1144
1192
|
|
|
1145
|
-
|
|
1193
|
+
log(`Notification sent: ${notifType} (msg ${sent.message_id})`);
|
|
1194
|
+
} catch (err) {
|
|
1195
|
+
log(`Notification send failed: ${err.message}`);
|
|
1196
|
+
}
|
|
1146
1197
|
}
|
|
1147
1198
|
|
|
1148
1199
|
function readBody(req) {
|
package/src/setup.js
CHANGED
|
@@ -25,13 +25,14 @@ function telegramApiCall(token, method) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function getHooksConfig(port) {
|
|
28
|
+
const envPrefix = port !== 7483 ? `CLAUDE_TG_PORT=${port} ` : '';
|
|
28
29
|
return {
|
|
29
30
|
PermissionRequest: [
|
|
30
31
|
{
|
|
31
32
|
hooks: [
|
|
32
33
|
{
|
|
33
34
|
type: 'command',
|
|
34
|
-
command:
|
|
35
|
+
command: `${envPrefix}node ${path.join(HOOKS_DIR, 'permission-request.js')}`,
|
|
35
36
|
timeout: 1800,
|
|
36
37
|
statusMessage: 'Waiting for Telegram approval...',
|
|
37
38
|
},
|
|
@@ -44,7 +45,7 @@ function getHooksConfig(port) {
|
|
|
44
45
|
hooks: [
|
|
45
46
|
{
|
|
46
47
|
type: 'command',
|
|
47
|
-
command:
|
|
48
|
+
command: `${envPrefix}node ${path.join(HOOKS_DIR, 'notification.js')}`,
|
|
48
49
|
async: true,
|
|
49
50
|
},
|
|
50
51
|
],
|