@dmsdc-ai/aigentry-telepty 0.1.41 → 0.1.43
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/daemon.js +66 -17
- package/package.json +1 -1
package/daemon.js
CHANGED
|
@@ -143,6 +143,7 @@ app.post('/api/sessions/spawn', (req, res) => {
|
|
|
143
143
|
command,
|
|
144
144
|
cwd,
|
|
145
145
|
createdAt: new Date().toISOString(),
|
|
146
|
+
lastActivityAt: new Date().toISOString(),
|
|
146
147
|
clients: new Set(),
|
|
147
148
|
isClosing: false
|
|
148
149
|
};
|
|
@@ -210,6 +211,7 @@ app.post('/api/sessions/register', (req, res) => {
|
|
|
210
211
|
command: command || 'wrapped',
|
|
211
212
|
cwd,
|
|
212
213
|
createdAt: new Date().toISOString(),
|
|
214
|
+
lastActivityAt: new Date().toISOString(),
|
|
213
215
|
clients: new Set(),
|
|
214
216
|
isClosing: false
|
|
215
217
|
};
|
|
@@ -252,14 +254,24 @@ app.post('/api/sessions/register', (req, res) => {
|
|
|
252
254
|
});
|
|
253
255
|
|
|
254
256
|
app.get('/api/sessions', (req, res) => {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
const idleGt = req.query.idle_gt ? Number(req.query.idle_gt) : null;
|
|
258
|
+
const now = Date.now();
|
|
259
|
+
let list = Object.entries(sessions).map(([id, session]) => {
|
|
260
|
+
const idleSeconds = session.lastActivityAt ? Math.floor((now - new Date(session.lastActivityAt).getTime()) / 1000) : null;
|
|
261
|
+
return {
|
|
262
|
+
id,
|
|
263
|
+
type: session.type || 'spawned',
|
|
264
|
+
command: session.command,
|
|
265
|
+
cwd: session.cwd,
|
|
266
|
+
createdAt: session.createdAt,
|
|
267
|
+
lastActivityAt: session.lastActivityAt || null,
|
|
268
|
+
idleSeconds,
|
|
269
|
+
active_clients: session.clients.size
|
|
270
|
+
};
|
|
271
|
+
});
|
|
272
|
+
if (idleGt !== null) {
|
|
273
|
+
list = list.filter(s => s.idleSeconds !== null && s.idleSeconds > idleGt);
|
|
274
|
+
}
|
|
263
275
|
res.json(list);
|
|
264
276
|
});
|
|
265
277
|
|
|
@@ -268,6 +280,7 @@ app.get('/api/sessions/:id', (req, res) => {
|
|
|
268
280
|
const resolvedId = resolveSessionAlias(requestedId);
|
|
269
281
|
if (!resolvedId) return res.status(404).json({ error: 'Session not found' });
|
|
270
282
|
const session = sessions[resolvedId];
|
|
283
|
+
const idleSeconds = session.lastActivityAt ? Math.floor((Date.now() - new Date(session.lastActivityAt).getTime()) / 1000) : null;
|
|
271
284
|
res.json({
|
|
272
285
|
id: resolvedId,
|
|
273
286
|
alias: requestedId !== resolvedId ? requestedId : null,
|
|
@@ -275,6 +288,8 @@ app.get('/api/sessions/:id', (req, res) => {
|
|
|
275
288
|
command: session.command,
|
|
276
289
|
cwd: session.cwd,
|
|
277
290
|
createdAt: session.createdAt,
|
|
291
|
+
lastActivityAt: session.lastActivityAt || null,
|
|
292
|
+
idleSeconds,
|
|
278
293
|
active_clients: session.clients ? session.clients.size : 0,
|
|
279
294
|
lastInjectFrom: session.lastInjectFrom || null,
|
|
280
295
|
lastInjectReplyTo: session.lastInjectReplyTo || null
|
|
@@ -611,6 +626,7 @@ app.post('/api/sessions/:id/inject', (req, res) => {
|
|
|
611
626
|
if (from) session.lastInjectFrom = from;
|
|
612
627
|
if (reply_to) session.lastInjectReplyTo = reply_to;
|
|
613
628
|
if (thread_id) session.lastThreadId = thread_id;
|
|
629
|
+
session.lastActivityAt = new Date().toISOString();
|
|
614
630
|
|
|
615
631
|
// Auto-prepend [from:] [reply-to:] header if from is set and not already in prompt
|
|
616
632
|
let finalPrompt = prompt;
|
|
@@ -644,25 +660,32 @@ app.post('/api/sessions/:id/inject', (req, res) => {
|
|
|
644
660
|
}
|
|
645
661
|
|
|
646
662
|
if (!no_enter) {
|
|
647
|
-
//
|
|
648
|
-
// Allow bridge 0.1.40+ has 3s queue flush timeout, so \r always gets delivered
|
|
663
|
+
// Split_cr: text first, \r after delay + kitty send-key Return as backup
|
|
649
664
|
setTimeout(() => {
|
|
650
665
|
const ok = writeToSession('\r');
|
|
651
666
|
console.log(`[INJECT+SUBMIT] Split \\r for ${id}: ${ok ? 'success' : 'failed'}`);
|
|
652
|
-
//
|
|
667
|
+
// Kitty send-key Return as backup (handles old allow bridges without queue flush)
|
|
653
668
|
try {
|
|
654
669
|
const sock = findKittySocket();
|
|
655
670
|
if (sock) {
|
|
656
671
|
if (!session.kittyWindowId) session.kittyWindowId = findKittyWindowId(sock, id);
|
|
657
672
|
if (session.kittyWindowId) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
673
|
+
const { execSync } = require('child_process');
|
|
674
|
+
setTimeout(() => {
|
|
675
|
+
try {
|
|
676
|
+
execSync(`kitty @ --to unix:${sock} send-key --match id:${session.kittyWindowId} Return`, {
|
|
677
|
+
timeout: 3000, stdio: ['pipe', 'pipe', 'pipe']
|
|
678
|
+
});
|
|
679
|
+
execSync(`kitty @ --to unix:${sock} set-tab-title --match id:${session.kittyWindowId} '⚡ telepty :: ${id}'`, {
|
|
680
|
+
timeout: 2000, stdio: ['pipe', 'pipe', 'pipe']
|
|
681
|
+
});
|
|
682
|
+
} catch {}
|
|
683
|
+
}, 500);
|
|
661
684
|
}
|
|
662
685
|
}
|
|
663
686
|
} catch {}
|
|
664
687
|
}, 300);
|
|
665
|
-
submitResult = { deferred: true, strategy: '
|
|
688
|
+
submitResult = { deferred: true, strategy: 'split_cr_kitty_backup' };
|
|
666
689
|
} else {
|
|
667
690
|
if (!writeToSession(finalPrompt)) {
|
|
668
691
|
return res.status(503).json({ error: 'Wrap process is not connected' });
|
|
@@ -1037,8 +1060,11 @@ const server = app.listen(PORT, HOST, () => {
|
|
|
1037
1060
|
console.log(`🚀 aigentry-telepty daemon listening on http://${HOST}:${PORT}`);
|
|
1038
1061
|
});
|
|
1039
1062
|
|
|
1063
|
+
const IDLE_THRESHOLD_SECONDS = 60;
|
|
1040
1064
|
setInterval(() => {
|
|
1065
|
+
const now = Date.now();
|
|
1041
1066
|
for (const [id, session] of Object.entries(sessions)) {
|
|
1067
|
+
const idleSeconds = session.lastActivityAt ? Math.floor((now - new Date(session.lastActivityAt).getTime()) / 1000) : null;
|
|
1042
1068
|
const healthMsg = JSON.stringify({
|
|
1043
1069
|
type: 'session_health',
|
|
1044
1070
|
session_id: id,
|
|
@@ -1046,13 +1072,34 @@ setInterval(() => {
|
|
|
1046
1072
|
alive: session.type === 'wrapped' ? (session.ownerWs && session.ownerWs.readyState === 1) : (session.ptyProcess && !session.ptyProcess.killed),
|
|
1047
1073
|
pid: session.ptyProcess?.pid || null,
|
|
1048
1074
|
type: session.type,
|
|
1049
|
-
clients: session.clients ? session.clients.size : 0
|
|
1075
|
+
clients: session.clients ? session.clients.size : 0,
|
|
1076
|
+
idleSeconds
|
|
1050
1077
|
},
|
|
1051
1078
|
timestamp: new Date().toISOString()
|
|
1052
1079
|
});
|
|
1053
1080
|
busClients.forEach(client => {
|
|
1054
1081
|
if (client.readyState === 1) client.send(healthMsg);
|
|
1055
1082
|
});
|
|
1083
|
+
|
|
1084
|
+
// Emit session.idle when idle exceeds threshold
|
|
1085
|
+
if (idleSeconds !== null && idleSeconds >= IDLE_THRESHOLD_SECONDS && !session._idleEmitted) {
|
|
1086
|
+
session._idleEmitted = true;
|
|
1087
|
+
const idleMsg = JSON.stringify({
|
|
1088
|
+
type: 'session.idle',
|
|
1089
|
+
session_id: id,
|
|
1090
|
+
idleSeconds,
|
|
1091
|
+
lastActivityAt: session.lastActivityAt,
|
|
1092
|
+
timestamp: new Date().toISOString()
|
|
1093
|
+
});
|
|
1094
|
+
busClients.forEach(client => {
|
|
1095
|
+
if (client.readyState === 1) client.send(idleMsg);
|
|
1096
|
+
});
|
|
1097
|
+
console.log(`[IDLE] Session ${id} idle for ${idleSeconds}s`);
|
|
1098
|
+
}
|
|
1099
|
+
// Reset idle flag when activity resumes
|
|
1100
|
+
if (idleSeconds !== null && idleSeconds < IDLE_THRESHOLD_SECONDS) {
|
|
1101
|
+
session._idleEmitted = false;
|
|
1102
|
+
}
|
|
1056
1103
|
}
|
|
1057
1104
|
}, 10000);
|
|
1058
1105
|
|
|
@@ -1085,6 +1132,7 @@ wss.on('connection', (ws, req) => {
|
|
|
1085
1132
|
command: 'wrapped',
|
|
1086
1133
|
cwd: process.cwd(),
|
|
1087
1134
|
createdAt: new Date().toISOString(),
|
|
1135
|
+
lastActivityAt: new Date().toISOString(),
|
|
1088
1136
|
clients: new Set([ws]),
|
|
1089
1137
|
isClosing: false
|
|
1090
1138
|
};
|
|
@@ -1123,8 +1171,9 @@ wss.on('connection', (ws, req) => {
|
|
|
1123
1171
|
|
|
1124
1172
|
if (activeSession.type === 'wrapped') {
|
|
1125
1173
|
if (ws === activeSession.ownerWs) {
|
|
1126
|
-
// Owner sending output -> broadcast to other clients
|
|
1174
|
+
// Owner sending output -> broadcast to other clients + update activity
|
|
1127
1175
|
if (type === 'output') {
|
|
1176
|
+
activeSession.lastActivityAt = new Date().toISOString();
|
|
1128
1177
|
activeSession.clients.forEach(client => {
|
|
1129
1178
|
if (client !== ws && client.readyState === 1) {
|
|
1130
1179
|
client.send(JSON.stringify({ type: 'output', data }));
|