mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Suppress socket disconnect error on normal Claude Code exit
- Add isExitingNormally flag set during onExit callback - Check for common normal disconnect errors (EPIPE, ECONNRESET, Unknown error) - Log as debug instead of error for normal disconnects - Prevents misleading error message when exiting Claude Code normally
This commit is contained in:
parent
7a69ccb711
commit
5d49693573
3 changed files with 40 additions and 17 deletions
|
|
@ -1,8 +1,8 @@
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { DEFAULT_NOTIFICATION_PREFERENCES } from '../../types/config.js';
|
||||
import { notificationEventService } from './notification-event-service.js';
|
||||
import type { NotificationPreferences } from './push-notification-service.js';
|
||||
import { pushNotificationService } from './push-notification-service.js';
|
||||
import { notificationEventService } from './notification-event-service.js';
|
||||
import { serverConfigService } from './server-config-service.js';
|
||||
|
||||
// Mock notification event service
|
||||
|
|
@ -514,7 +514,9 @@ describe('PushNotificationService', () => {
|
|||
vibrationEnabled: true,
|
||||
};
|
||||
|
||||
(serverConfigService.updateNotificationPreferences as vi.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(serverConfigService.updateNotificationPreferences as vi.Mock).mockRejectedValue(
|
||||
new Error('Network error')
|
||||
);
|
||||
|
||||
await expect(pushNotificationService.savePreferences(preferences)).rejects.toThrow(
|
||||
'Network error'
|
||||
|
|
@ -543,8 +545,8 @@ describe('PushNotificationService', () => {
|
|||
const testPromise = pushNotificationService.testNotification();
|
||||
|
||||
// Simulate receiving the event
|
||||
await new Promise(resolve => setTimeout(resolve, 100)); // allow time for listener to be registered
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100)); // allow time for listener to be registered
|
||||
|
||||
expect(testNotificationHandler!).toBeDefined();
|
||||
testNotificationHandler!({
|
||||
title: 'VibeTunnel Test',
|
||||
|
|
@ -584,8 +586,8 @@ describe('PushNotificationService', () => {
|
|||
const testPromise = pushNotificationService.testNotification();
|
||||
|
||||
// Simulate receiving the event
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
expect(testNotificationHandler!).toBeDefined();
|
||||
testNotificationHandler!({});
|
||||
|
||||
|
|
|
|||
|
|
@ -394,16 +394,13 @@ export class PushNotificationService {
|
|||
|
||||
// Show notification if we have permission
|
||||
if (this.serviceWorkerRegistration && this.getPermission() === 'granted') {
|
||||
await this.serviceWorkerRegistration.showNotification(
|
||||
data.title || 'VibeTunnel Test',
|
||||
{
|
||||
body: data.body || 'Test notification received via SSE!',
|
||||
icon: '/apple-touch-icon.png',
|
||||
badge: '/favicon-32.png',
|
||||
tag: 'vibetunnel-test-sse',
|
||||
requireInteraction: false,
|
||||
}
|
||||
);
|
||||
await this.serviceWorkerRegistration.showNotification(data.title || 'VibeTunnel Test', {
|
||||
body: data.body || 'Test notification received via SSE!',
|
||||
icon: '/apple-touch-icon.png',
|
||||
badge: '/favicon-32.png',
|
||||
tag: 'vibetunnel-test-sse',
|
||||
requireInteraction: false,
|
||||
});
|
||||
logger.log('✅ Displayed SSE test notification');
|
||||
}
|
||||
resolve();
|
||||
|
|
|
|||
|
|
@ -390,6 +390,7 @@ export async function startVibeTunnelForward(args: string[]) {
|
|||
// Variables that need to be accessible in cleanup
|
||||
let sessionFileWatcher: fs.FSWatcher | undefined;
|
||||
let fileWatchDebounceTimer: NodeJS.Timeout | undefined;
|
||||
let isExitingNormally = false;
|
||||
|
||||
const sessionOptions: Parameters<typeof ptyManager.createSession>[1] = {
|
||||
sessionId: finalSessionId,
|
||||
|
|
@ -405,6 +406,9 @@ export async function startVibeTunnelForward(args: string[]) {
|
|||
gitIsWorktree: gitInfo.gitIsWorktree,
|
||||
gitMainRepoPath: gitInfo.gitMainRepoPath,
|
||||
onExit: async (exitCode: number) => {
|
||||
// Mark that we're exiting normally
|
||||
isExitingNormally = true;
|
||||
|
||||
// Show exit message
|
||||
logger.log(
|
||||
chalk.yellow(`\n✓ VibeTunnel session ended`) + chalk.gray(` (exit code: ${exitCode})`)
|
||||
|
|
@ -737,7 +741,27 @@ export async function startVibeTunnelForward(args: string[]) {
|
|||
|
||||
// Handle socket events
|
||||
socketClient.on('disconnect', (error) => {
|
||||
logger.error('Socket disconnected:', error?.message || 'Unknown error');
|
||||
// Don't log error if we're exiting normally
|
||||
if (isExitingNormally) {
|
||||
logger.debug('Socket disconnected during normal exit');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a common disconnect error during normal operation
|
||||
const errorMessage = error?.message || '';
|
||||
const isNormalDisconnect =
|
||||
errorMessage.includes('EPIPE') ||
|
||||
errorMessage.includes('ECONNRESET') ||
|
||||
errorMessage.includes('socket hang up') ||
|
||||
errorMessage === 'Unknown error' || // Common during clean exits
|
||||
!error; // No error object means clean disconnect
|
||||
|
||||
if (isNormalDisconnect) {
|
||||
logger.debug('Socket disconnected (normal termination)');
|
||||
} else {
|
||||
logger.error('Socket disconnected:', error?.message || 'Unknown error');
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue