claude-code-watch 0.0.2 → 0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-watch",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Web-based real-time monitor for Claude Code.",
5
5
  "main": "./src/server/server.js",
6
6
  "bin": {
@@ -301,7 +301,35 @@ class DashboardServer {
301
301
  return w;
302
302
  }
303
303
 
304
- start(options = {}) {
304
+ async killExistingPort(port) {
305
+ let cmd;
306
+ if (process.platform === 'win32') {
307
+ cmd = `netstat -ano | findstr :${port} | findstr LISTENING`;
308
+ } else {
309
+ cmd = `lsof -ti:${port}`;
310
+ }
311
+ try {
312
+ const result = cp.execSync(cmd, { encoding: 'utf-8' }).trim();
313
+ if (!result) return false;
314
+ const pids = result.split('\n').map(s => s.trim()).filter(Boolean);
315
+ for (const pid of pids) {
316
+ try {
317
+ if (process.platform === 'win32') {
318
+ cp.execSync(`taskkill /PID ${pid} /F`, { encoding: 'utf-8' });
319
+ } else {
320
+ process.kill(parseInt(pid, 10), 'SIGKILL');
321
+ }
322
+ } catch {}
323
+ }
324
+ // Wait briefly for the port to be released
325
+ await new Promise(r => setTimeout(r, 500));
326
+ return true;
327
+ } catch {
328
+ return false;
329
+ }
330
+ }
331
+
332
+ async start(options = {}) {
305
333
  const skipHistory = options.skipHistory || false;
306
334
  const pollMs = options.pollMs || 500;
307
335
  const activeWindow = options.activeWindow || 5 * 60 * 1000;
@@ -314,6 +342,12 @@ class DashboardServer {
314
342
  maxSessions,
315
343
  };
316
344
 
345
+ // Proactively kill any process occupying the port before starting
346
+ const killed = await this.killExistingPort(this.port);
347
+ if (killed) {
348
+ console.log(`Previous instance on port ${this.port} killed, restarting...`);
349
+ }
350
+
317
351
  this.server = http.createServer((req, res) => {
318
352
  this.handleHTTP(req, res).catch(() => {
319
353
  if (!res.headersSent) {
@@ -326,6 +360,17 @@ class DashboardServer {
326
360
  this.wss = new WebSocketServer({ server: this.server });
327
361
  this.wss.on('connection', (ws) => this.onWsConnection(ws));
328
362
 
363
+ // Register error handler once (not inside doListen to avoid accumulation)
364
+ this.server.on('error', (err) => {
365
+ if (err.code === 'EADDRINUSE') {
366
+ console.error(`Port ${this.port} is still in use after attempting to free it. Exiting.`);
367
+ process.exit(1);
368
+ } else {
369
+ console.error(`Server error: ${err.message}`);
370
+ process.exit(1);
371
+ }
372
+ });
373
+
329
374
  const w = this.setupWatcher(watcherOpts);
330
375
 
331
376
  w.init().then(() => {
@@ -367,7 +412,7 @@ class DashboardServer {
367
412
  }
368
413
  }
369
414
 
370
- function startServer(options = {}) {
415
+ async function startServer(options = {}) {
371
416
  const ds = new DashboardServer(options);
372
417
  return ds.start(options);
373
418
  }