@rikcodes/teamclaude 1.1.20-rik.6 → 1.1.20-rik.7
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 +1 -1
- package/src/tui.js +22 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rikcodes/teamclaude",
|
|
3
|
-
"version": "1.1.20-rik.
|
|
3
|
+
"version": "1.1.20-rik.7",
|
|
4
4
|
"description": "Multi-account proxy for Claude Code and Codex: pools Claude Max, ChatGPT/Codex, API-key and third-party backend accounts, and rotates on quota",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/tui.js
CHANGED
|
@@ -513,6 +513,15 @@ export class TUI {
|
|
|
513
513
|
// thing blocked. Non-blocking here, and the paint below drops a frame
|
|
514
514
|
// when the terminal is behind instead of waiting for it.
|
|
515
515
|
this._setStdoutBlocking(false);
|
|
516
|
+
// The other half of that bargain: a non-blocking write reports failure
|
|
517
|
+
// asynchronously, as an 'error' event on the stream, and an unhandled one
|
|
518
|
+
// becomes an uncaughtException that ends the process. So a terminal going
|
|
519
|
+
// away — a pane closed, a pty recreated — took the whole proxy with it:
|
|
520
|
+
// two EPIPE crashes, 2026-09-15 and 2026-09-16, both from this path, each
|
|
521
|
+
// leaving the sidecar orphaned on its port. A display may no more kill the
|
|
522
|
+
// proxy than block it. Record that stdout is gone and serve on without it.
|
|
523
|
+
this._stdoutErrorHandler = () => { this._stdoutBroken = true; };
|
|
524
|
+
process.stdout.on('error', this._stdoutErrorHandler);
|
|
516
525
|
process.stdout.write(`${ESC}?1049h${ESC}?25l`);
|
|
517
526
|
process.stdin.setRawMode(true);
|
|
518
527
|
process.stdin.resume();
|
|
@@ -575,7 +584,15 @@ export class TUI {
|
|
|
575
584
|
// screen with no cursor is the one state an operator cannot recover
|
|
576
585
|
// without knowing the escape by heart.
|
|
577
586
|
this._setStdoutBlocking(true);
|
|
578
|
-
|
|
587
|
+
// Blocking again means a failed write throws here instead of arriving as
|
|
588
|
+
// an event, and a terminal that has already gone will fail. Restoring the
|
|
589
|
+
// screen is best-effort: there is nobody left to restore it for. The error
|
|
590
|
+
// listener outlives this write, so a late async failure is absorbed too.
|
|
591
|
+
try { process.stdout.write(`${ESC}?25h${ESC}?1049l`); } catch { /* terminal already gone */ }
|
|
592
|
+
if (this._stdoutErrorHandler) {
|
|
593
|
+
process.stdout.removeListener('error', this._stdoutErrorHandler);
|
|
594
|
+
this._stdoutErrorHandler = null;
|
|
595
|
+
}
|
|
579
596
|
try { process.stdin.setRawMode(false); } catch {}
|
|
580
597
|
process.stdin.pause();
|
|
581
598
|
}
|
|
@@ -1384,6 +1401,10 @@ export class TUI {
|
|
|
1384
1401
|
* again costs a wake-up and a terminal round trip to change nothing.
|
|
1385
1402
|
*/
|
|
1386
1403
|
_paint(buf, force) {
|
|
1404
|
+
// stdout has already failed once: the terminal is gone, every further
|
|
1405
|
+
// write would fail the same way, and a stream that never drains would
|
|
1406
|
+
// strand the pending-paint handshake below. Serving continues blind.
|
|
1407
|
+
if (this._stdoutBroken) return;
|
|
1387
1408
|
const stale = Date.now() - (this._lastPaintAt || 0) >= FORCE_REPAINT_MS;
|
|
1388
1409
|
if (!force && !stale && buf === this._lastFrame) return;
|
|
1389
1410
|
// The terminal has not taken the previous frame yet. Painting anyway would
|