git-watchtower 1.14.3 → 1.14.5

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.
@@ -1958,6 +1958,8 @@ async function pollGitChanges() {
1958
1958
  lastPrStatusFetch = Date.now();
1959
1959
  prStatusFetchInFlight = false;
1960
1960
  }).catch(() => {
1961
+ // gh/glab errored (unauthed, rate-limited, network). PR indicators
1962
+ // keep their last-known state; the next poll tick will retry.
1961
1963
  prStatusFetchInFlight = false;
1962
1964
  });
1963
1965
  }
@@ -2207,7 +2209,12 @@ function createStaticServer() {
2207
2209
  let realStaticDir;
2208
2210
  try {
2209
2211
  realStaticDir = fs.realpathSync(resolvedStaticDir);
2210
- } catch {
2212
+ } catch (e) {
2213
+ // STATIC_DIR comes from our own package layout, so a realpath failure
2214
+ // means the install is broken (missing dir, permissions, etc.) — worth
2215
+ // diagnosing. Fall back to the unresolved path so the request still
2216
+ // gets its 403 rather than crashing.
2217
+ telemetry.captureError(e);
2211
2218
  realStaticDir = resolvedStaticDir;
2212
2219
  }
2213
2220
  if (!resolvedPath.startsWith(realStaticDir + path.sep) && resolvedPath !== realStaticDir) {
@@ -2857,6 +2864,8 @@ function setupKeyboardInput() {
2857
2864
  render();
2858
2865
  }
2859
2866
  }).catch(() => {
2867
+ // Async enrichment failed (no remote, gh/glab errored, etc.).
2868
+ // Drop the spinner so the modal shows what we have from phase 1.
2860
2869
  if (store.get('actionMode') && store.get('actionData') && store.get('actionData').branch.name === branch.name) {
2861
2870
  store.setState({ actionLoading: false });
2862
2871
  render();
@@ -3529,6 +3538,23 @@ process.on('uncaughtException', async (err) => {
3529
3538
  process.exit(1);
3530
3539
  });
3531
3540
 
3541
+ // Mirror of uncaughtException for unhandled promise rejections. Without this,
3542
+ // Node 15+ crashes the process on a missed .catch() tail with no telemetry
3543
+ // and no terminal restore — leaving the TUI user in a broken terminal. Also
3544
+ // high-signal: an unhandled rejection reaching here means we missed a .catch()
3545
+ // somewhere and telemetry will tell us where.
3546
+ process.on('unhandledRejection', async (reason) => {
3547
+ isShuttingDown = true;
3548
+
3549
+ cleanupResources();
3550
+
3551
+ const err = reason instanceof Error ? reason : new Error(String(reason));
3552
+ try { telemetry.captureError(err); } catch (_) { /* telemetry must never prevent crash cleanup */ }
3553
+ console.error('Unhandled rejection:', reason);
3554
+ try { await telemetry.shutdown(); } catch (_) { /* telemetry must never prevent crash cleanup */ }
3555
+ process.exit(1);
3556
+ });
3557
+
3532
3558
  // ============================================================================
3533
3559
  // Startup
3534
3560
  // ============================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.14.3",
3
+ "version": "1.14.5",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {
@@ -20,6 +20,7 @@ const fs = require('fs');
20
20
  const path = require('path');
21
21
  const os = require('os');
22
22
  const crypto = require('crypto');
23
+ const telemetry = require('../telemetry');
23
24
 
24
25
  /**
25
26
  * Directory for watchtower runtime files
@@ -359,7 +360,12 @@ class Coordinator {
359
360
  try {
360
361
  const msg = JSON.parse(line);
361
362
  this._handleWorkerMessage(socket, msg, (id) => { workerId = id; }, () => workerId);
362
- } catch (e) { /* malformed IPC frame from worker — skip it and keep reading */ }
363
+ } catch (e) {
364
+ // Both sides of this socket are our own code, so a JSON-parse
365
+ // failure indicates a protocol/version bug worth diagnosing.
366
+ telemetry.captureError(e);
367
+ /* skip malformed frame and keep reading */
368
+ }
363
369
  }
364
370
  }
365
371
  });
@@ -517,7 +523,12 @@ class Worker {
517
523
  try {
518
524
  const msg = JSON.parse(line);
519
525
  this._handleMessage(msg);
520
- } catch (e) { /* malformed IPC frame from coordinator — skip it and keep reading */ }
526
+ } catch (e) {
527
+ // Both sides of this socket are our own code, so a JSON-parse
528
+ // failure indicates a protocol/version bug worth diagnosing.
529
+ telemetry.captureError(e);
530
+ /* skip malformed frame and keep reading */
531
+ }
521
532
  }
522
533
  }
523
534
  });