@dmsdc-ai/aigentry-telepty 0.1.7 → 0.1.8

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/cli.js CHANGED
@@ -9,6 +9,7 @@ const prompts = require('prompts');
9
9
  const updateNotifier = require('update-notifier');
10
10
  const pkg = require('./package.json');
11
11
  const { getConfig } = require('./auth');
12
+ const { attachInteractiveTerminal } = require('./interactive-terminal');
12
13
  const { runInteractiveSkillInstaller } = require('./skill-installer');
13
14
  const args = process.argv.slice(2);
14
15
 
@@ -92,29 +93,24 @@ async function ensureDaemonRunning() {
92
93
  async function manageInteractiveAttach(sessionId, targetHost) {
93
94
  const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
94
95
  const ws = new WebSocket(wsUrl);
95
- let inputHandler = null;
96
- let resizeHandler = null;
96
+ let cleanupTerminal = null;
97
97
  return new Promise((resolve) => {
98
98
  ws.on('open', () => {
99
99
  // Set Ghostty tab title to show session ID
100
100
  process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
101
101
  console.log(`\n\x1b[32mEntered room '${sessionId}'.\x1b[0m\n`);
102
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
103
- inputHandler = (d) => ws.send(JSON.stringify({ type: 'input', data: d.toString() }));
104
- resizeHandler = () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }));
105
- process.stdin.on('data', inputHandler);
106
- process.stdout.on('resize', resizeHandler);
107
- resizeHandler();
102
+ cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
103
+ onData: (d) => ws.send(JSON.stringify({ type: 'input', data: d.toString() })),
104
+ onResize: () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }))
105
+ });
108
106
  });
109
107
  ws.on('message', m => {
110
108
  const msg = JSON.parse(m);
111
109
  if (msg.type === 'output') process.stdout.write(msg.data);
112
110
  });
113
111
  ws.on('close', async () => {
114
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
115
112
  process.stdout.write(`\x1b]0;\x07`); // Restore default terminal title
116
- if (inputHandler) process.stdin.off('data', inputHandler);
117
- if (resizeHandler) process.stdout.off('resize', resizeHandler);
113
+ if (cleanupTerminal) cleanupTerminal();
118
114
 
119
115
  // Check if other clients are still attached before destroying
120
116
  try {
@@ -480,11 +476,13 @@ async function main() {
480
476
  process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
481
477
  console.log(`\x1b[32m⚡ '${command}' is now session '\x1b[36m${sessionId}\x1b[32m'. Inject allowed.\x1b[0m\n`);
482
478
 
483
- // Enter raw mode and relay stdin to local PTY
484
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
485
-
486
- process.stdin.on('data', (data) => {
487
- child.write(data.toString());
479
+ const cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
480
+ onData: (data) => {
481
+ child.write(data.toString());
482
+ },
483
+ onResize: () => {
484
+ child.resize(process.stdout.columns, process.stdout.rows);
485
+ }
488
486
  });
489
487
 
490
488
  // Relay PTY output to current terminal + send to daemon for attach clients
@@ -495,14 +493,9 @@ async function main() {
495
493
  }
496
494
  });
497
495
 
498
- // Handle terminal resize
499
- process.stdout.on('resize', () => {
500
- child.resize(process.stdout.columns, process.stdout.rows);
501
- });
502
-
503
496
  // Handle child exit
504
497
  child.onExit(({ exitCode }) => {
505
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
498
+ cleanupTerminal();
506
499
  process.stdout.write(`\x1b]0;\x07`);
507
500
  console.log(`\n\x1b[33mSession '${sessionId}' exited (code ${exitCode}).\x1b[0m`);
508
501
 
@@ -551,8 +544,7 @@ async function main() {
551
544
 
552
545
  const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
553
546
  const ws = new WebSocket(wsUrl);
554
- let inputHandler = null;
555
- let resizeHandler = null;
547
+ let cleanupTerminal = null;
556
548
 
557
549
  ws.on('open', () => {
558
550
  // Set Ghostty tab title to show session ID
@@ -560,25 +552,18 @@ async function main() {
560
552
  process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}${hostSuffix}\x07`);
561
553
  console.log(`\x1b[32mEntered room '${sessionId}'${hostSuffix ? ` (${targetHost})` : ''}.\x1b[0m\n`);
562
554
 
563
- if (process.stdin.isTTY) {
564
- process.stdin.setRawMode(true);
565
- }
566
-
567
- inputHandler = (data) => {
568
- ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
569
- };
570
- process.stdin.on('data', inputHandler);
571
-
572
- resizeHandler = () => {
573
- ws.send(JSON.stringify({
574
- type: 'resize',
575
- cols: process.stdout.columns,
576
- rows: process.stdout.rows
577
- }));
578
- };
579
-
580
- process.stdout.on('resize', resizeHandler);
581
- resizeHandler(); // Initial resize
555
+ cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
556
+ onData: (data) => {
557
+ ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
558
+ },
559
+ onResize: () => {
560
+ ws.send(JSON.stringify({
561
+ type: 'resize',
562
+ cols: process.stdout.columns,
563
+ rows: process.stdout.rows
564
+ }));
565
+ }
566
+ });
582
567
  });
583
568
 
584
569
  ws.on('message', (message) => {
@@ -589,12 +574,8 @@ async function main() {
589
574
  });
590
575
 
591
576
  ws.on('close', async (code, reason) => {
592
- if (process.stdin.isTTY) {
593
- process.stdin.setRawMode(false);
594
- }
595
577
  process.stdout.write(`\x1b]0;\x07`); // Restore default terminal title
596
- if (inputHandler) process.stdin.off('data', inputHandler);
597
- if (resizeHandler) process.stdout.off('resize', resizeHandler);
578
+ if (cleanupTerminal) cleanupTerminal();
598
579
 
599
580
  // Check if other clients are still attached before destroying
600
581
  try {
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ function removeListener(stream, eventName, handler) {
4
+ if (!handler || !stream) {
5
+ return;
6
+ }
7
+
8
+ if (typeof stream.off === 'function') {
9
+ stream.off(eventName, handler);
10
+ return;
11
+ }
12
+
13
+ if (typeof stream.removeListener === 'function') {
14
+ stream.removeListener(eventName, handler);
15
+ }
16
+ }
17
+
18
+ function attachInteractiveTerminal(input, output, handlers = {}) {
19
+ const { onData = null, onResize = null } = handlers;
20
+
21
+ if (input && input.isTTY && typeof input.setRawMode === 'function') {
22
+ input.setRawMode(true);
23
+ }
24
+
25
+ if (input && typeof input.resume === 'function') {
26
+ input.resume();
27
+ }
28
+
29
+ if (input && onData) {
30
+ input.on('data', onData);
31
+ }
32
+
33
+ if (output && onResize) {
34
+ output.on('resize', onResize);
35
+ onResize();
36
+ }
37
+
38
+ return () => {
39
+ removeListener(input, 'data', onData);
40
+ removeListener(output, 'resize', onResize);
41
+
42
+ if (input && input.isTTY && typeof input.setRawMode === 'function') {
43
+ input.setRawMode(false);
44
+ }
45
+
46
+ if (input && typeof input.pause === 'function') {
47
+ input.pause();
48
+ }
49
+ };
50
+ }
51
+
52
+ module.exports = {
53
+ attachInteractiveTerminal
54
+ };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "telepty": "cli.js",
7
7
  "telepty-install": "install.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node --test test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js",
11
- "test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js",
12
- "test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js"
10
+ "test": "node --test test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js",
11
+ "test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js",
12
+ "test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js"
13
13
  },
14
14
  "keywords": [],
15
15
  "author": "",