@dmsdc-ai/aigentry-telepty 0.0.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dmsdc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # aigentry-telepty
2
+
3
+ **Personal PTY-based remote prompt injection daemon for AI CLIs.**
4
+
5
+ `telepty` (Tele-Prompt) is a lightweight, personal background daemon that bridges the gap between the network and interactive AI command-line interfaces.
Binary file
package/auth.js ADDED
@@ -0,0 +1,33 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const { v4: uuidv4 } = require('uuid');
5
+
6
+ const CONFIG_DIR = path.join(os.homedir(), '.telepty');
7
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
8
+
9
+ function getConfig() {
10
+ if (!fs.existsSync(CONFIG_DIR)) {
11
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 }); // Restrict permissions
12
+ }
13
+
14
+ if (fs.existsSync(CONFIG_FILE)) {
15
+ try {
16
+ const data = fs.readFileSync(CONFIG_FILE, 'utf8');
17
+ return JSON.parse(data);
18
+ } catch (e) {
19
+ console.warn('⚠️ Warning: Failed to read config file, generating a new one.', e.message);
20
+ }
21
+ }
22
+
23
+ // Generate new config
24
+ const newConfig = {
25
+ authToken: uuidv4(),
26
+ createdAt: new Date().toISOString()
27
+ };
28
+
29
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
30
+ return newConfig;
31
+ }
32
+
33
+ module.exports = { getConfig };
package/cli.js ADDED
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const WebSocket = require('ws');
5
+ const { execSync } = require('child_process');
6
+ const readline = require('readline');
7
+ const { getConfig } = require('./auth');
8
+ const args = process.argv.slice(2);
9
+
10
+ // Support remote host via environment variable or default to localhost
11
+ let REMOTE_HOST = process.env.TELEPTY_HOST || '127.0.0.1';
12
+ const PORT = 3848;
13
+ let DAEMON_URL = `http://${REMOTE_HOST}:${PORT}`;
14
+ let WS_URL = `ws://${REMOTE_HOST}:${PORT}`;
15
+
16
+ const config = getConfig();
17
+ const TOKEN = config.authToken;
18
+
19
+ const fetchWithAuth = (url, options = {}) => {
20
+ const headers = { ...options.headers, 'x-telepty-token': TOKEN };
21
+ return fetch(url, { ...options, headers });
22
+ };
23
+
24
+ async function discoverSessions() {
25
+ const hosts = ['127.0.0.1'];
26
+ try {
27
+ const tsStatus = execSync('tailscale status --json', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
28
+ const tsData = JSON.parse(tsStatus);
29
+ if (tsData && tsData.Peer) {
30
+ for (const peer of Object.values(tsData.Peer)) {
31
+ if (peer.Online && peer.TailscaleIPs && peer.TailscaleIPs.length > 0) {
32
+ hosts.push(peer.TailscaleIPs[0]);
33
+ }
34
+ }
35
+ }
36
+ } catch (e) {
37
+ // Tailscale not available or not running, ignore
38
+ }
39
+
40
+ const allSessions = [];
41
+ process.stdout.write('\x1b[36m🔍 Discovering active sessions across your Tailnet...\x1b[0m\n');
42
+
43
+ await Promise.all(hosts.map(async (host) => {
44
+ try {
45
+ const res = await fetchWithAuth(`http://${host}:${PORT}/api/sessions`, {
46
+ signal: AbortSignal.timeout(1500)
47
+ });
48
+ if (res.ok) {
49
+ const sessions = await res.json();
50
+ sessions.forEach(s => {
51
+ allSessions.push({ host, ...s });
52
+ });
53
+ }
54
+ } catch (e) {
55
+ // Ignore nodes that don't have telepty running
56
+ }
57
+ }));
58
+
59
+ return allSessions;
60
+ }
61
+
62
+ async function main() {
63
+ const cmd = args[0];
64
+ if (cmd === 'mcp') {
65
+ require('./mcp.js');
66
+ return;
67
+ }
68
+
69
+ if (cmd === 'daemon') {
70
+ console.log('Starting telepty daemon...');
71
+ require('./daemon.js');
72
+ return;
73
+ }
74
+
75
+ if (cmd === 'list') {
76
+ try {
77
+ const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions`);
78
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
79
+ const sessions = await res.json();
80
+ if (sessions.length === 0) { console.log('No active sessions found.'); return; }
81
+ console.log('\x1b[1mActive Sessions:\x1b[0m');
82
+ sessions.forEach(s => {
83
+ console.log(` - ID: \x1b[36m${s.id}\x1b[0m`);
84
+ console.log(` Command: ${s.command}`);
85
+ console.log(` CWD: ${s.cwd}`);
86
+ console.log(` Clients: ${s.active_clients}`);
87
+ console.log(` Started: ${new Date(s.createdAt).toLocaleString()}`);
88
+ console.log('');
89
+ });
90
+ } catch (e) {
91
+ console.error('❌ Failed to connect to daemon. Is it running? (run `telepty daemon`)');
92
+ }
93
+ return;
94
+ }
95
+
96
+ if (cmd === 'spawn') {
97
+ const idIndex = args.indexOf('--id');
98
+ if (idIndex === -1 || !args[idIndex + 1]) { console.error('❌ Usage: telepty spawn --id <session_id> <command> [args...]'); process.exit(1); }
99
+ const sessionId = args[idIndex + 1];
100
+ const spawnArgs = args.filter((a, i) => a !== 'spawn' && i !== idIndex && i !== idIndex + 1);
101
+ if (spawnArgs.length === 0) { console.error('❌ Missing command. Example: telepty spawn --id "test" bash'); process.exit(1); }
102
+ const command = spawnArgs[0]; const cmdArgs = spawnArgs.slice(1);
103
+
104
+ const cols = process.stdout.columns || 80;
105
+ const rows = process.stdout.rows || 30;
106
+
107
+ try {
108
+ const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/spawn`, {
109
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
110
+ body: JSON.stringify({ session_id: sessionId, command: command, args: cmdArgs, cwd: process.cwd(), cols, rows })
111
+ });
112
+ const data = await res.json();
113
+ if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
114
+ console.log(`✅ Session '\x1b[36m${data.session_id}\x1b[0m' spawned successfully.`);
115
+ } catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
116
+ return;
117
+ }
118
+
119
+ if (cmd === 'attach') {
120
+ let sessionId = args[1];
121
+ let targetHost = REMOTE_HOST;
122
+
123
+ if (!sessionId) {
124
+ const sessions = await discoverSessions();
125
+ if (sessions.length === 0) {
126
+ console.log('❌ No active sessions found on any known networks.');
127
+ process.exit(0);
128
+ }
129
+
130
+ console.log('\n\x1b[1mAvailable Sessions:\x1b[0m');
131
+ sessions.forEach((s, i) => {
132
+ const hostLabel = s.host === '127.0.0.1' ? 'Local' : s.host;
133
+ console.log(` [${i + 1}] \x1b[36m${s.id}\x1b[0m (\x1b[33m${hostLabel}\x1b[0m) - ${s.command}`);
134
+ });
135
+
136
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
137
+ const answer = await new Promise(resolve => rl.question('\nSelect a session number to attach: ', resolve));
138
+ rl.close();
139
+
140
+ const idx = parseInt(answer) - 1;
141
+ if (isNaN(idx) || !sessions[idx]) {
142
+ console.error('❌ Invalid selection.');
143
+ process.exit(1);
144
+ }
145
+
146
+ sessionId = sessions[idx].id;
147
+ targetHost = sessions[idx].host;
148
+ }
149
+
150
+ const wsUrl = `ws://${targetHost}:${PORT}/api/sessions/${encodeURIComponent(sessionId)}?token=${encodeURIComponent(TOKEN)}`;
151
+ const ws = new WebSocket(wsUrl);
152
+
153
+ ws.on('open', () => {
154
+ console.log(`\x1b[32mConnected to session '${sessionId}' at ${targetHost}. Press Ctrl+C to detach.\x1b[0m\n`);
155
+
156
+ if (process.stdin.isTTY) {
157
+ process.stdin.setRawMode(true);
158
+ }
159
+
160
+ process.stdin.on('data', (data) => {
161
+ ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
162
+ });
163
+
164
+ const resizeHandler = () => {
165
+ ws.send(JSON.stringify({
166
+ type: 'resize',
167
+ cols: process.stdout.columns,
168
+ rows: process.stdout.rows
169
+ }));
170
+ };
171
+
172
+ process.stdout.on('resize', resizeHandler);
173
+ resizeHandler(); // Initial resize
174
+ });
175
+
176
+ ws.on('message', (message) => {
177
+ const { type, data } = JSON.parse(message);
178
+ if (type === 'output') {
179
+ process.stdout.write(data);
180
+ }
181
+ });
182
+
183
+ ws.on('close', (code, reason) => {
184
+ if (process.stdin.isTTY) {
185
+ process.stdin.setRawMode(false);
186
+ }
187
+ console.log(`\n\x1b[33mDisconnected from session. (Code: ${code}, Reason: ${reason || 'None'})\x1b[0m`);
188
+ process.exit(0);
189
+ });
190
+
191
+ ws.on('error', (err) => {
192
+ console.error('❌ WebSocket Error:', err.message);
193
+ process.exit(1);
194
+ });
195
+
196
+ return;
197
+ }
198
+
199
+ if (cmd === 'inject') {
200
+ const sessionId = args[1]; const prompt = args.slice(2).join(' ');
201
+ if (!sessionId || !prompt) { console.error('❌ Usage: telepty inject <session_id> "<prompt text>"'); process.exit(1); }
202
+ try {
203
+ const res = await fetchWithAuth(`${DAEMON_URL}/api/sessions/${encodeURIComponent(sessionId)}/inject`, {
204
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt })
205
+ });
206
+ const data = await res.json();
207
+ if (!res.ok) { console.error(`❌ Error: ${data.error}`); return; }
208
+ console.log(`✅ Context injected successfully into '\x1b[36m${sessionId}\x1b[0m'.`);
209
+ } catch (e) { console.error('❌ Failed to connect to daemon. Is it running?'); }
210
+ return;
211
+ }
212
+
213
+ console.log(`
214
+ \x1b[1maigentry-telepty\x1b[0m - Remote PTY Control
215
+
216
+ Usage:
217
+ telepty daemon Start the background daemon
218
+ telepty spawn --id <id> <command> [args...] Spawn a new background CLI
219
+ telepty list List all active sessions
220
+ telepty attach [id] Attach to a session (Interactive picker if no ID)
221
+ telepty inject <id> "<prompt>" Inject text into an active session
222
+ telepty mcp Start the MCP stdio server
223
+ `);
224
+ }
225
+
226
+ main();
package/daemon.js ADDED
@@ -0,0 +1,159 @@
1
+ const express = require('express');
2
+ const cors = require('cors');
3
+ const pty = require('node-pty');
4
+ const os = require('os');
5
+ const { WebSocketServer } = require('ws');
6
+ const { getConfig } = require('./auth');
7
+
8
+ const config = getConfig();
9
+ const EXPECTED_TOKEN = config.authToken;
10
+
11
+ const app = express();
12
+ app.use(cors());
13
+ app.use(express.json());
14
+
15
+ // Authentication Middleware
16
+ app.use((req, res, next) => {
17
+ const isLocalhost = req.ip === '127.0.0.1' || req.ip === '::1' || req.ip === '::ffff:127.0.0.1';
18
+ const isTailscale = req.ip && req.ip.startsWith('100.');
19
+
20
+ if (isLocalhost || isTailscale) {
21
+ return next(); // Trust local and Tailscale networks
22
+ }
23
+
24
+ const token = req.headers['x-telepty-token'] || req.query.token;
25
+ if (token === EXPECTED_TOKEN) {
26
+ return next();
27
+ }
28
+
29
+ console.warn(`[AUTH] Rejected unauthorized request from ${req.ip}`);
30
+ res.status(401).json({ error: 'Unauthorized: Invalid or missing token.' });
31
+ });
32
+
33
+ const PORT = process.env.PORT || 3848;
34
+
35
+ const HOST = process.env.HOST || '0.0.0.0';
36
+
37
+ const sessions = {};
38
+
39
+ app.post('/api/sessions/spawn', (req, res) => {
40
+ const { session_id, command, args = [], cwd = process.cwd(), cols = 80, rows = 30 } = req.body;
41
+ if (!session_id) return res.status(400).json({ error: 'session_id is strictly required.' });
42
+ if (sessions[session_id]) return res.status(409).json({ error: `Session ID '${session_id}' is already active.` });
43
+ if (!command) return res.status(400).json({ error: 'command is required' });
44
+
45
+ const isWin = os.platform() === 'win32';
46
+ const shell = isWin ? (command === 'powershell' ? 'powershell.exe' : 'cmd.exe') : command;
47
+ const shellArgs = isWin ? (command === 'powershell' || command === 'cmd' ? args : ['/c', command, ...args]) : args;
48
+
49
+ try {
50
+ console.log(`[SPAWN] Spawning ${shell} with args:`, shellArgs, "in cwd:", cwd);
51
+ const ptyProcess = pty.spawn(shell, shellArgs, {
52
+ name: isWin ? 'Windows Terminal' : 'xterm-256color',
53
+ cols: parseInt(cols),
54
+ rows: parseInt(rows),
55
+ cwd,
56
+ env: { ...process.env, TERM: isWin ? undefined : 'xterm-256color' }
57
+ });
58
+
59
+ sessions[session_id] = {
60
+ ptyProcess,
61
+ command,
62
+ cwd,
63
+ createdAt: new Date().toISOString(),
64
+ clients: new Set()
65
+ };
66
+
67
+ ptyProcess.onData((data) => {
68
+ sessions[session_id].clients.forEach(ws => {
69
+ if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'output', data }));
70
+ });
71
+ });
72
+
73
+ ptyProcess.onExit(({ exitCode, signal }) => {
74
+ console.log(`[EXIT] Session ${session_id} exited with code ${exitCode}`);
75
+ sessions[session_id].clients.forEach(ws => ws.close(1000, 'Session exited'));
76
+ delete sessions[session_id];
77
+ });
78
+
79
+ console.log(`[SPAWN] Created session ${session_id} (${command})`);
80
+ res.status(201).json({ session_id, command, cwd });
81
+ } catch (err) {
82
+ res.status(500).json({ error: err.message });
83
+ }
84
+ });
85
+
86
+ app.get('/api/sessions', (req, res) => {
87
+ const list = Object.entries(sessions).map(([id, session]) => ({
88
+ id,
89
+ command: session.command,
90
+ cwd: session.cwd,
91
+ createdAt: session.createdAt,
92
+ active_clients: session.clients.size
93
+ }));
94
+ res.json(list);
95
+ });
96
+
97
+ app.post('/api/sessions/:id/inject', (req, res) => {
98
+ const { id } = req.params;
99
+ const { prompt } = req.body;
100
+ const session = sessions[id];
101
+ if (!session) return res.status(404).json({ error: 'Session not found' });
102
+ if (!prompt) return res.status(400).json({ error: 'prompt is required' });
103
+ try {
104
+ session.ptyProcess.write(`${prompt}\r`);
105
+ console.log(`[INJECT] Wrote to session ${id}`);
106
+ res.json({ success: true });
107
+ } catch (err) {
108
+ res.status(500).json({ error: err.message });
109
+ }
110
+ });
111
+
112
+ const server = app.listen(PORT, HOST, () => {
113
+ console.log(`🚀 aigentry-telepty daemon listening on http://${HOST}:${PORT}`);
114
+ });
115
+
116
+ const wss = new WebSocketServer({ server });
117
+
118
+ wss.on('connection', (ws, req) => {
119
+ const isLocalhost = req.socket.remoteAddress === '127.0.0.1' || req.socket.remoteAddress === '::1' || req.socket.remoteAddress === '::ffff:127.0.0.1';
120
+ const isTailscale = req.socket.remoteAddress && req.socket.remoteAddress.startsWith('100.');
121
+
122
+ const url = new URL(req.url, `http://${req.headers.host}`);
123
+ const token = url.searchParams.get('token');
124
+
125
+ if (!isLocalhost && !isTailscale && token !== EXPECTED_TOKEN) {
126
+ console.warn(`[WS-AUTH] Rejected unauthorized WebSocket from ${req.socket.remoteAddress}`);
127
+ ws.close(1008, 'Unauthorized');
128
+ return;
129
+ }
130
+
131
+ const sessionId = url.pathname.split('/').pop();
132
+ const session = sessions[sessionId];
133
+
134
+ if (!session) {
135
+ ws.close(1008, 'Session not found');
136
+ return;
137
+ }
138
+
139
+ session.clients.add(ws);
140
+ console.log(`[WS] Client attached to session ${sessionId} (Total: ${session.clients.size})`);
141
+
142
+ ws.on('message', (message) => {
143
+ try {
144
+ const { type, data, cols, rows } = JSON.parse(message);
145
+ if (type === 'input') {
146
+ session.ptyProcess.write(data);
147
+ } else if (type === 'resize') {
148
+ session.ptyProcess.resize(cols, rows);
149
+ }
150
+ } catch (e) {
151
+ console.error('[WS] Invalid message format', e);
152
+ }
153
+ });
154
+
155
+ ws.on('close', () => {
156
+ session.clients.delete(ws);
157
+ console.log(`[WS] Client detached from session ${sessionId} (Total: ${session.clients.size})`);
158
+ });
159
+ });
package/install.sh ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ echo "🚀 Installing aigentry-telepty..."
5
+ if ! command -v npm &> /dev/null; then
6
+ echo "❌ Error: npm is not installed. Please install Node.js first."
7
+ exit 1
8
+ fi
9
+
10
+ npm install -g git+https://github.com/dmsdc-ai/aigentry-telepty.git
11
+
12
+ # Set up systemd service if systemd is available
13
+ if command -v systemctl &> /dev/null && [ -d "/etc/systemd/system" ] && [ "$EUID" -eq 0 ]; then
14
+ echo "⚙️ Setting up systemd service..."
15
+ TELEPTY_PATH=$(which telepty)
16
+ cat <<SYSTEMD_EOF > /etc/systemd/system/telepty.service
17
+ [Unit]
18
+ Description=Telepty Daemon
19
+ After=network.target tailscaled.service
20
+
21
+ [Service]
22
+ ExecStart=$TELEPTY_PATH daemon
23
+ Restart=always
24
+ User=$SUDO_USER
25
+ Environment=PATH=/usr/bin:/usr/local/bin:$PATH
26
+ Environment=NODE_ENV=production
27
+
28
+ [Install]
29
+ WantedBy=multi-user.target
30
+ SYSTEMD_EOF
31
+
32
+ systemctl daemon-reload
33
+ systemctl enable telepty
34
+ systemctl start telepty
35
+ echo "✅ Systemd service installed and started. Daemon will run automatically on boot."
36
+ else
37
+ echo "⚠️ Skipping systemd setup (requires root and systemd). Starting daemon in background..."
38
+ nohup telepty daemon > /dev/null 2>&1 &
39
+ echo "✅ Daemon started in background. (Note: It will not auto-start on reboot)"
40
+ fi
41
+
42
+ echo "🎉 Installation complete!"
43
+ echo "You can now use 'telepty spawn --id <name> <command>' to create sessions."
package/mcp.js ADDED
@@ -0,0 +1,68 @@
1
+ const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
2
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
3
+ const { CallToolRequestSchema, ListToolsRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
4
+ const { z } = require('zod');
5
+ const { zodToJsonSchema } = require('zod-to-json-schema');
6
+ const { getConfig } = require('./auth');
7
+
8
+ const config = getConfig();
9
+ const TOKEN = config.authToken;
10
+
11
+ const server = new Server({ name: 'telepty-mcp-server', version: '0.0.1' }, { capabilities: { tools: {} } });
12
+
13
+ const tools = [
14
+ {
15
+ name: 'telepty_list_remote_sessions',
16
+ description: 'List all active AI CLI sessions (PTYs) running on a remote telepty daemon. Used to discover available target session IDs.',
17
+ schema: z.object({ remote_url: z.string().describe('Tailscale IP/Host and port (e.g., 100.100.100.5:3848) of the remote daemon.') })
18
+ },
19
+ {
20
+ name: 'telepty_inject_context',
21
+ description: 'Inject a prompt or context into an active AI CLI session on a remote machine. WARNING: You MUST use telepty_list_remote_sessions first to find the exact session_id, and ask the user for confirmation if ambiguous.',
22
+ schema: z.object({ remote_url: z.string(), session_id: z.string().describe('The EXACT session ID.'), prompt: z.string().describe('Text to inject into stdin.') })
23
+ }
24
+ ];
25
+
26
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
27
+ tools: tools.map(t => {
28
+ const schema = zodToJsonSchema(t.schema);
29
+ delete schema.$schema;
30
+ if (!schema.type) schema.type = 'object';
31
+ return { name: t.name, description: t.description, inputSchema: schema };
32
+ })
33
+ }));
34
+
35
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
36
+ try {
37
+ const { name, arguments: args } = request.params;
38
+ if (name === 'telepty_list_remote_sessions') {
39
+ const baseUrl = args.remote_url.startsWith('http') ? args.remote_url : `http://${args.remote_url}`;
40
+ const res = await fetch(`${baseUrl}/api/sessions`, { headers: { 'x-telepty-token': TOKEN } });
41
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
42
+ const sessions = await res.json();
43
+ if (!sessions || sessions.length === 0) return { content: [{ type: 'text', text: `No active sessions found on ${args.remote_url}` }] };
44
+ let text = `Active sessions on ${args.remote_url}:\n\n`;
45
+ sessions.forEach(s => { text += `- ID: ${s.id}\n Command: ${s.command}\n Workspace: ${s.cwd}\n\n`; });
46
+ return { content: [{ type: 'text', text }] };
47
+ }
48
+ if (name === 'telepty_inject_context') {
49
+ const baseUrl = args.remote_url.startsWith('http') ? args.remote_url : `http://${args.remote_url}`;
50
+ const res = await fetch(`${baseUrl}/api/sessions/${encodeURIComponent(args.session_id)}/inject`, {
51
+ method: 'POST', headers: { 'Content-Type': 'application/json', 'x-telepty-token': TOKEN }, body: JSON.stringify({ prompt: args.prompt })
52
+ });
53
+ const data = await res.json();
54
+ if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
55
+ return { content: [{ type: 'text', text: `✅ Successfully injected context into session '${args.session_id}'. The remote agent has been awakened.` }] };
56
+ }
57
+ throw new Error(`Unknown tool: ${name}`);
58
+ } catch (err) {
59
+ return { content: [{ type: 'text', text: `❌ Error: ${err.message}` }], isError: true };
60
+ }
61
+ });
62
+
63
+ async function main() {
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ console.error('Telepty MCP Server running on stdio');
67
+ }
68
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@dmsdc-ai/aigentry-telepty",
3
+ "version": "0.0.5",
4
+ "main": "daemon.js",
5
+ "bin": {
6
+ "telepty": "cli.js"
7
+ },
8
+ "scripts": {
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "description": "",
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.27.1",
17
+ "cors": "^2.8.6",
18
+ "express": "^5.2.1",
19
+ "node-pty": "^1.2.0-beta.11",
20
+ "uuid": "^13.0.0",
21
+ "ws": "^8.19.0",
22
+ "zod": "^4.3.6"
23
+ }
24
+ }
package/test-pty.js ADDED
@@ -0,0 +1,14 @@
1
+ const pty = require('node-pty');
2
+ try {
3
+ const p = pty.spawn('/bin/bash', [], {
4
+ name: 'xterm-color',
5
+ cols: 80,
6
+ rows: 30,
7
+ cwd: process.env.HOME,
8
+ env: process.env
9
+ });
10
+ console.log('Success, PID:', p.pid);
11
+ p.kill();
12
+ } catch (e) {
13
+ console.error('Error:', e);
14
+ }