@dmsdc-ai/aigentry-telepty 0.0.8 → 0.0.10
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/.gemini/skills/telepty/SKILL.md +3 -2
- package/.github/workflows/test-install.yml +32 -0
- package/cli.js +62 -1
- package/daemon.js +42 -0
- package/install.ps1 +11 -11
- package/mcp.js +34 -5
- package/package.json +1 -1
|
@@ -14,5 +14,6 @@ When the user asks about their current session ID (e.g. "내 세션 ID가 뭐야
|
|
|
14
14
|
2. **To list all sessions:**
|
|
15
15
|
- Run `telepty list`.
|
|
16
16
|
3. **To inject a command into another session:**
|
|
17
|
-
-
|
|
18
|
-
- Run `telepty
|
|
17
|
+
- For a single session: Run `telepty inject <target_session_id> "<message or command>"`.
|
|
18
|
+
- For broadcasting to ALL active sessions: Run `telepty broadcast "<message or command>"`.
|
|
19
|
+
- For multicasting to multiple specific sessions: Run `telepty multicast <id1>,<id2> "<message or command>"`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Test Installation Scripts
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ "main" ]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [ "main" ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test-windows:
|
|
10
|
+
runs-on: windows-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v3
|
|
13
|
+
- name: Run Install Script
|
|
14
|
+
shell: powershell
|
|
15
|
+
run: |
|
|
16
|
+
./install.ps1
|
|
17
|
+
|
|
18
|
+
test-ubuntu:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v3
|
|
22
|
+
- name: Run Install Script
|
|
23
|
+
run: |
|
|
24
|
+
bash ./install.sh
|
|
25
|
+
|
|
26
|
+
test-macos:
|
|
27
|
+
runs-on: macos-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v3
|
|
30
|
+
- name: Run Install Script
|
|
31
|
+
run: |
|
|
32
|
+
bash ./install.sh
|
package/cli.js
CHANGED
|
@@ -60,6 +60,25 @@ async function discoverSessions() {
|
|
|
60
60
|
return allSessions;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
async function ensureDaemonRunning() {
|
|
64
|
+
if (REMOTE_HOST !== '127.0.0.1') return; // Only auto-start local daemon
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions`);
|
|
67
|
+
if (res.ok) return; // Already running
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// Not running, let's start it
|
|
70
|
+
process.stdout.write('\x1b[33m⚙️ Auto-starting local telepty daemon...\x1b[0m\n');
|
|
71
|
+
const cp = spawn(process.argv[0], [process.argv[1], 'daemon'], {
|
|
72
|
+
detached: true,
|
|
73
|
+
stdio: 'ignore'
|
|
74
|
+
});
|
|
75
|
+
cp.unref();
|
|
76
|
+
|
|
77
|
+
// Wait a brief moment for the daemon to boot up
|
|
78
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
async function manageInteractive() {
|
|
64
83
|
console.clear();
|
|
65
84
|
console.log('\x1b[36m\x1b[1m⚡ Telepty Agent Manager\x1b[0m\n');
|
|
@@ -118,6 +137,8 @@ async function manageInteractive() {
|
|
|
118
137
|
]);
|
|
119
138
|
if (!id || !command) continue;
|
|
120
139
|
|
|
140
|
+
await ensureDaemonRunning();
|
|
141
|
+
|
|
121
142
|
const cols = process.stdout.columns || 80;
|
|
122
143
|
const rows = process.stdout.rows || 30;
|
|
123
144
|
try {
|
|
@@ -218,6 +239,7 @@ async function main() {
|
|
|
218
239
|
}
|
|
219
240
|
|
|
220
241
|
if (cmd === 'list') {
|
|
242
|
+
await ensureDaemonRunning();
|
|
221
243
|
try {
|
|
222
244
|
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions`);
|
|
223
245
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
@@ -239,6 +261,7 @@ async function main() {
|
|
|
239
261
|
}
|
|
240
262
|
|
|
241
263
|
if (cmd === 'spawn') {
|
|
264
|
+
await ensureDaemonRunning();
|
|
242
265
|
const idIndex = args.indexOf('--id');
|
|
243
266
|
if (idIndex === -1 || !args[idIndex + 1]) { console.error('❌ Usage: telepty spawn --id <session_id> <command> [args...]'); process.exit(1); }
|
|
244
267
|
const sessionId = args[idIndex + 1];
|
|
@@ -262,6 +285,7 @@ async function main() {
|
|
|
262
285
|
}
|
|
263
286
|
|
|
264
287
|
if (cmd === 'attach') {
|
|
288
|
+
await ensureDaemonRunning();
|
|
265
289
|
let sessionId = args[1];
|
|
266
290
|
let targetHost = REMOTE_HOST;
|
|
267
291
|
|
|
@@ -342,6 +366,7 @@ async function main() {
|
|
|
342
366
|
}
|
|
343
367
|
|
|
344
368
|
if (cmd === 'inject') {
|
|
369
|
+
await ensureDaemonRunning();
|
|
345
370
|
const sessionId = args[1]; const prompt = args.slice(2).join(' ');
|
|
346
371
|
if (!sessionId || !prompt) { console.error('❌ Usage: telepty inject <session_id> "<prompt text>"'); process.exit(1); }
|
|
347
372
|
try {
|
|
@@ -355,6 +380,40 @@ async function main() {
|
|
|
355
380
|
return;
|
|
356
381
|
}
|
|
357
382
|
|
|
383
|
+
if (cmd === 'multicast') {
|
|
384
|
+
await ensureDaemonRunning();
|
|
385
|
+
const sessionIdsRaw = args[1]; const prompt = args.slice(2).join(' ');
|
|
386
|
+
if (!sessionIdsRaw || !prompt) { console.error('❌ Usage: telepty multicast <id1,id2,...> "<prompt text>"'); process.exit(1); }
|
|
387
|
+
const sessionIds = sessionIdsRaw.split(',').map(s => s.trim()).filter(s => s);
|
|
388
|
+
try {
|
|
389
|
+
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/multicast/inject`, {
|
|
390
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_ids: sessionIds, prompt })
|
|
391
|
+
});
|
|
392
|
+
const data = await res.json();
|
|
393
|
+
if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
|
|
394
|
+
console.log(`✅ Context multicasted successfully to ${data.results.successful.length} sessions.`);
|
|
395
|
+
if (data.results.failed.length > 0) {
|
|
396
|
+
console.warn(`⚠️ Failed to inject into ${data.results.failed.length} sessions:`, data.results.failed.map(f => f.id).join(', '));
|
|
397
|
+
}
|
|
398
|
+
} catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (cmd === 'broadcast') {
|
|
403
|
+
await ensureDaemonRunning();
|
|
404
|
+
const prompt = args.slice(1).join(' ');
|
|
405
|
+
if (!prompt) { console.error('❌ Usage: telepty broadcast "<prompt text>"'); process.exit(1); }
|
|
406
|
+
try {
|
|
407
|
+
const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/broadcast/inject`, {
|
|
408
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt })
|
|
409
|
+
});
|
|
410
|
+
const data = await res.json();
|
|
411
|
+
if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
|
|
412
|
+
console.log(`✅ Context broadcasted successfully to ${data.results.successful.length} active sessions.`);
|
|
413
|
+
} catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
358
417
|
console.log(`
|
|
359
418
|
\x1b[1maigentry-telepty\x1b[0m - Remote PTY Control
|
|
360
419
|
|
|
@@ -363,7 +422,9 @@ Usage:
|
|
|
363
422
|
telepty spawn --id <id> <command> [args...] Spawn a new background CLI
|
|
364
423
|
telepty list List all active sessions
|
|
365
424
|
telepty attach [id] Attach to a session (Interactive picker if no ID)
|
|
366
|
-
telepty inject <id> "<prompt>" Inject text into
|
|
425
|
+
telepty inject <id> "<prompt>" Inject text into a single session
|
|
426
|
+
telepty multicast <id1,id2> "<prompt>" Inject text into multiple specific sessions
|
|
427
|
+
telepty broadcast "<prompt>" Inject text into ALL active sessions
|
|
367
428
|
telepty mcp Start the MCP stdio server
|
|
368
429
|
`);
|
|
369
430
|
}
|
package/daemon.js
CHANGED
|
@@ -94,6 +94,48 @@ app.get('/api/sessions', (req, res) => {
|
|
|
94
94
|
res.json(list);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
app.post('/api/sessions/multicast/inject', (req, res) => {
|
|
98
|
+
const { session_ids, prompt } = req.body;
|
|
99
|
+
if (!prompt) return res.status(400).json({ error: 'prompt is required' });
|
|
100
|
+
if (!Array.isArray(session_ids)) return res.status(400).json({ error: 'session_ids must be an array' });
|
|
101
|
+
|
|
102
|
+
const results = { successful: [], failed: [] };
|
|
103
|
+
|
|
104
|
+
session_ids.forEach(id => {
|
|
105
|
+
const session = sessions[id];
|
|
106
|
+
if (session) {
|
|
107
|
+
try {
|
|
108
|
+
session.ptyProcess.write(`${prompt}\r`);
|
|
109
|
+
results.successful.push(id);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
results.failed.push({ id, error: err.message });
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
results.failed.push({ id, error: 'Session not found' });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
res.json({ success: true, results });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
app.post('/api/sessions/broadcast/inject', (req, res) => {
|
|
122
|
+
const { prompt } = req.body;
|
|
123
|
+
if (!prompt) return res.status(400).json({ error: 'prompt is required' });
|
|
124
|
+
|
|
125
|
+
const results = { successful: [], failed: [] };
|
|
126
|
+
|
|
127
|
+
Object.keys(sessions).forEach(id => {
|
|
128
|
+
try {
|
|
129
|
+
sessions[id].ptyProcess.write(`${prompt}\r`);
|
|
130
|
+
results.successful.push(id);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
results.failed.push({ id, error: err.message });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
res.json({ success: true, results });
|
|
137
|
+
});
|
|
138
|
+
|
|
97
139
|
app.post('/api/sessions/:id/inject', (req, res) => {
|
|
98
140
|
const { id } = req.params;
|
|
99
141
|
const { prompt } = req.body;
|
package/install.ps1
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
Write-Host "
|
|
1
|
+
Write-Host "Installing @dmsdc-ai/aigentry-telepty..." -ForegroundColor Cyan
|
|
2
2
|
|
|
3
3
|
# 1. Check for Node.js/npm and install if missing
|
|
4
4
|
if (!(Get-Command npm -ErrorAction SilentlyContinue)) {
|
|
5
|
-
Write-Host "
|
|
5
|
+
Write-Host "Warning: Node.js/npm not found. Attempting to install via winget..." -ForegroundColor Yellow
|
|
6
6
|
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
|
7
7
|
winget install OpenJS.NodeJS --accept-package-agreements --accept-source-agreements
|
|
8
|
-
Write-Host "
|
|
8
|
+
Write-Host "Refreshing environment variables..." -ForegroundColor Cyan
|
|
9
9
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
10
10
|
} else {
|
|
11
|
-
Write-Host "
|
|
11
|
+
Write-Host "Error: winget not found. Please install Node.js manually: https://nodejs.org/" -ForegroundColor Red
|
|
12
12
|
exit 1
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
# 2. Install telepty via npm
|
|
17
|
-
Write-Host "
|
|
17
|
+
Write-Host "Installing telepty globally..." -ForegroundColor Cyan
|
|
18
18
|
npm install -g @dmsdc-ai/aigentry-telepty
|
|
19
19
|
|
|
20
20
|
# 3. Setup Daemon
|
|
21
|
-
Write-Host "
|
|
21
|
+
Write-Host "Setting up Windows background process..." -ForegroundColor Cyan
|
|
22
22
|
$teleptyCmd = Get-Command telepty -ErrorAction SilentlyContinue
|
|
23
23
|
if (!$teleptyCmd) {
|
|
24
|
-
Write-Host "
|
|
24
|
+
Write-Host "Error: Failed to locate telepty executable after installation." -ForegroundColor Red
|
|
25
25
|
exit 1
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
$teleptyPath = $teleptyCmd.Source
|
|
29
|
-
Start-Process -
|
|
30
|
-
Write-Host "
|
|
29
|
+
Start-Process -FilePath node -ArgumentList "$teleptyPath daemon" -WindowStyle Hidden
|
|
30
|
+
Write-Host "Success: Windows daemon started in background." -ForegroundColor Green
|
|
31
31
|
|
|
32
|
-
Write-Host "`
|
|
33
|
-
Write-Host "
|
|
32
|
+
Write-Host "`nInstallation complete! Telepty daemon is running." -ForegroundColor Cyan
|
|
33
|
+
Write-Host "Next step: Try running: telepty attach" -ForegroundColor Yellow
|
package/mcp.js
CHANGED
|
@@ -18,8 +18,14 @@ const tools = [
|
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
name: 'telepty_inject_context',
|
|
21
|
-
description: 'Inject a prompt or context into
|
|
22
|
-
schema: z.object({
|
|
21
|
+
description: 'Inject a prompt or context into specific active AI CLI sessions on a remote machine. You can specify a single session ID, multiple session IDs, or broadcast to all.',
|
|
22
|
+
schema: z.object({
|
|
23
|
+
remote_url: z.string(),
|
|
24
|
+
session_ids: z.array(z.string()).optional().describe('An array of exact session IDs to inject into. If not provided, it will inject into session_id.'),
|
|
25
|
+
session_id: z.string().optional().describe('Legacy fallback for a single session ID.'),
|
|
26
|
+
broadcast: z.boolean().optional().describe('If true, injects the prompt into ALL active sessions on the remote daemon. Overrides session_ids.'),
|
|
27
|
+
prompt: z.string().describe('Text to inject into stdin.')
|
|
28
|
+
})
|
|
23
29
|
}
|
|
24
30
|
];
|
|
25
31
|
|
|
@@ -47,12 +53,35 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
47
53
|
}
|
|
48
54
|
if (name === 'telepty_inject_context') {
|
|
49
55
|
const baseUrl = args.remote_url.startsWith('http') ? args.remote_url : `http://${args.remote_url}`;
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
|
|
57
|
+
let endpoint = '';
|
|
58
|
+
let body = {};
|
|
59
|
+
|
|
60
|
+
if (args.broadcast) {
|
|
61
|
+
endpoint = `${baseUrl}/api/sessions/broadcast/inject`;
|
|
62
|
+
body = { prompt: args.prompt };
|
|
63
|
+
} else if (args.session_ids && args.session_ids.length > 0) {
|
|
64
|
+
endpoint = `${baseUrl}/api/sessions/multicast/inject`;
|
|
65
|
+
body = { session_ids: args.session_ids, prompt: args.prompt };
|
|
66
|
+
} else if (args.session_id) {
|
|
67
|
+
endpoint = `${baseUrl}/api/sessions/${encodeURIComponent(args.session_id)}/inject`;
|
|
68
|
+
body = { prompt: args.prompt };
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error('You must provide either broadcast: true, session_ids: [...], or session_id: "..."');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const res = await fetch(endpoint, {
|
|
74
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', 'x-telepty-token': TOKEN }, body: JSON.stringify(body)
|
|
52
75
|
});
|
|
53
76
|
const data = await res.json();
|
|
54
77
|
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
|
|
55
|
-
|
|
78
|
+
|
|
79
|
+
let msg = `✅ Successfully injected context.`;
|
|
80
|
+
if (args.broadcast) msg += ` (Broadcasted to ${data.results.successful.length} sessions)`;
|
|
81
|
+
else if (args.session_ids) msg += ` (Multicasted to ${data.results.successful.length} sessions)`;
|
|
82
|
+
else msg += ` (Targeted session '${args.session_id}')`;
|
|
83
|
+
|
|
84
|
+
return { content: [{ type: 'text', text: msg }] };
|
|
56
85
|
}
|
|
57
86
|
throw new Error(`Unknown tool: ${name}`);
|
|
58
87
|
} catch (err) {
|