@dmsdc-ai/aigentry-telepty 0.1.68 → 0.1.70
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 +129 -52
- package/cross-machine.js +174 -128
- package/daemon.js +230 -48
- package/interactive-terminal.js +46 -1
- package/package.json +1 -1
- package/terminal-backend.js +137 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
// Detect terminal environment at daemon level
|
|
6
|
+
function detectTerminal() {
|
|
7
|
+
// 1. cmux: check env var or cmux ping
|
|
8
|
+
if (process.env.CMUX_WORKSPACE_ID) {
|
|
9
|
+
try {
|
|
10
|
+
execSync('cmux ping', { timeout: 2000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
11
|
+
return 'cmux';
|
|
12
|
+
} catch {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 2. kitty: check for socket
|
|
16
|
+
try {
|
|
17
|
+
const files = require('fs').readdirSync('/tmp').filter(f => f.startsWith('kitty-sock'));
|
|
18
|
+
if (files.length > 0) return 'kitty';
|
|
19
|
+
} catch {}
|
|
20
|
+
|
|
21
|
+
// 3. headless fallback
|
|
22
|
+
return 'headless';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Cache: sessionId -> surfaceRef
|
|
26
|
+
const surfaceCache = new Map();
|
|
27
|
+
let lastCacheRefresh = 0;
|
|
28
|
+
const CACHE_TTL = 30000; // 30 seconds
|
|
29
|
+
|
|
30
|
+
// Build session -> cmux surface mapping from tab titles
|
|
31
|
+
function refreshSurfaceCache() {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
if (now - lastCacheRefresh < CACHE_TTL && surfaceCache.size > 0) return;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Find number of workspaces from list-windows
|
|
37
|
+
const windowsOutput = execSync('cmux list-windows', { timeout: 5000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
38
|
+
const workspacesMatch = windowsOutput.match(/workspaces=(\d+)/);
|
|
39
|
+
const workspaceCount = workspacesMatch ? parseInt(workspacesMatch[1]) : 10;
|
|
40
|
+
|
|
41
|
+
surfaceCache.clear();
|
|
42
|
+
for (let i = 1; i <= workspaceCount; i++) {
|
|
43
|
+
try {
|
|
44
|
+
const output = execSync(`cmux list-pane-surfaces --workspace workspace:${i}`, {
|
|
45
|
+
timeout: 3000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
|
|
46
|
+
});
|
|
47
|
+
// Parse: "* surface:1 ⚡ telepty :: aigentry-orchestrator-claude [selected]"
|
|
48
|
+
const lines = output.split('\n').filter(l => l.trim());
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
const surfaceMatch = line.match(/surface:(\d+)/);
|
|
51
|
+
const sessionMatch = line.match(/telepty\s*::\s*(\S+)/);
|
|
52
|
+
if (surfaceMatch && sessionMatch) {
|
|
53
|
+
surfaceCache.set(sessionMatch[1], `surface:${surfaceMatch[1]}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch {}
|
|
57
|
+
}
|
|
58
|
+
lastCacheRefresh = now;
|
|
59
|
+
console.log(`[BACKEND] Refreshed cmux surface cache: ${surfaceCache.size} sessions mapped`);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error(`[BACKEND] Failed to refresh surface cache:`, err.message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Find cmux surface ref for a session
|
|
66
|
+
function findSurface(sessionId) {
|
|
67
|
+
refreshSurfaceCache();
|
|
68
|
+
|
|
69
|
+
// Direct match
|
|
70
|
+
if (surfaceCache.has(sessionId)) return surfaceCache.get(sessionId);
|
|
71
|
+
|
|
72
|
+
// Prefix match (e.g., "aigentry-orchestrator" matches "aigentry-orchestrator-claude")
|
|
73
|
+
for (const [id, ref] of surfaceCache.entries()) {
|
|
74
|
+
if (id.startsWith(sessionId) || sessionId.startsWith(id)) return ref;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Send text to a cmux surface
|
|
81
|
+
function cmuxSendText(sessionId, text) {
|
|
82
|
+
const surface = findSurface(sessionId);
|
|
83
|
+
if (!surface) return false;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Escape single quotes for shell
|
|
87
|
+
const escaped = text.replace(/'/g, "'\\''");
|
|
88
|
+
execSync(`cmux send --surface ${surface} '${escaped}'`, {
|
|
89
|
+
timeout: 5000, stdio: ['pipe', 'pipe', 'pipe']
|
|
90
|
+
});
|
|
91
|
+
console.log(`[BACKEND] cmux send text to ${sessionId} (${surface})`);
|
|
92
|
+
return true;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error(`[BACKEND] cmux send failed for ${sessionId}:`, err.message);
|
|
95
|
+
// Invalidate cache entry
|
|
96
|
+
surfaceCache.delete(sessionId);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Send enter key to a cmux surface
|
|
102
|
+
function cmuxSendEnter(sessionId) {
|
|
103
|
+
const surface = findSurface(sessionId);
|
|
104
|
+
if (!surface) return false;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
execSync(`cmux send-key --surface ${surface} return`, {
|
|
108
|
+
timeout: 5000, stdio: ['pipe', 'pipe', 'pipe']
|
|
109
|
+
});
|
|
110
|
+
console.log(`[BACKEND] cmux send-key return to ${sessionId} (${surface})`);
|
|
111
|
+
return true;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error(`[BACKEND] cmux send-key failed for ${sessionId}:`, err.message);
|
|
114
|
+
surfaceCache.delete(sessionId);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Invalidate cache for a session (e.g., when surface changes)
|
|
120
|
+
function invalidateCache(sessionId) {
|
|
121
|
+
surfaceCache.delete(sessionId);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function clearCache() {
|
|
125
|
+
surfaceCache.clear();
|
|
126
|
+
lastCacheRefresh = 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
detectTerminal,
|
|
131
|
+
findSurface,
|
|
132
|
+
cmuxSendText,
|
|
133
|
+
cmuxSendEnter,
|
|
134
|
+
refreshSurfaceCache,
|
|
135
|
+
invalidateCache,
|
|
136
|
+
clearCache
|
|
137
|
+
};
|