@dmsdc-ai/aigentry-telepty 0.0.17 → 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.
- package/cli.js +45 -26
- package/daemon.js +28 -1
- 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
|
|
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
|
-
|
|
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
|
|
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[
|
|
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[
|
|
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
|
@@ -48,12 +48,25 @@ app.post('/api/sessions/spawn', (req, res) => {
|
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
50
|
console.log(`[SPAWN] Spawning ${shell} with args:`, shellArgs, "in cwd:", cwd);
|
|
51
|
+
|
|
52
|
+
// Create a custom prompt for Linux/Mac so the user sees the session ID.
|
|
53
|
+
// For bash/zsh, we can inject a custom PS1 variable.
|
|
54
|
+
let customEnv = { ...process.env, TERM: isWin ? undefined : 'xterm-256color', TELEPTY_SESSION_ID: session_id };
|
|
55
|
+
|
|
56
|
+
if (!isWin) {
|
|
57
|
+
if (command.includes('bash')) {
|
|
58
|
+
customEnv.PS1 = `\\[\\e[36m\\][telepty: ${session_id}]\\[\\e[0m\\] \\w \\$ `;
|
|
59
|
+
} else if (command.includes('zsh')) {
|
|
60
|
+
customEnv.PROMPT = `%F{cyan}[telepty: ${session_id}]%f %~ %# `;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
const ptyProcess = pty.spawn(shell, shellArgs, {
|
|
52
65
|
name: isWin ? 'Windows Terminal' : 'xterm-256color',
|
|
53
66
|
cols: parseInt(cols),
|
|
54
67
|
rows: parseInt(rows),
|
|
55
68
|
cwd,
|
|
56
|
-
env:
|
|
69
|
+
env: customEnv
|
|
57
70
|
});
|
|
58
71
|
|
|
59
72
|
sessions[session_id] = {
|
|
@@ -151,6 +164,20 @@ app.post('/api/sessions/:id/inject', (req, res) => {
|
|
|
151
164
|
}
|
|
152
165
|
});
|
|
153
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
|
+
|
|
154
181
|
const server = app.listen(PORT, HOST, () => {
|
|
155
182
|
console.log(`🚀 aigentry-telepty daemon listening on http://${HOST}:${PORT}`);
|
|
156
183
|
});
|