claude-tg 1.0.8 → 1.0.10
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 +36 -3
- package/package.json +1 -1
- package/src/hooks/notification.js +20 -3
- package/src/hooks/permission-request.js +20 -4
- package/src/setup.js +5 -3
package/bin/claude-tg.js
CHANGED
|
@@ -140,6 +140,25 @@ daemon
|
|
|
140
140
|
}
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
const HOOKS_LOG_PATH = path.join(CONFIG_DIR, 'hooks.log');
|
|
144
|
+
|
|
145
|
+
program
|
|
146
|
+
.command('hooks-log')
|
|
147
|
+
.description('Show hook execution log (diagnose if hooks are firing)')
|
|
148
|
+
.action(() => {
|
|
149
|
+
if (!fs.existsSync(HOOKS_LOG_PATH)) {
|
|
150
|
+
console.log('No hooks log found. Hooks have not been called yet.');
|
|
151
|
+
console.log('Make sure Claude Code is running and try using a tool.');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const content = fs.readFileSync(HOOKS_LOG_PATH, 'utf8');
|
|
155
|
+
const lines = content.trim().split('\n');
|
|
156
|
+
const last = lines.slice(-30);
|
|
157
|
+
console.log(`--- Last ${last.length} hook log entries ---\n`);
|
|
158
|
+
console.log(last.join('\n'));
|
|
159
|
+
console.log(`\nFull log: ${HOOKS_LOG_PATH}`);
|
|
160
|
+
});
|
|
161
|
+
|
|
143
162
|
// --- Send commands (stateless, no daemon needed) ---
|
|
144
163
|
|
|
145
164
|
function createTelegramClient() {
|
|
@@ -276,18 +295,27 @@ program
|
|
|
276
295
|
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
277
296
|
const hooks = settings.hooks || {};
|
|
278
297
|
|
|
279
|
-
// Extract script path from hook command
|
|
298
|
+
// Extract script path and node binary from hook command
|
|
280
299
|
function extractScriptPath(cmd) {
|
|
281
300
|
const match = cmd.match(/node\s+(.+)$/);
|
|
282
301
|
return match ? match[1].trim() : null;
|
|
283
302
|
}
|
|
303
|
+
function extractNodeBin(cmd) {
|
|
304
|
+
const match = cmd.match(/^(?:\S+=\S+\s+)?(\S*node)\s/);
|
|
305
|
+
return match ? match[1] : 'node';
|
|
306
|
+
}
|
|
284
307
|
|
|
285
308
|
const permHooks = hooks.PermissionRequest || [];
|
|
286
309
|
const permCmd = permHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
287
310
|
console.log(` PermissionRequest: ${permCmd}`);
|
|
288
311
|
if (permCmd !== 'NOT FOUND') {
|
|
312
|
+
const nodeBin = extractNodeBin(permCmd);
|
|
289
313
|
const scriptPath = extractScriptPath(permCmd);
|
|
290
|
-
if (
|
|
314
|
+
if (nodeBin !== 'node' && !fs.existsSync(nodeBin)) {
|
|
315
|
+
console.log(` Node binary: NO — ${nodeBin}`);
|
|
316
|
+
console.log(' Run: claude-tg setup (to fix hook paths)');
|
|
317
|
+
ok = false;
|
|
318
|
+
} else if (scriptPath && fs.existsSync(scriptPath)) {
|
|
291
319
|
console.log(' File exists: YES');
|
|
292
320
|
} else {
|
|
293
321
|
console.log(` File exists: NO — ${scriptPath || permCmd}`);
|
|
@@ -299,8 +327,13 @@ program
|
|
|
299
327
|
const notifCmd = notifHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
300
328
|
console.log(` Notification: ${notifCmd}`);
|
|
301
329
|
if (notifCmd !== 'NOT FOUND') {
|
|
330
|
+
const nodeBin = extractNodeBin(notifCmd);
|
|
302
331
|
const scriptPath = extractScriptPath(notifCmd);
|
|
303
|
-
if (
|
|
332
|
+
if (nodeBin !== 'node' && !fs.existsSync(nodeBin)) {
|
|
333
|
+
console.log(` Node binary: NO — ${nodeBin}`);
|
|
334
|
+
console.log(' Run: claude-tg setup (to fix hook paths)');
|
|
335
|
+
ok = false;
|
|
336
|
+
} else if (scriptPath && fs.existsSync(scriptPath)) {
|
|
304
337
|
console.log(' File exists: YES');
|
|
305
338
|
} else {
|
|
306
339
|
console.log(` File exists: NO — ${scriptPath || notifCmd}`);
|
package/package.json
CHANGED
|
@@ -4,10 +4,21 @@
|
|
|
4
4
|
// Detects parent TTY, reads hook input from stdin, fires POST to daemon, exits.
|
|
5
5
|
|
|
6
6
|
const http = require('http');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
7
9
|
const { execSync } = require('child_process');
|
|
8
10
|
|
|
9
11
|
const DAEMON_PORT = parseInt(process.env.CLAUDE_TG_PORT || '7483', 10);
|
|
10
12
|
const DAEMON_HOST = '127.0.0.1';
|
|
13
|
+
const HOOK_LOG = path.join(process.env.HOME, '.claude-telegram-bridge', 'hooks.log');
|
|
14
|
+
|
|
15
|
+
function hookLog(msg) {
|
|
16
|
+
try {
|
|
17
|
+
const dir = path.dirname(HOOK_LOG);
|
|
18
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
19
|
+
fs.appendFileSync(HOOK_LOG, `[${new Date().toISOString()}] [notify] ${msg}\n`);
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
11
22
|
|
|
12
23
|
function readStdin() {
|
|
13
24
|
return new Promise((resolve, reject) => {
|
|
@@ -70,21 +81,27 @@ function postToDaemon(body) {
|
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
async function main() {
|
|
84
|
+
hookLog('Hook called');
|
|
73
85
|
try {
|
|
74
86
|
const input = await readStdin();
|
|
75
87
|
const hookInput = input.hookInput || input;
|
|
76
88
|
const ttyPath = findTty();
|
|
89
|
+
const notifType = hookInput.notification_type || hookInput.type || 'unknown';
|
|
90
|
+
|
|
91
|
+
hookLog(`type=${notifType} session=${hookInput.session_id} tty=${ttyPath} port=${DAEMON_PORT}`);
|
|
77
92
|
|
|
78
93
|
await postToDaemon({
|
|
79
94
|
session_id: hookInput.session_id,
|
|
80
95
|
cwd: hookInput.cwd,
|
|
81
|
-
notification_type:
|
|
96
|
+
notification_type: notifType,
|
|
82
97
|
message: hookInput.message,
|
|
83
98
|
transcript_path: hookInput.transcript_path,
|
|
84
99
|
tty_path: ttyPath,
|
|
85
100
|
});
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
|
|
102
|
+
hookLog(`Sent to daemon OK`);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
hookLog(`ERROR: ${err.message || err}`);
|
|
88
105
|
}
|
|
89
106
|
process.exit(0);
|
|
90
107
|
}
|
|
@@ -5,10 +5,21 @@
|
|
|
5
5
|
// On error/timeout: exits 0 with no output → falls back to local dialog.
|
|
6
6
|
|
|
7
7
|
const http = require('http');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
8
10
|
const { execSync } = require('child_process');
|
|
9
11
|
|
|
10
12
|
const DAEMON_PORT = parseInt(process.env.CLAUDE_TG_PORT || '7483', 10);
|
|
11
13
|
const DAEMON_HOST = '127.0.0.1';
|
|
14
|
+
const HOOK_LOG = path.join(process.env.HOME, '.claude-telegram-bridge', 'hooks.log');
|
|
15
|
+
|
|
16
|
+
function hookLog(msg) {
|
|
17
|
+
try {
|
|
18
|
+
const dir = path.dirname(HOOK_LOG);
|
|
19
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
20
|
+
fs.appendFileSync(HOOK_LOG, `[${new Date().toISOString()}] [perm] ${msg}\n`);
|
|
21
|
+
} catch {}
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
function findTty() {
|
|
14
25
|
try {
|
|
@@ -71,11 +82,14 @@ function postToDaemon(path, body) {
|
|
|
71
82
|
}
|
|
72
83
|
|
|
73
84
|
async function main() {
|
|
85
|
+
hookLog('Hook called');
|
|
74
86
|
try {
|
|
75
87
|
const input = await readStdin();
|
|
76
|
-
|
|
77
88
|
const hookInput = input.hookInput || input;
|
|
78
89
|
const ttyPath = findTty();
|
|
90
|
+
|
|
91
|
+
hookLog(`tool=${hookInput.tool_name} session=${hookInput.session_id} tty=${ttyPath} port=${DAEMON_PORT}`);
|
|
92
|
+
|
|
79
93
|
const result = await postToDaemon('/api/permission', {
|
|
80
94
|
session_id: hookInput.session_id,
|
|
81
95
|
cwd: hookInput.cwd,
|
|
@@ -87,10 +101,12 @@ async function main() {
|
|
|
87
101
|
});
|
|
88
102
|
|
|
89
103
|
if (!result || !result.decision) {
|
|
90
|
-
|
|
104
|
+
hookLog('No decision from daemon — falling back to local dialog');
|
|
91
105
|
process.exit(0);
|
|
92
106
|
}
|
|
93
107
|
|
|
108
|
+
hookLog(`Decision: ${JSON.stringify(result.decision)}`);
|
|
109
|
+
|
|
94
110
|
const output = {
|
|
95
111
|
hookSpecificOutput: {
|
|
96
112
|
hookEventName: 'PermissionRequest',
|
|
@@ -100,8 +116,8 @@ async function main() {
|
|
|
100
116
|
};
|
|
101
117
|
|
|
102
118
|
process.stdout.write(JSON.stringify(output));
|
|
103
|
-
} catch {
|
|
104
|
-
|
|
119
|
+
} catch (err) {
|
|
120
|
+
hookLog(`ERROR: ${err.message || err}`);
|
|
105
121
|
process.exit(0);
|
|
106
122
|
}
|
|
107
123
|
}
|
package/src/setup.js
CHANGED
|
@@ -26,13 +26,16 @@ function telegramApiCall(token, method) {
|
|
|
26
26
|
|
|
27
27
|
function getHooksConfig(port) {
|
|
28
28
|
const envPrefix = port !== 7483 ? `CLAUDE_TG_PORT=${port} ` : '';
|
|
29
|
+
// Use absolute path to the node binary that ran setup — ensures hooks work
|
|
30
|
+
// even if node isn't in PATH for non-interactive shells (nvm, fnm, etc.)
|
|
31
|
+
const nodeBin = process.execPath;
|
|
29
32
|
return {
|
|
30
33
|
PermissionRequest: [
|
|
31
34
|
{
|
|
32
35
|
hooks: [
|
|
33
36
|
{
|
|
34
37
|
type: 'command',
|
|
35
|
-
command: `${envPrefix}
|
|
38
|
+
command: `${envPrefix}${nodeBin} ${path.join(HOOKS_DIR, 'permission-request.js')}`,
|
|
36
39
|
timeout: 1800,
|
|
37
40
|
statusMessage: 'Waiting for Telegram approval...',
|
|
38
41
|
},
|
|
@@ -41,11 +44,10 @@ function getHooksConfig(port) {
|
|
|
41
44
|
],
|
|
42
45
|
Notification: [
|
|
43
46
|
{
|
|
44
|
-
matcher: 'idle_prompt|elicitation_dialog',
|
|
45
47
|
hooks: [
|
|
46
48
|
{
|
|
47
49
|
type: 'command',
|
|
48
|
-
command: `${envPrefix}
|
|
50
|
+
command: `${envPrefix}${nodeBin} ${path.join(HOOKS_DIR, 'notification.js')}`,
|
|
49
51
|
async: true,
|
|
50
52
|
},
|
|
51
53
|
],
|