@dmsdc-ai/aigentry-telepty 0.1.83 → 0.1.84
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 +2 -1
- package/mcp-server/index.mjs +163 -0
- package/package.json +8 -3
- package/templates/AGENTS.md +71 -0
package/cli.js
CHANGED
|
@@ -871,6 +871,7 @@ async function main() {
|
|
|
871
871
|
}
|
|
872
872
|
delete process.env.TELEPTY_SESSION_ID;
|
|
873
873
|
process.env.TELEPTY_SESSION_ID = sessionId;
|
|
874
|
+
process.env.TELEPTY_AVAILABLE = 'true';
|
|
874
875
|
|
|
875
876
|
await ensureDaemonRunning({ requiredCapabilities: ['wrapped-sessions'] });
|
|
876
877
|
|
|
@@ -914,7 +915,7 @@ async function main() {
|
|
|
914
915
|
// Spawn local PTY (preserves isTTY, env, shell config)
|
|
915
916
|
const pty = require('node-pty');
|
|
916
917
|
const sessionCwd = process.cwd();
|
|
917
|
-
const sessionEnv = { ...process.env, TELEPTY_SESSION_ID: sessionId };
|
|
918
|
+
const sessionEnv = { ...process.env, TELEPTY_SESSION_ID: sessionId, TELEPTY_AVAILABLE: 'true' };
|
|
918
919
|
let child = null;
|
|
919
920
|
let sessionStartTime = Date.now();
|
|
920
921
|
let crashCount = 0;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP Telepty Server — Session management tools for AI CLIs
|
|
5
|
+
*
|
|
6
|
+
* Tools:
|
|
7
|
+
* telepty_list_sessions List all active telepty sessions
|
|
8
|
+
* telepty_inject_session Inject text into a session
|
|
9
|
+
* telepty_session_status Get detailed status of a session
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import os from "os";
|
|
18
|
+
|
|
19
|
+
const PKG_VERSION = (() => {
|
|
20
|
+
try {
|
|
21
|
+
const p = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
22
|
+
return p.version || "0.0.0";
|
|
23
|
+
} catch { return "0.0.0"; }
|
|
24
|
+
})();
|
|
25
|
+
|
|
26
|
+
// ── Config ──
|
|
27
|
+
|
|
28
|
+
function getAuthToken() {
|
|
29
|
+
try {
|
|
30
|
+
const configPath = path.join(os.homedir(), ".telepty", "config.json");
|
|
31
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
32
|
+
return config.authToken || "";
|
|
33
|
+
} catch { return ""; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getDaemonUrl() {
|
|
37
|
+
const port = process.env.TELEPTY_PORT || "3848";
|
|
38
|
+
const host = process.env.TELEPTY_HOST || "127.0.0.1";
|
|
39
|
+
return `http://${host}:${port}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function daemonFetch(endpoint, options = {}) {
|
|
43
|
+
const url = `${getDaemonUrl()}${endpoint}`;
|
|
44
|
+
const token = getAuthToken();
|
|
45
|
+
const headers = {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
...(token ? { "x-telepty-token": token } : {}),
|
|
48
|
+
...options.headers,
|
|
49
|
+
};
|
|
50
|
+
const res = await fetch(url, { ...options, headers });
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const body = await res.text().catch(() => "");
|
|
53
|
+
throw new Error(`telepty daemon ${res.status}: ${body || res.statusText}`);
|
|
54
|
+
}
|
|
55
|
+
return res.json();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── MCP Server ──
|
|
59
|
+
|
|
60
|
+
const server = new McpServer({
|
|
61
|
+
name: "telepty",
|
|
62
|
+
version: PKG_VERSION,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Tool: telepty_list_sessions
|
|
66
|
+
server.tool(
|
|
67
|
+
"telepty_list_sessions",
|
|
68
|
+
"List all active telepty sessions with their status, type, and last activity.",
|
|
69
|
+
{},
|
|
70
|
+
async () => {
|
|
71
|
+
try {
|
|
72
|
+
const sessions = await daemonFetch("/api/sessions");
|
|
73
|
+
if (!Array.isArray(sessions) || sessions.length === 0) {
|
|
74
|
+
return { content: [{ type: "text", text: "No active sessions." }] };
|
|
75
|
+
}
|
|
76
|
+
let text = `## Active Sessions (${sessions.length})\n\n`;
|
|
77
|
+
text += "| ID | Type | Status | Last Activity |\n";
|
|
78
|
+
text += "|----|------|--------|---------------|\n";
|
|
79
|
+
for (const s of sessions) {
|
|
80
|
+
const status = s.connected ? "connected" : (s.stale ? "stale" : "disconnected");
|
|
81
|
+
const lastAct = s.lastActivityAt ? new Date(s.lastActivityAt).toLocaleString() : "—";
|
|
82
|
+
text += `| ${s.id} | ${s.type || "—"} | ${status} | ${lastAct} |\n`;
|
|
83
|
+
}
|
|
84
|
+
return { content: [{ type: "text", text }] };
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return { content: [{ type: "text", text: `Error listing sessions: ${err.message}` }], isError: true };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Tool: telepty_session_status
|
|
92
|
+
server.tool(
|
|
93
|
+
"telepty_session_status",
|
|
94
|
+
"Get detailed status of a specific telepty session.",
|
|
95
|
+
{
|
|
96
|
+
session_id: z.string().describe("Session ID to query"),
|
|
97
|
+
},
|
|
98
|
+
async ({ session_id }) => {
|
|
99
|
+
try {
|
|
100
|
+
const sessions = await daemonFetch("/api/sessions");
|
|
101
|
+
const session = Array.isArray(sessions)
|
|
102
|
+
? sessions.find(s => s.id === session_id || s.id.startsWith(session_id))
|
|
103
|
+
: null;
|
|
104
|
+
if (!session) {
|
|
105
|
+
return { content: [{ type: "text", text: `Session "${session_id}" not found.` }] };
|
|
106
|
+
}
|
|
107
|
+
const status = session.connected ? "connected" : (session.stale ? "stale" : "disconnected");
|
|
108
|
+
let text = `## Session: ${session.id}\n\n`;
|
|
109
|
+
text += `- **Type:** ${session.type || "unknown"}\n`;
|
|
110
|
+
text += `- **Status:** ${status}\n`;
|
|
111
|
+
text += `- **Command:** ${session.command || "—"}\n`;
|
|
112
|
+
text += `- **CWD:** ${session.cwd || "—"}\n`;
|
|
113
|
+
text += `- **Backend:** ${session.backend || "—"}\n`;
|
|
114
|
+
text += `- **Created:** ${session.createdAt ? new Date(session.createdAt).toLocaleString() : "—"}\n`;
|
|
115
|
+
text += `- **Last Activity:** ${session.lastActivityAt ? new Date(session.lastActivityAt).toLocaleString() : "—"}\n`;
|
|
116
|
+
if (session.stateReport) {
|
|
117
|
+
text += `- **State Report:** ${session.stateReport}\n`;
|
|
118
|
+
}
|
|
119
|
+
return { content: [{ type: "text", text }] };
|
|
120
|
+
} catch (err) {
|
|
121
|
+
return { content: [{ type: "text", text: `Error querying session: ${err.message}` }], isError: true };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Tool: telepty_inject_session
|
|
127
|
+
server.tool(
|
|
128
|
+
"telepty_inject_session",
|
|
129
|
+
"Inject text into a telepty session. Use this to send messages or prompts to other AI sessions.",
|
|
130
|
+
{
|
|
131
|
+
session_id: z.string().describe("Target session ID"),
|
|
132
|
+
text: z.string().describe("Text to inject"),
|
|
133
|
+
from: z.string().optional().describe("Sender session ID (return address)"),
|
|
134
|
+
no_enter: z.boolean().default(false).describe("If true, do not send Enter after text"),
|
|
135
|
+
},
|
|
136
|
+
async ({ session_id, text, from, no_enter }) => {
|
|
137
|
+
try {
|
|
138
|
+
const body = { prompt: text };
|
|
139
|
+
if (from) body.from = from;
|
|
140
|
+
if (no_enter) body.no_enter = true;
|
|
141
|
+
const result = await daemonFetch(`/api/sessions/${encodeURIComponent(session_id)}/inject`, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
body: JSON.stringify(body),
|
|
144
|
+
});
|
|
145
|
+
const deliveryInfo = result.deliveryPath ? ` (via ${result.deliveryPath})` : "";
|
|
146
|
+
return { content: [{ type: "text", text: `Injected to ${session_id}${deliveryInfo}.` }] };
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return { content: [{ type: "text", text: `Error injecting to ${session_id}: ${err.message}` }], isError: true };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// ── Start ──
|
|
154
|
+
|
|
155
|
+
async function main() {
|
|
156
|
+
const transport = new StdioServerTransport();
|
|
157
|
+
await server.connect(transport);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main().catch((err) => {
|
|
161
|
+
console.error("telepty MCP server fatal:", err);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmsdc-ai/aigentry-telepty",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.84",
|
|
4
4
|
"main": "daemon.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aigentry-telepty": "install.js",
|
|
7
7
|
"telepty": "cli.js",
|
|
8
|
-
"telepty-install": "install.js"
|
|
8
|
+
"telepty-install": "install.js",
|
|
9
|
+
"telepty-mcp": "mcp-server/index.mjs"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"test": "node --test test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js",
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
"remote",
|
|
21
22
|
"inject",
|
|
22
23
|
"ai-cli",
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
23
26
|
"claude",
|
|
24
27
|
"codex",
|
|
25
28
|
"gemini",
|
|
@@ -38,6 +41,7 @@
|
|
|
38
41
|
"url": "https://github.com/dmsdc-ai/aigentry-telepty/issues"
|
|
39
42
|
},
|
|
40
43
|
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
41
45
|
"blessed": "^0.1.81",
|
|
42
46
|
"cors": "^2.8.6",
|
|
43
47
|
"express": "^5.2.1",
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
"prompts": "^2.4.2",
|
|
46
50
|
"update-notifier": "^5.1.0",
|
|
47
51
|
"uuid": "^13.0.0",
|
|
48
|
-
"ws": "^8.19.0"
|
|
52
|
+
"ws": "^8.19.0",
|
|
53
|
+
"zod": "^3.24.0"
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# telepty Session Communication / 세션 통신
|
|
2
|
+
|
|
3
|
+
AI CLI sessions use `telepty` to communicate with other sessions.
|
|
4
|
+
|
|
5
|
+
## Environment Detection / 환경 감지
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Check if telepty is available
|
|
9
|
+
echo "$TELEPTY_AVAILABLE" # "true" if telepty is installed and daemon running
|
|
10
|
+
|
|
11
|
+
# Get current session ID
|
|
12
|
+
echo "$TELEPTY_SESSION_ID" # e.g., "aigentry-brain-claude"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If `$TELEPTY_AVAILABLE` is not set, telepty commands are unavailable.
|
|
16
|
+
|
|
17
|
+
## Commands / 명령어
|
|
18
|
+
|
|
19
|
+
| English action | 한국어 동작 | Command |
|
|
20
|
+
|----------------|------------|---------|
|
|
21
|
+
| List sessions | 세션 목록 보기 | `telepty list` |
|
|
22
|
+
| Send to session | 세션에 메시지 보내기 | `telepty inject <session> 'message'` |
|
|
23
|
+
| Send with return address | 발신자 포함 전송 | `telepty inject --from $TELEPTY_SESSION_ID <session> 'message'` |
|
|
24
|
+
| Send without enter | 엔터 없이 전송 | `telepty inject --no-enter <session> 'message'` |
|
|
25
|
+
| Broadcast to all | 전체 브로드캐스트 | `telepty broadcast 'message'` |
|
|
26
|
+
| Multicast to group | 그룹 멀티캐스트 | `telepty multicast <id1,id2> 'message'` |
|
|
27
|
+
| Reply to last sender | 마지막 발신자에게 답장 | `telepty reply 'message'` |
|
|
28
|
+
| Check session status | 세션 상태 확인 | `telepty list` (check connected/stale) |
|
|
29
|
+
| Read session screen | 세션 화면 읽기 | `curl -s http://127.0.0.1:3848/api/sessions/<id>/screen -H "x-telepty-token: $TOKEN"` |
|
|
30
|
+
| Kill session | 세션 종료 | `telepty delete <session>` |
|
|
31
|
+
| Clean stale sessions | 비활성 세션 정리 | `telepty clean` |
|
|
32
|
+
| Rename session | 세션 이름 변경 | `telepty rename <old> <new>` |
|
|
33
|
+
| Listen to event bus | 이벤트 버스 수신 | `telepty listen` |
|
|
34
|
+
| Attach to session | 세션에 접속 | `telepty attach <session>` |
|
|
35
|
+
|
|
36
|
+
## Natural Language → Command / 자연어 → 명령어
|
|
37
|
+
|
|
38
|
+
| English request | 한국어 요청 | Command |
|
|
39
|
+
|----------------|------------|---------|
|
|
40
|
+
| `list sessions` | `세션 목록 보여줘` | `telepty list` |
|
|
41
|
+
| `send to brain` | `brain에 메시지 보내줘` | `telepty inject aigentry-brain-claude 'msg'` |
|
|
42
|
+
| `broadcast message` | `전체에 메시지 보내줘` | `telepty broadcast 'msg'` |
|
|
43
|
+
| `reply to sender` | `답장해줘` | `telepty reply 'msg'` |
|
|
44
|
+
| `kill session` | `세션 종료해줘` | `telepty delete <session>` |
|
|
45
|
+
| `clean stale` | `비활성 정리해줘` | `telepty clean` |
|
|
46
|
+
| `check status` | `상태 확인해줘` | `telepty list` |
|
|
47
|
+
| `rename session` | `세션 이름 바꿔줘` | `telepty rename <old> <new>` |
|
|
48
|
+
| `read screen` | `화면 읽어줘` | REST API `/api/sessions/<id>/screen` |
|
|
49
|
+
|
|
50
|
+
## Cross-Machine Sessions / 크로스 머신 세션
|
|
51
|
+
|
|
52
|
+
When the same session ID exists on multiple hosts, use `session_id@host`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
telepty inject brain-claude@macbook 'message'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Rules / 규칙
|
|
59
|
+
|
|
60
|
+
1. **Return address**: Always include `--from $TELEPTY_SESSION_ID` when expecting a reply.
|
|
61
|
+
2. **Submit method**: Never rely on PTY `\r` directly. Use `telepty inject` which handles submit automatically.
|
|
62
|
+
3. **Env detection**: Check `$TELEPTY_AVAILABLE` before using telepty commands.
|
|
63
|
+
4. **Session ID format**: Use `<project>-<cli>` pattern (e.g., `aigentry-brain-claude`).
|
|
64
|
+
|
|
65
|
+
## Reporting / 보고
|
|
66
|
+
|
|
67
|
+
On task completion, report to orchestrator:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
telepty inject --from $TELEPTY_SESSION_ID aigentry-orchestrator-claude 'REPORT: <summary>'
|
|
71
|
+
```
|