tmex-cli 0.4.3 → 0.4.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.
@@ -52814,6 +52814,8 @@ class LocalExternalTmuxConnection {
52814
52814
  hookReadAbort = null;
52815
52815
  hookBuffer = "";
52816
52816
  bellDedup = new Map;
52817
+ closeNotified = false;
52818
+ cleanupPromise = null;
52817
52819
  fsPaths = createRuntimeFsPaths({
52818
52820
  deviceId: "pending",
52819
52821
  sessionName: "pending",
@@ -52830,6 +52832,7 @@ class LocalExternalTmuxConnection {
52830
52832
  }
52831
52833
  async connect() {
52832
52834
  this.manualDisconnect = false;
52835
+ this.closeNotified = false;
52833
52836
  this.device = this.deps.getDevice(this.deviceId);
52834
52837
  if (!this.device) {
52835
52838
  throw new Error(`Device not found: ${this.deviceId}`);
@@ -52853,7 +52856,8 @@ class LocalExternalTmuxConnection {
52853
52856
  updateDeviceRuntimeStatus(this.deviceId, {
52854
52857
  lastSeenAt: new Date().toISOString(),
52855
52858
  tmuxAvailable: true,
52856
- lastError: null
52859
+ lastError: null,
52860
+ lastErrorType: null
52857
52861
  });
52858
52862
  await this.requestSnapshotInternal();
52859
52863
  }
@@ -53137,6 +53141,20 @@ class LocalExternalTmuxConnection {
53137
53141
  ])
53138
53142
  ]);
53139
53143
  if (sessionRes.exitCode !== 0 || windowsRes.exitCode !== 0 || panesRes.exitCode !== 0) {
53144
+ const stderrBlob = `${sessionRes.stderr}
53145
+ ${windowsRes.stderr}
53146
+ ${panesRes.stderr}`;
53147
+ if (this.connected && !this.manualDisconnect && this.isTmuxServerGoneMessage(stderrBlob)) {
53148
+ const message = stderrBlob.trim().split(/\r?\n/).find((line) => line.trim())?.trim() ?? "tmux server gone";
53149
+ console.warn(`[local] tmux server gone during snapshot on ${this.deviceId}: ${message}`);
53150
+ updateDeviceRuntimeStatus(this.deviceId, {
53151
+ lastSeenAt: new Date().toISOString(),
53152
+ tmuxAvailable: false,
53153
+ lastError: message
53154
+ });
53155
+ this.shutdownInternal(true);
53156
+ return;
53157
+ }
53140
53158
  this.callbacks.onSnapshot({ deviceId: this.deviceId, session: null });
53141
53159
  return;
53142
53160
  }
@@ -53382,6 +53400,10 @@ class LocalExternalTmuxConnection {
53382
53400
  return result;
53383
53401
  }
53384
53402
  this.notifyRuntimeError(message);
53403
+ if (this.connected && !this.manualDisconnect && this.isTmuxServerGoneMessage(message)) {
53404
+ console.warn(`[local] tmux server gone on ${this.deviceId}: ${message}`);
53405
+ this.shutdownInternal(true);
53406
+ }
53385
53407
  throw new Error(message);
53386
53408
  }
53387
53409
  async notifyRuntimeError(message) {
@@ -53408,6 +53430,40 @@ class LocalExternalTmuxConnection {
53408
53430
  const normalized = message.toLowerCase();
53409
53431
  return normalized.includes("can't find window") || normalized.includes("can't find pane") || normalized.includes("no such window") || normalized.includes("no such pane");
53410
53432
  }
53433
+ isTmuxServerGoneMessage(message) {
53434
+ const normalized = message.toLowerCase();
53435
+ return normalized.includes("no server running on") || normalized.includes("no sessions") || normalized.includes("lost server") || normalized.includes("can't find session") || normalized.includes("session not found") || normalized.includes("no such session");
53436
+ }
53437
+ async shutdownInternal(notifyClose) {
53438
+ if (this.cleanupPromise) {
53439
+ await this.cleanupPromise;
53440
+ if (notifyClose && !this.closeNotified && !this.manualDisconnect) {
53441
+ this.closeNotified = true;
53442
+ this.callbacks.onClose();
53443
+ }
53444
+ return;
53445
+ }
53446
+ this.connected = false;
53447
+ this.cleanupPromise = (async () => {
53448
+ await this.stopAllPipeReaders().catch(() => {
53449
+ return;
53450
+ });
53451
+ if (this.deps.enableHooks) {
53452
+ await this.stopHooks().catch(() => {
53453
+ return;
53454
+ });
53455
+ }
53456
+ try {
53457
+ rmSync(this.fsPaths.rootDir, { recursive: true, force: true });
53458
+ } catch {}
53459
+ })();
53460
+ await this.cleanupPromise;
53461
+ this.cleanupPromise = null;
53462
+ if (notifyClose && !this.closeNotified && !this.manualDisconnect) {
53463
+ this.closeNotified = true;
53464
+ this.callbacks.onClose();
53465
+ }
53466
+ }
53411
53467
  recoverFromTargetMissingError(message) {
53412
53468
  const normalized = message.toLowerCase();
53413
53469
  if (normalized.includes("window")) {
@@ -53810,7 +53866,8 @@ class SshExternalTmuxConnection {
53810
53866
  updateDeviceRuntimeStatus(this.deviceId, {
53811
53867
  lastSeenAt: new Date().toISOString(),
53812
53868
  tmuxAvailable: true,
53813
- lastError: null
53869
+ lastError: null,
53870
+ lastErrorType: null
53814
53871
  });
53815
53872
  await this.requestSnapshotInternal();
53816
53873
  }
@@ -54042,9 +54099,11 @@ class SshExternalTmuxConnection {
54042
54099
  this.handleHookChunk(data.toString());
54043
54100
  },
54044
54101
  onClose: () => {
54045
- if (!this.manualDisconnect) {
54046
- this.callbacks.onError(new Error("SSH hook reader closed unexpectedly"));
54102
+ if (this.manualDisconnect) {
54103
+ return;
54047
54104
  }
54105
+ console.error("[ssh] hook reader channel closed unexpectedly, tearing down");
54106
+ this.shutdownInternal(true);
54048
54107
  }
54049
54108
  });
54050
54109
  this.hookReadAbort = () => {
@@ -54172,6 +54231,20 @@ class SshExternalTmuxConnection {
54172
54231
  ])
54173
54232
  ]);
54174
54233
  if (sessionRes.exitCode !== 0 || windowsRes.exitCode !== 0 || panesRes.exitCode !== 0) {
54234
+ const stderrBlob = `${sessionRes.stderr}
54235
+ ${windowsRes.stderr}
54236
+ ${panesRes.stderr}`;
54237
+ if (this.connected && !this.manualDisconnect && this.isTmuxServerGoneMessage(stderrBlob)) {
54238
+ const message = stderrBlob.trim().split(/\r?\n/).find((line) => line.trim())?.trim() ?? "tmux server gone";
54239
+ console.warn(`[ssh] tmux server gone during snapshot on ${this.deviceId}: ${message}`);
54240
+ updateDeviceRuntimeStatus(this.deviceId, {
54241
+ lastSeenAt: new Date().toISOString(),
54242
+ tmuxAvailable: false,
54243
+ lastError: message
54244
+ });
54245
+ this.shutdownInternal(true);
54246
+ return;
54247
+ }
54175
54248
  this.callbacks.onSnapshot({ deviceId: this.deviceId, session: null });
54176
54249
  return;
54177
54250
  }
@@ -54331,9 +54404,17 @@ class SshExternalTmuxConnection {
54331
54404
  }
54332
54405
  },
