claude-notification-plugin 1.1.40 → 1.1.42

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
- "version": "1.1.40",
3
+ "version": "1.1.42",
4
4
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
5
5
  "author": {
6
6
  "name": "Viacheslav Makarov",
package/commit-sha CHANGED
@@ -1 +1 @@
1
- 03d819f371c1cd76acd7127b9c1e1a6a90b4daa9
1
+ 2d4bb2df655de8cdae7f5f4fbf99a0fe0ae1e78f
@@ -13,6 +13,28 @@ import { WorktreeManager } from './worktree-manager.js';
13
13
  import { parseMessage, parseTarget } from './message-parser.js';
14
14
  import { CLAUDE_DIR, CONFIG_PATH, LISTENER_LOG_FILENAME } from '../bin/constants.js';
15
15
 
16
+ // ----------------------
17
+ // CRASH PROTECTION
18
+ // ----------------------
19
+
20
+ process.on('uncaughtException', (err) => {
21
+ const msg = `[UNCAUGHT] ${err.message}`;
22
+ try {
23
+ console.error(msg, err.stack);
24
+ } catch {
25
+ // ignore
26
+ }
27
+ // Don't exit for known node-pty cleanup errors
28
+ if (err.message?.includes('AttachConsole failed')) {
29
+ return;
30
+ }
31
+ process.exit(1);
32
+ });
33
+
34
+ process.on('unhandledRejection', (reason) => {
35
+ console.error('[UNHANDLED REJECTION]', reason);
36
+ });
37
+
16
38
  // ----------------------
17
39
  // CONFIG
18
40
  // ----------------------
@@ -131,9 +153,10 @@ for (const alias of Object.keys(listenerConfig.projects)) {
131
153
  }
132
154
 
133
155
  // ----------------------
134
- // WATCHDOG
156
+ // WATCHDOG + ORPHAN RECOVERY
135
157
  // ----------------------
136
158
 
159
+ // 1. Clean up tasks that exceeded taskTimeout
137
160
  const recovered = queue.watchdog(taskTimeout);
138
161
  for (const { workDir, next } of recovered) {
139
162
  if (next) {
@@ -141,6 +164,14 @@ for (const { workDir, next } of recovered) {
141
164
  }
142
165
  }
143
166
 
167
+ // 2. Re-start orphaned active tasks (PTY sessions lost on restart)
168
+ for (const [workDir, entry] of Object.entries(queue.queues)) {
169
+ if (entry.active && !runner.isRunning(workDir)) {
170
+ logger.info(`Orphan recovery: re-starting task "${entry.active.id}" in ${workDir}`);
171
+ startTask(workDir, entry.active);
172
+ }
173
+ }
174
+
144
175
  // ----------------------
145
176
  // TASK RUNNER EVENTS
146
177
  // ----------------------
@@ -239,8 +239,36 @@ export class PtyRunner extends EventEmitter {
239
239
  session.pty.write(`\x1b[200~${task.text}\x1b[201~\r`);
240
240
  this.logger.info(`PTY task sent to ${workDir}: ${task.text.slice(0, 100)}`);
241
241
 
242
+ // Monitor PTY output for fatal errors that prevent task completion
243
+ const errorPatterns = [
244
+ { pattern: 'auto mode temporarily unavailable', msg: 'Claude auto mode temporarily unavailable' },
245
+ { pattern: 'Session expired', msg: 'Claude session expired' },
246
+ { pattern: 'Authentication required', msg: 'Claude authentication required' },
247
+ ];
248
+ const errorCheckInterval = setInterval(() => {
249
+ const buf = session._buffer || '';
250
+ for (const { pattern, msg } of errorPatterns) {
251
+ if (buf.includes(pattern)) {
252
+ clearInterval(errorCheckInterval);
253
+ this.logger.error(`PTY fatal: ${msg} in ${workDir}`);
254
+ if (session._pendingId && this.pendingMarkers.has(session._pendingId)) {
255
+ this.pendingMarkers.delete(session._pendingId);
256
+ }
257
+ session.state = 'idle';
258
+ session.currentTask = null;
259
+ this._destroyPty(workDir);
260
+ this.emit('error', workDir, task, msg);
261
+ return;
262
+ }
263
+ }
264
+ }, 3000);
265
+
266
+ // Clean up error monitor when task completes normally
267
+ const clearErrorCheck = () => clearInterval(errorCheckInterval);
268
+
242
269
  // Handle completion asynchronously
243
270
  markerPromise.then((marker) => {
271
+ clearErrorCheck();
244
272
  session.state = 'idle';
245
273
  session.currentTask = null;
246
274
  session.sessionId = marker.sessionId;
@@ -262,6 +290,7 @@ export class PtyRunner extends EventEmitter {
262
290
  }
263
291
  this.emit('complete', workDir, task, result);
264
292
  }).catch((err) => {
293
+ clearErrorCheck();
265
294
  session.state = 'idle';
266
295
  session.currentTask = null;
267
296
 
@@ -293,6 +322,11 @@ export class PtyRunner extends EventEmitter {
293
322
  // Filter out pipe-mode-specific args
294
323
  const args = claudeArgs.filter(a => a !== '-p' && a !== '--output-format' && a !== 'json');
295
324
 
325
+ // Ensure --permission-mode is set (default: auto) to prevent interactive permission prompts
326
+ if (!args.includes('--permission-mode')) {
327
+ args.push('--permission-mode', 'auto');
328
+ }
329
+
296
330
  this.logger.info(`Creating PTY session in ${workDir} with args: ${JSON.stringify(args)}`);
297
331
 
298
332
  const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/bash';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
3
  "productName": "claude-notification-plugin",
4
- "version": "1.1.40",
4
+ "version": "1.1.42",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {