claude-tg 1.0.2 → 1.0.4
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 +134 -0
- package/package.json +1 -1
- package/src/daemon.js +14 -0
package/bin/claude-tg.js
CHANGED
|
@@ -249,6 +249,140 @@ program
|
|
|
249
249
|
}
|
|
250
250
|
});
|
|
251
251
|
|
|
252
|
+
program
|
|
253
|
+
.command('test')
|
|
254
|
+
.description('Test the full chain: config, daemon, hooks, Telegram')
|
|
255
|
+
.action(async () => {
|
|
256
|
+
const http = require('http');
|
|
257
|
+
let ok = true;
|
|
258
|
+
|
|
259
|
+
// 1. Config
|
|
260
|
+
console.log('\n--- Config ---');
|
|
261
|
+
const config = loadConfig();
|
|
262
|
+
if (config.botToken && config.chatId) {
|
|
263
|
+
console.log(` Bot token: ${config.botToken.slice(0, 8)}...`);
|
|
264
|
+
console.log(` Chat ID: ${config.chatId}`);
|
|
265
|
+
console.log(` Port: ${config.port}`);
|
|
266
|
+
} else {
|
|
267
|
+
console.log(' NOT CONFIGURED. Run: claude-tg setup');
|
|
268
|
+
ok = false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 2. Hooks
|
|
272
|
+
console.log('\n--- Hooks ---');
|
|
273
|
+
const settingsPath = path.join(process.env.HOME, '.claude', 'settings.json');
|
|
274
|
+
if (fs.existsSync(settingsPath)) {
|
|
275
|
+
try {
|
|
276
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
277
|
+
const hooks = settings.hooks || {};
|
|
278
|
+
|
|
279
|
+
const permHooks = hooks.PermissionRequest || [];
|
|
280
|
+
const permCmd = permHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
281
|
+
console.log(` PermissionRequest: ${permCmd}`);
|
|
282
|
+
if (permCmd !== 'NOT FOUND') {
|
|
283
|
+
const scriptPath = permCmd.replace(/^node\s+/, '');
|
|
284
|
+
if (fs.existsSync(scriptPath)) {
|
|
285
|
+
console.log(' File exists: YES');
|
|
286
|
+
} else {
|
|
287
|
+
console.log(` File exists: NO — ${scriptPath}`);
|
|
288
|
+
ok = false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const notifHooks = hooks.Notification || [];
|
|
293
|
+
const notifCmd = notifHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
|
|
294
|
+
console.log(` Notification: ${notifCmd}`);
|
|
295
|
+
if (notifCmd !== 'NOT FOUND') {
|
|
296
|
+
const scriptPath = notifCmd.replace(/^node\s+/, '');
|
|
297
|
+
if (fs.existsSync(scriptPath)) {
|
|
298
|
+
console.log(' File exists: YES');
|
|
299
|
+
} else {
|
|
300
|
+
console.log(` File exists: NO — ${scriptPath}`);
|
|
301
|
+
ok = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {
|
|
305
|
+
console.log(` Error reading settings: ${e.message}`);
|
|
306
|
+
ok = false;
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
console.log(' ~/.claude/settings.json not found. Run: claude-tg setup');
|
|
310
|
+
ok = false;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 3. Daemon
|
|
314
|
+
console.log('\n--- Daemon ---');
|
|
315
|
+
try {
|
|
316
|
+
const health = await new Promise((resolve, reject) => {
|
|
317
|
+
const req = http.get(`http://127.0.0.1:${config.port}/api/health`, (res) => {
|
|
318
|
+
let data = '';
|
|
319
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
320
|
+
res.on('end', () => {
|
|
321
|
+
try { resolve(JSON.parse(data)); } catch { reject(new Error('bad response')); }
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
req.on('error', reject);
|
|
325
|
+
req.setTimeout(3000, () => { req.destroy(); reject(new Error('timeout')); });
|
|
326
|
+
});
|
|
327
|
+
console.log(` Running: YES (${health.sessions} sessions, ${health.pending} pending)`);
|
|
328
|
+
} catch {
|
|
329
|
+
console.log(' Running: NO — start with: claude-tg daemon start');
|
|
330
|
+
ok = false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 4. Telegram
|
|
334
|
+
console.log('\n--- Telegram ---');
|
|
335
|
+
if (config.botToken && config.chatId) {
|
|
336
|
+
try {
|
|
337
|
+
const { Telegram } = require('telegraf');
|
|
338
|
+
const tg = new Telegram(config.botToken);
|
|
339
|
+
await tg.sendMessage(config.chatId, '🧪 claude-tg test — everything is working!');
|
|
340
|
+
console.log(' Test message sent: YES');
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.log(` Test message FAILED: ${err.message}`);
|
|
343
|
+
ok = false;
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
console.log(' Skipped (no config)');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 5. Send test notification to daemon
|
|
350
|
+
if (ok) {
|
|
351
|
+
console.log('\n--- Hook simulation ---');
|
|
352
|
+
try {
|
|
353
|
+
const result = await new Promise((resolve, reject) => {
|
|
354
|
+
const payload = JSON.stringify({
|
|
355
|
+
session_id: 'test-' + Date.now(),
|
|
356
|
+
cwd: process.cwd(),
|
|
357
|
+
notification_type: 'idle_prompt',
|
|
358
|
+
message: 'Test notification from claude-tg test',
|
|
359
|
+
transcript_path: null,
|
|
360
|
+
tty_path: null,
|
|
361
|
+
});
|
|
362
|
+
const req = http.request({
|
|
363
|
+
hostname: '127.0.0.1', port: config.port,
|
|
364
|
+
path: '/api/notify', method: 'POST',
|
|
365
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
366
|
+
timeout: 5000,
|
|
367
|
+
}, (res) => {
|
|
368
|
+
let data = '';
|
|
369
|
+
res.on('data', (c) => { data += c; });
|
|
370
|
+
res.on('end', () => resolve(data));
|
|
371
|
+
});
|
|
372
|
+
req.on('error', reject);
|
|
373
|
+
req.write(payload);
|
|
374
|
+
req.end();
|
|
375
|
+
});
|
|
376
|
+
console.log(' Notification sent to daemon: YES');
|
|
377
|
+
console.log(' Check Telegram — you should see a test idle notification');
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.log(` Notification FAILED: ${err.message}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
console.log(`\n${ok ? 'All checks passed.' : 'Some checks FAILED — fix the issues above.'}\n`);
|
|
384
|
+
});
|
|
385
|
+
|
|
252
386
|
program
|
|
253
387
|
.command('uninstall')
|
|
254
388
|
.description('Remove hooks from ~/.claude/settings.json')
|
package/package.json
CHANGED
package/src/daemon.js
CHANGED
|
@@ -938,6 +938,20 @@ function startBot() {
|
|
|
938
938
|
}
|
|
939
939
|
targetSessionId = mapping.sessionId;
|
|
940
940
|
}
|
|
941
|
+
|
|
942
|
+
// Fallback: parse session number from the replied-to message text (survives daemon restarts)
|
|
943
|
+
if (!targetSessionId && ctx.message.reply_to_message?.text) {
|
|
944
|
+
const match = ctx.message.reply_to_message.text.match(/#(\d+)\s/);
|
|
945
|
+
if (match) {
|
|
946
|
+
const num = parseInt(match[1]);
|
|
947
|
+
for (const [sid] of sessions) {
|
|
948
|
+
if (getSessionLabel(sid) === num) {
|
|
949
|
+
targetSessionId = sid;
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
941
955
|
}
|
|
942
956
|
|
|
943
957
|
// If no reply-to, try auto-routing
|