54333
54406
  onClose: () => {
54334
- if (!this.manualDisconnect && this.paneReaders.has(paneId)) {
54335
- this.callbacks.onError(new Error(`SSH pane reader closed unexpectedly: ${paneId}`));
54407
+ if (this.manualDisconnect) {
54408
+ return;
54409
+ }
54410
+ const existing = this.paneReaders.get(paneId);
54411
+ if (!existing) {
54412
+ return;
54336
54413
  }
54414
+ console.warn(`[ssh] pane reader channel closed for ${paneId}, resync on next snapshot`);
54415
+ this.paneReaders.delete(paneId);
54416
+ this.runShellAllowFailure(`rm -f ${quoteShellArg(existing.fifoPath)}`);
54417
+ this.requestSnapshot();
54337
54418
  }
54338
54419
  });
54339
54420
  const handle = {
@@ -54409,6 +54490,10 @@ class SshExternalTmuxConnection {
54409
54490
  tmuxAvailable: false,
54410
54491
  lastError: message
54411
54492
  });
54493
+ if (this.connected && !this.manualDisconnect && this.isTmuxServerGoneMessage(message)) {
54494
+ console.warn(`[ssh] tmux server gone on ${this.deviceId}: ${message}`);
54495
+ this.shutdownInternal(true);
54496
+ }
54412
54497
  throw new Error(message);
54413
54498
  }
54414
54499
  async runTmuxAllowFailure(argv, timeoutMs = 1e4) {
@@ -54536,6 +54621,10 @@ printf '\\036TMEX_END %s %d\\036\\n' ${quoteShellArg(commandId)} $?
54536
54621
  const normalized = message.toLowerCase();
54537
54622
  return normalized.includes("can't find window") || normalized.includes("can't find pane") || normalized.includes("no such window") || normalized.includes("no such pane");
54538
54623
  }
54624
+ isTmuxServerGoneMessage(message) {
54625
+ const normalized = message.toLowerCase();
54626
+ return normalized.includes("no server running on") || normalized.includes("no sessions") || normalized.includes("lost server") || normalized.includes("can't find session") || normalized.includes("session not found") || normalized.includes("no such session");
54627
+ }
54539
54628
  recoverFromTargetMissingError(message) {
54540
54629
  const normalized = message.toLowerCase();
54541
54630
  if (normalized.includes("window")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmex-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tmex": "./bin/tmex.js",