@dmsdc-ai/aigentry-telepty 0.0.18 → 0.0.19

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.
Files changed (3) hide show
  1. package/cli.js +45 -26
  2. package/daemon.js +14 -0
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -104,6 +104,36 @@ async function ensureDaemonRunning() {
104
104
  }
105
105
  }
106
106
 
107
+ async function manageInteractiveAttach(sessionId, targetHost) {
108
+ const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
109
+ const ws = new WebSocket(wsUrl);
110
+ return new Promise((resolve) => {
111
+ ws.on('open', () => {
112
+ console.log(`\n\x1b[32mEntered room '${sessionId}'. The room will be destroyed if you exit (Ctrl+C or exit command).\x1b[0m\n`);
113
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
114
+ process.stdin.on('data', d => ws.send(JSON.stringify({ type: 'input', data: d.toString() })));
115
+ const resizer = () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }));
116
+ process.stdout.on('resize', resizer); resizer();
117
+ });
118
+ ws.on('message', m => {
119
+ const msg = JSON.parse(m);
120
+ if (msg.type === 'output') process.stdout.write(msg.data);
121
+ });
122
+ ws.on('close', async () => {
123
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
124
+ console.log(`\n\x1b[33mLeft room '${sessionId}'. Destroying session to prevent zombies...\x1b[0m\n`);
125
+ process.stdin.removeAllListeners('data');
126
+
127
+ // Auto-kill session when the primary creator leaves
128
+ try {
129
+ await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' });
130
+ } catch(e) {}
131
+
132
+ resolve();
133
+ });
134
+ });
135
+ }
136
+
107
137
  async function manageInteractive() {
108
138
  console.clear();
109
139
  console.log('\x1b[36m\x1b[1m⚡ Telepty Agent Manager\x1b[0m\n');
@@ -189,7 +219,12 @@ async function manageInteractive() {
189
219
  });
190
220
  const data = await res.json();
191
221
  if (!res.ok) console.error(`\n❌ Error: ${data.error}\n`);
192
- else console.log(`\n✅ Session '\x1b[36m${data.session_id}\x1b[0m' spawned successfully.\n`);
222
+ else {
223
+ // Immediately attach to the spawned session automatically
224
+ console.log(`\n✅ Session '\x1b[36m${data.session_id}\x1b[0m' spawned. Entering room automatically...\n`);
225
+ args[1] = data.session_id; // Spoof args for attach
226
+ return manageInteractiveAttach(data.session_id, '127.0.0.1');
227
+ }
193
228
  } catch (e) {
194
229
  console.error('\n❌ Failed to connect to local daemon. Is it running?\n');
195
230
  }
@@ -215,27 +250,7 @@ async function manageInteractive() {
215
250
  if (!target) continue;
216
251
 
217
252
  if (response.action === 'attach') {
218
- const wsUrl = `ws://${target.host}:${PORT}/api/sessions/${encodeURIComponent(target.id)}?token=${encodeURIComponent(TOKEN)}`;
219
- const ws = new WebSocket(wsUrl);
220
- await new Promise((resolve) => {
221
- ws.on('open', () => {
222
- console.log(`\n\x1b[32mConnected to '${target.id}'. Press Ctrl+C to detach.\x1b[0m\n`);
223
- if (process.stdin.isTTY) process.stdin.setRawMode(true);
224
- process.stdin.on('data', d => ws.send(JSON.stringify({ type: 'input', data: d.toString() })));
225
- const resizer = () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }));
226
- process.stdout.on('resize', resizer); resizer();
227
- });
228
- ws.on('message', m => {
229
- const msg = JSON.parse(m);
230
- if (msg.type === 'output') process.stdout.write(msg.data);
231
- });
232
- ws.on('close', () => {
233
- if (process.stdin.isTTY) process.stdin.setRawMode(false);
234
- console.log(`\n\x1b[33mDisconnected from session.\x1b[0m\n`);
235
- process.stdin.removeAllListeners('data');
236
- resolve();
237
- });
238
- });
253
+ await manageInteractiveAttach(target.id, target.host);
239
254
  continue;
240
255
  }
241
256
 
@@ -340,7 +355,8 @@ async function main() {
340
355
  });
341
356
  const data = await res.json();
342
357
  if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
343
- console.log(`✅ Session '\x1b[36m${data.session_id}\x1b[0m' spawned successfully.`);
358
+ console.log(`✅ Session '\x1b[36m${data.session_id}\x1b[0m' spawned. Entering room automatically...`);
359
+ return manageInteractiveAttach(data.session_id, '127.0.0.1');
344
360
  } catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
345
361
  return;
346
362
  }
@@ -380,7 +396,7 @@ async function main() {
380
396
  const ws = new WebSocket(wsUrl);
381
397
 
382
398
  ws.on('open', () => {
383
- console.log(`\x1b[32mConnected to session '${sessionId}' at ${targetHost}. Press Ctrl+C to detach.\x1b[0m\n`);
399
+ console.log(`\x1b[32mEntered room '${sessionId}' at ${targetHost}. The room will be destroyed if you exit.\x1b[0m\n`);
384
400
 
385
401
  if (process.stdin.isTTY) {
386
402
  process.stdin.setRawMode(true);
@@ -409,11 +425,14 @@ async function main() {
409
425
  }
410
426
  });
411
427
 
412
- ws.on('close', (code, reason) => {
428
+ ws.on('close', async (code, reason) => {
413
429
  if (process.stdin.isTTY) {
414
430
  process.stdin.setRawMode(false);
415
431
  }
416
- console.log(`\n\x1b[33mDisconnected from session. (Code: ${code}, Reason: ${reason || 'None'})\x1b[0m`);
432
+ console.log(`\n\x1b[33mLeft room. Destroying session '${sessionId}' to prevent zombies... (Code: ${code})\x1b[0m`);
433
+ try {
434
+ await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' });
435
+ } catch(e) {}
417
436
  process.exit(0);
418
437
  });
419
438
 
package/daemon.js CHANGED
@@ -164,6 +164,20 @@ app.post('/api/sessions/:id/inject', (req, res) => {
164
164
  }
165
165
  });
166
166
 
167
+ app.delete('/api/sessions/:id', (req, res) => {
168
+ const { id } = req.params;
169
+ const session = sessions[id];
170
+ if (!session) return res.status(404).json({ error: 'Session not found' });
171
+ try {
172
+ session.ptyProcess.kill();
173
+ delete sessions[id];
174
+ console.log(`[KILL] Session ${id} forcefully closed`);
175
+ res.json({ success: true });
176
+ } catch (err) {
177
+ res.status(500).json({ error: err.message });
178
+ }
179
+ });
180
+
167
181
  const server = app.listen(PORT, HOST, () => {
168
182
  console.log(`🚀 aigentry-telepty daemon listening on http://${HOST}:${PORT}`);
169
183
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "telepty": "cli.js",