@dmsdc-ai/aigentry-telepty 0.1.5 → 0.1.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/.claude/commands/telepty-allow.md +58 -0
- package/.claude/commands/telepty-attach.md +22 -0
- package/.claude/commands/telepty-inject.md +34 -0
- package/.claude/commands/telepty-list.md +22 -0
- package/.claude/commands/telepty-manual-test.md +73 -0
- package/.claude/commands/telepty-start.md +25 -0
- package/.claude/commands/telepty-test.md +25 -0
- package/.claude/commands/telepty.md +82 -0
- package/README.md +27 -0
- package/cli.js +249 -46
- package/daemon.js +261 -27
- package/install.js +96 -64
- package/install.ps1 +1 -0
- package/install.sh +1 -0
- package/package.json +4 -2
- package/skill-installer.js +269 -0
- package/skills/telepty/SKILL.md +86 -0
- package/.cross_session_deliberation.json +0 -7
- package/.deliberation_request.json +0 -1
- package/.deliberation_request2.json +0 -1
- package/.deliberation_request3.json +0 -1
- package/.deliberation_request_bridge.json +0 -1
- package/.deliberation_request_bridge_retry.json +0 -1
- package/.gemini/skills/telepty/SKILL.md +0 -48
- package/.github/workflows/test-install.yml +0 -32
- package/aigentry-telepty-0.0.4.tgz +0 -0
- package/clipboard_image.png +0 -0
- package/test-pty.js +0 -14
package/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
4
5
|
const WebSocket = require('ws');
|
|
5
6
|
const { execSync, spawn } = require('child_process');
|
|
6
7
|
const readline = require('readline');
|
|
@@ -8,14 +9,17 @@ const prompts = require('prompts');
|
|
|
8
9
|
const updateNotifier = require('update-notifier');
|
|
9
10
|
const pkg = require('./package.json');
|
|
10
11
|
const { getConfig } = require('./auth');
|
|
12
|
+
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
11
13
|
const args = process.argv.slice(2);
|
|
12
14
|
|
|
13
|
-
// Check for updates
|
|
14
|
-
|
|
15
|
+
// Check for updates unless explicitly disabled for tests/CI.
|
|
16
|
+
if (!process.env.NO_UPDATE_NOTIFIER && !process.env.TELEPTY_DISABLE_UPDATE_NOTIFIER) {
|
|
17
|
+
updateNotifier({pkg}).notify({ isGlobal: true });
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
// Support remote host via environment variable or default to localhost
|
|
17
21
|
let REMOTE_HOST = process.env.TELEPTY_HOST || '127.0.0.1';
|
|
18
|
-
const PORT = 3848;
|
|
22
|
+
const PORT = Number(process.env.TELEPTY_PORT || 3848);
|
|
19
23
|
let DAEMON_URL = `http://${REMOTE_HOST}:${PORT}`;
|
|
20
24
|
let WS_URL = `ws://${REMOTE_HOST}:${PORT}`;
|
|
21
25
|
|
|
@@ -27,25 +31,6 @@ const fetchWithAuth = (url, options = {}) => {
|
|
|
27
31
|
return fetch(url, { ...options, headers });
|
|
28
32
|
};
|
|
29
33
|
|
|
30
|
-
async function ensureDaemonRunning() {
|
|
31
|
-
if (REMOTE_HOST !== '127.0.0.1') return; // Only auto-start local daemon
|
|
32
|
-
try {
|
|
33
|
-
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions`);
|
|
34
|
-
if (res.ok) return; // Already running
|
|
35
|
-
} catch (e) {
|
|
36
|
-
// Not running, let's start it
|
|
37
|
-
process.stdout.write('\x1b[33m⚙️ Auto-starting local telepty daemon...\x1b[0m\n');
|
|
38
|
-
const cp = spawn(process.argv[0], [process.argv[1], 'daemon'], {
|
|
39
|
-
detached: true,
|
|
40
|
-
stdio: 'ignore'
|
|
41
|
-
});
|
|
42
|
-
cp.unref();
|
|
43
|
-
|
|
44
|
-
// Wait a brief moment for the daemon to boot up
|
|
45
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
34
|
async function discoverSessions() {
|
|
50
35
|
await ensureDaemonRunning();
|
|
51
36
|
const hosts = ['127.0.0.1'];
|
|
@@ -107,13 +92,19 @@ async function ensureDaemonRunning() {
|
|
|
107
92
|
async function manageInteractiveAttach(sessionId, targetHost) {
|
|
108
93
|
const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
|
|
109
94
|
const ws = new WebSocket(wsUrl);
|
|
95
|
+
let inputHandler = null;
|
|
96
|
+
let resizeHandler = null;
|
|
110
97
|
return new Promise((resolve) => {
|
|
111
98
|
ws.on('open', () => {
|
|
112
|
-
|
|
99
|
+
// Set Ghostty tab title to show session ID
|
|
100
|
+
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
|
|
101
|
+
console.log(`\n\x1b[32mEntered room '${sessionId}'.\x1b[0m\n`);
|
|
113
102
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
process.
|
|
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();
|
|
117
108
|
});
|
|
118
109
|
ws.on('message', m => {
|
|
119
110
|
const msg = JSON.parse(m);
|
|
@@ -121,14 +112,27 @@ async function manageInteractiveAttach(sessionId, targetHost) {
|
|
|
121
112
|
});
|
|
122
113
|
ws.on('close', async () => {
|
|
123
114
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
124
|
-
|
|
125
|
-
process.stdin.
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
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);
|
|
118
|
+
|
|
119
|
+
// Check if other clients are still attached before destroying
|
|
128
120
|
try {
|
|
129
|
-
await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions
|
|
130
|
-
|
|
131
|
-
|
|
121
|
+
const res = await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions`);
|
|
122
|
+
if (res.ok) {
|
|
123
|
+
const sessions = await res.json();
|
|
124
|
+
const session = sessions.find(s => s.id === sessionId);
|
|
125
|
+
if (session && session.active_clients > 0) {
|
|
126
|
+
console.log(`\n\x1b[33mLeft room '${sessionId}'. Other clients still attached — session kept alive.\x1b[0m\n`);
|
|
127
|
+
} else {
|
|
128
|
+
console.log(`\n\x1b[33mLeft room '${sessionId}'. No other clients — destroying session.\x1b[0m\n`);
|
|
129
|
+
await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch(e) {
|
|
133
|
+
// Daemon unreachable, nothing to clean up
|
|
134
|
+
}
|
|
135
|
+
|
|
132
136
|
resolve();
|
|
133
137
|
});
|
|
134
138
|
});
|
|
@@ -146,8 +150,10 @@ async function manageInteractive() {
|
|
|
146
150
|
choices: [
|
|
147
151
|
{ title: '🖥️ Enter a room (Attach to session)', value: 'attach' },
|
|
148
152
|
{ title: '➕ Create a new room (Spawn session)', value: 'spawn' },
|
|
153
|
+
{ title: '🔌 Allow inject (Run CLI with inject)', value: 'allow' },
|
|
149
154
|
{ title: '💬 Send message to a room (Inject command)', value: 'inject' },
|
|
150
155
|
{ title: '📋 View all open rooms (List sessions)', value: 'list' },
|
|
156
|
+
{ title: '🧠 Install telepty skills', value: 'install-skills' },
|
|
151
157
|
{ title: '🔄 Update telepty to latest version', value: 'update' },
|
|
152
158
|
{ title: '❌ Exit', value: 'exit' }
|
|
153
159
|
]
|
|
@@ -185,6 +191,15 @@ async function manageInteractive() {
|
|
|
185
191
|
continue;
|
|
186
192
|
}
|
|
187
193
|
|
|
194
|
+
if (response.action === 'install-skills') {
|
|
195
|
+
try {
|
|
196
|
+
await runInteractiveSkillInstaller({ packageRoot: __dirname, cwd: process.cwd() });
|
|
197
|
+
} catch (e) {
|
|
198
|
+
console.error(`\n❌ ${e.message}\n`);
|
|
199
|
+
}
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
|
|
188
203
|
if (response.action === 'list') {
|
|
189
204
|
console.log('\n');
|
|
190
205
|
const sessions = await discoverSessions();
|
|
@@ -215,7 +230,7 @@ async function manageInteractive() {
|
|
|
215
230
|
try {
|
|
216
231
|
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/spawn`, {
|
|
217
232
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
218
|
-
body: JSON.stringify({ session_id: id, command, args: [], cwd: process.cwd(), cols, rows })
|
|
233
|
+
body: JSON.stringify({ session_id: id, command, args: [], cwd: process.cwd(), cols, rows, type: 'USER' })
|
|
219
234
|
});
|
|
220
235
|
const data = await res.json();
|
|
221
236
|
if (!res.ok) console.error(`\n❌ Error: ${data.error}\n`);
|
|
@@ -231,6 +246,20 @@ async function manageInteractive() {
|
|
|
231
246
|
continue;
|
|
232
247
|
}
|
|
233
248
|
|
|
249
|
+
if (response.action === 'allow') {
|
|
250
|
+
const { id, command } = await prompts([
|
|
251
|
+
{ type: 'text', name: 'id', message: 'Enter session ID (e.g. my-claude):', validate: v => v ? true : 'Required' },
|
|
252
|
+
{ type: 'text', name: 'command', message: 'Enter command to run (e.g. claude, codex, gemini, bash):', initial: 'bash' }
|
|
253
|
+
]);
|
|
254
|
+
if (!id || !command) continue;
|
|
255
|
+
|
|
256
|
+
// Delegate to the allow command handler by setting up args and calling main flow
|
|
257
|
+
process.argv.splice(2, process.argv.length - 2, 'allow', '--id', id, command);
|
|
258
|
+
args.length = 0;
|
|
259
|
+
args.push('allow', '--id', id, command);
|
|
260
|
+
return main();
|
|
261
|
+
}
|
|
262
|
+
|
|
234
263
|
if (response.action === 'attach' || response.action === 'inject') {
|
|
235
264
|
const sessions = await discoverSessions();
|
|
236
265
|
if (sessions.length === 0) {
|
|
@@ -346,7 +375,7 @@ async function main() {
|
|
|
346
375
|
try {
|
|
347
376
|
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/spawn`, {
|
|
348
377
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
349
|
-
body: JSON.stringify({ session_id: sessionId, command: command, args: cmdArgs, cwd: process.cwd(), cols, rows })
|
|
378
|
+
body: JSON.stringify({ session_id: sessionId, command: command, args: cmdArgs, cwd: process.cwd(), cols, rows, type: 'USER' })
|
|
350
379
|
});
|
|
351
380
|
const data = await res.json();
|
|
352
381
|
if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
|
|
@@ -356,6 +385,139 @@ async function main() {
|
|
|
356
385
|
return;
|
|
357
386
|
}
|
|
358
387
|
|
|
388
|
+
if (cmd === 'allow' || cmd === 'enable' || cmd === 'wrap') {
|
|
389
|
+
// Parse arguments: telepty allow [--id <session_id>] <command> [args...]
|
|
390
|
+
// Also supports legacy: telepty allow [--id <session_id>] -- <command> [args...]
|
|
391
|
+
const allowArgs = args.slice(1);
|
|
392
|
+
|
|
393
|
+
// Extract --id flag
|
|
394
|
+
let sessionId;
|
|
395
|
+
const idIndex = allowArgs.indexOf('--id');
|
|
396
|
+
if (idIndex !== -1 && allowArgs[idIndex + 1]) {
|
|
397
|
+
sessionId = allowArgs[idIndex + 1];
|
|
398
|
+
allowArgs.splice(idIndex, 2);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Strip optional -- separator for backward compat
|
|
402
|
+
const sepIndex = allowArgs.indexOf('--');
|
|
403
|
+
if (sepIndex !== -1) allowArgs.splice(sepIndex, 1);
|
|
404
|
+
|
|
405
|
+
const command = allowArgs[0];
|
|
406
|
+
const cmdArgs = allowArgs.slice(1);
|
|
407
|
+
|
|
408
|
+
if (!command) {
|
|
409
|
+
console.error('❌ Usage: telepty allow [--id <session_id>] <command> [args...]');
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Default session ID = command name
|
|
414
|
+
if (!sessionId) {
|
|
415
|
+
sessionId = path.basename(command);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
await ensureDaemonRunning();
|
|
419
|
+
|
|
420
|
+
// Register session with daemon
|
|
421
|
+
try {
|
|
422
|
+
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/register`, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
headers: { 'Content-Type': 'application/json' },
|
|
425
|
+
body: JSON.stringify({ session_id: sessionId, command, cwd: process.cwd() })
|
|
426
|
+
});
|
|
427
|
+
const data = await res.json();
|
|
428
|
+
if (!res.ok) {
|
|
429
|
+
console.error(`❌ Error: ${data.error}`);
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
} catch (e) {
|
|
433
|
+
console.error('❌ Failed to register with daemon:', e.message);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Spawn local PTY (preserves isTTY, env, shell config)
|
|
438
|
+
const pty = require('node-pty');
|
|
439
|
+
const child = pty.spawn(command, cmdArgs, {
|
|
440
|
+
name: 'xterm-256color',
|
|
441
|
+
cols: process.stdout.columns || 80,
|
|
442
|
+
rows: process.stdout.rows || 30,
|
|
443
|
+
cwd: process.cwd(),
|
|
444
|
+
env: { ...process.env, TELEPTY_SESSION_ID: sessionId }
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Connect to daemon WebSocket for inject reception and output relay
|
|
448
|
+
const wsUrl = `ws://${REMOTE_HOST}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
|
|
449
|
+
const daemonWs = new WebSocket(wsUrl);
|
|
450
|
+
let wsReady = false;
|
|
451
|
+
|
|
452
|
+
daemonWs.on('open', () => {
|
|
453
|
+
wsReady = true;
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Receive inject messages from daemon
|
|
457
|
+
daemonWs.on('message', (message) => {
|
|
458
|
+
try {
|
|
459
|
+
const msg = JSON.parse(message);
|
|
460
|
+
if (msg.type === 'inject') {
|
|
461
|
+
child.write(msg.data);
|
|
462
|
+
} else if (msg.type === 'resize') {
|
|
463
|
+
child.resize(msg.cols, msg.rows);
|
|
464
|
+
}
|
|
465
|
+
} catch (e) {
|
|
466
|
+
// ignore malformed messages
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
daemonWs.on('close', () => {
|
|
471
|
+
wsReady = false;
|
|
472
|
+
console.error(`\n\x1b[33m⚠️ Disconnected from daemon. Inject unavailable. Session continues locally.\x1b[0m`);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
daemonWs.on('error', () => {
|
|
476
|
+
// silently handle
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Set terminal title
|
|
480
|
+
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}\x07`);
|
|
481
|
+
console.log(`\x1b[32m⚡ '${command}' is now session '\x1b[36m${sessionId}\x1b[32m'. Inject allowed.\x1b[0m\n`);
|
|
482
|
+
|
|
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());
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
// Relay PTY output to current terminal + send to daemon for attach clients
|
|
491
|
+
child.onData((data) => {
|
|
492
|
+
process.stdout.write(data);
|
|
493
|
+
if (wsReady && daemonWs.readyState === 1) {
|
|
494
|
+
daemonWs.send(JSON.stringify({ type: 'output', data }));
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Handle terminal resize
|
|
499
|
+
process.stdout.on('resize', () => {
|
|
500
|
+
child.resize(process.stdout.columns, process.stdout.rows);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Handle child exit
|
|
504
|
+
child.onExit(({ exitCode }) => {
|
|
505
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
506
|
+
process.stdout.write(`\x1b]0;\x07`);
|
|
507
|
+
console.log(`\n\x1b[33mSession '${sessionId}' exited (code ${exitCode}).\x1b[0m`);
|
|
508
|
+
|
|
509
|
+
// Deregister from daemon
|
|
510
|
+
fetchWithAuth(`${DAEMON_URL}/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' }).catch(() => {});
|
|
511
|
+
daemonWs.close();
|
|
512
|
+
process.exit(exitCode || 0);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Graceful shutdown on SIGINT (let child handle it via PTY)
|
|
516
|
+
process.on('SIGINT', () => {});
|
|
517
|
+
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
|
|
359
521
|
if (cmd === 'attach') {
|
|
360
522
|
let sessionId = args[1];
|
|
361
523
|
let targetHost = REMOTE_HOST;
|
|
@@ -389,19 +551,25 @@ async function main() {
|
|
|
389
551
|
|
|
390
552
|
const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
|
|
391
553
|
const ws = new WebSocket(wsUrl);
|
|
554
|
+
let inputHandler = null;
|
|
555
|
+
let resizeHandler = null;
|
|
392
556
|
|
|
393
557
|
ws.on('open', () => {
|
|
394
|
-
|
|
395
|
-
|
|
558
|
+
// Set Ghostty tab title to show session ID
|
|
559
|
+
const hostSuffix = targetHost === '127.0.0.1' ? '' : ` @ ${targetHost}`;
|
|
560
|
+
process.stdout.write(`\x1b]0;⚡ telepty :: ${sessionId}${hostSuffix}\x07`);
|
|
561
|
+
console.log(`\x1b[32mEntered room '${sessionId}'${hostSuffix ? ` (${targetHost})` : ''}.\x1b[0m\n`);
|
|
562
|
+
|
|
396
563
|
if (process.stdin.isTTY) {
|
|
397
564
|
process.stdin.setRawMode(true);
|
|
398
565
|
}
|
|
399
566
|
|
|
400
|
-
|
|
567
|
+
inputHandler = (data) => {
|
|
401
568
|
ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
|
|
402
|
-
}
|
|
569
|
+
};
|
|
570
|
+
process.stdin.on('data', inputHandler);
|
|
403
571
|
|
|
404
|
-
|
|
572
|
+
resizeHandler = () => {
|
|
405
573
|
ws.send(JSON.stringify({
|
|
406
574
|
type: 'resize',
|
|
407
575
|
cols: process.stdout.columns,
|
|
@@ -424,9 +592,23 @@ async function main() {
|
|
|
424
592
|
if (process.stdin.isTTY) {
|
|
425
593
|
process.stdin.setRawMode(false);
|
|
426
594
|
}
|
|
427
|
-
|
|
595
|
+
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);
|
|
598
|
+
|
|
599
|
+
// Check if other clients are still attached before destroying
|
|
428
600
|
try {
|
|
429
|
-
await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions
|
|
601
|
+
const res = await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions`);
|
|
602
|
+
if (res.ok) {
|
|
603
|
+
const allSessions = await res.json();
|
|
604
|
+
const session = allSessions.find(s => s.id === sessionId);
|
|
605
|
+
if (session && session.active_clients > 0) {
|
|
606
|
+
console.log(`\n\x1b[33mLeft room '${sessionId}'. Other clients still attached — session kept alive.\x1b[0m`);
|
|
607
|
+
} else {
|
|
608
|
+
console.log(`\n\x1b[33mLeft room '${sessionId}'. No other clients — destroying session.\x1b[0m`);
|
|
609
|
+
await fetchWithAuth(`http://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
430
612
|
} catch(e) {}
|
|
431
613
|
process.exit(0);
|
|
432
614
|
});
|
|
@@ -490,6 +672,20 @@ async function main() {
|
|
|
490
672
|
return;
|
|
491
673
|
}
|
|
492
674
|
|
|
675
|
+
if (cmd === 'rename') {
|
|
676
|
+
const oldId = args[1]; const newId = args[2];
|
|
677
|
+
if (!oldId || !newId) { console.error('❌ Usage: telepty rename <old_id> <new_id>'); process.exit(1); }
|
|
678
|
+
try {
|
|
679
|
+
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/${encodeURIComponent(oldId)}`, {
|
|
680
|
+
method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ new_id: newId })
|
|
681
|
+
});
|
|
682
|
+
const data = await res.json();
|
|
683
|
+
if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
|
|
684
|
+
console.log(`✅ Session renamed: '\x1b[36m${oldId}\x1b[0m' → '\x1b[36m${newId}\x1b[0m'`);
|
|
685
|
+
} catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
|
|
493
689
|
if (cmd === 'listen' || cmd === 'monitor') {
|
|
494
690
|
await ensureDaemonRunning();
|
|
495
691
|
|
|
@@ -518,14 +714,19 @@ async function main() {
|
|
|
518
714
|
const msg = JSON.parse(raw);
|
|
519
715
|
const time = new Date().toLocaleTimeString();
|
|
520
716
|
const sender = msg.sender || msg.from || 'Unknown';
|
|
521
|
-
const target = msg.target_agent || msg.to || '
|
|
717
|
+
const target = msg.target_agent || msg.to || 'Bus';
|
|
718
|
+
|
|
719
|
+
let preview = msg.content || msg.message || msg.payload || msg.data;
|
|
720
|
+
if (msg.type === 'session_spawn') {
|
|
721
|
+
console.log(`\x1b[90m[${time}]\x1b[0m 🚀 \x1b[32m\x1b[1mNew Session\x1b[0m: \x1b[36m${msg.session_id}\x1b[0m (${msg.command})`);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
522
724
|
|
|
523
|
-
let preview = msg.content || msg.message || msg.payload || JSON.stringify(msg);
|
|
524
725
|
if (typeof preview === 'object') preview = JSON.stringify(preview);
|
|
525
|
-
if (preview.length > 200) preview = preview.substring(0, 197) + '...';
|
|
726
|
+
if (preview && preview.length > 200) preview = preview.substring(0, 197) + '...';
|
|
526
727
|
|
|
527
728
|
console.log(`\x1b[90m[${time}]\x1b[0m \x1b[32m\x1b[1m${sender}\x1b[0m ➔ \x1b[33m\x1b[1m${target}\x1b[0m`);
|
|
528
|
-
console.log(` \x1b[37m${preview}\x1b[0m\n`);
|
|
729
|
+
if (preview) console.log(` \x1b[37m${preview}\x1b[0m\n`);
|
|
529
730
|
} catch (e) {
|
|
530
731
|
// Fallback if not valid JSON
|
|
531
732
|
console.log(`\x1b[90m[${new Date().toLocaleTimeString()}]\x1b[0m 📦 \x1b[37m${raw}\x1b[0m\n`);
|
|
@@ -550,11 +751,13 @@ async function main() {
|
|
|
550
751
|
Usage:
|
|
551
752
|
telepty daemon Start the background daemon
|
|
552
753
|
telepty spawn --id <id> <command> [args...] Spawn a new background CLI
|
|
754
|
+
telepty allow [--id <id>] <command> [args...] Allow inject on a CLI
|
|
553
755
|
telepty list List all active sessions
|
|
554
756
|
telepty attach [id] Attach to a session (Interactive picker if no ID)
|
|
555
757
|
telepty inject [--no-enter] <id> "<prompt>" Inject text into a single session
|
|
556
758
|
telepty multicast <id1,id2> "<prompt>" Inject text into multiple specific sessions
|
|
557
759
|
telepty broadcast "<prompt>" Inject text into ALL active sessions
|
|
760
|
+
telepty rename <old_id> <new_id> Rename a session (updates terminal title too)
|
|
558
761
|
telepty listen Listen to the event bus and print JSON to stdout
|
|
559
762
|
telepty monitor Human-readable real-time billboard of bus events
|
|
560
763
|
telepty update Update telepty to the latest version
|