@dmsdc-ai/aigentry-telepty 0.1.41 → 0.1.42

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.
Files changed (2) hide show
  1. package/daemon.js +52 -10
  2. 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 list = Object.entries(sessions).map(([id, session]) => ({
256
- id,
257
- type: session.type || 'spawned',
258
- command: session.command,
259
- cwd: session.cwd,
260
- createdAt: session.createdAt,
261
- active_clients: session.clients.size
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;
@@ -1037,8 +1053,11 @@ const server = app.listen(PORT, HOST, () => {
1037
1053
  console.log(`🚀 aigentry-telepty daemon listening on http://${HOST}:${PORT}`);
1038
1054
  });
1039
1055
 
1056
+ const IDLE_THRESHOLD_SECONDS = 60;
1040
1057
  setInterval(() => {
1058
+ const now = Date.now();
1041
1059
  for (const [id, session] of Object.entries(sessions)) {
1060
+ const idleSeconds = session.lastActivityAt ? Math.floor((now - new Date(session.lastActivityAt).getTime()) / 1000) : null;
1042
1061
  const healthMsg = JSON.stringify({
1043
1062
  type: 'session_health',
1044
1063
  session_id: id,
@@ -1046,13 +1065,34 @@ setInterval(() => {
1046
1065
  alive: session.type === 'wrapped' ? (session.ownerWs && session.ownerWs.readyState === 1) : (session.ptyProcess && !session.ptyProcess.killed),
1047
1066
  pid: session.ptyProcess?.pid || null,
1048
1067
  type: session.type,
1049
- clients: session.clients ? session.clients.size : 0
1068
+ clients: session.clients ? session.clients.size : 0,
1069
+ idleSeconds
1050
1070
  },
1051
1071
  timestamp: new Date().toISOString()
1052
1072
  });
1053
1073
  busClients.forEach(client => {
1054
1074
  if (client.readyState === 1) client.send(healthMsg);
1055
1075
  });
1076
+
1077
+ // Emit session.idle when idle exceeds threshold
1078
+ if (idleSeconds !== null && idleSeconds >= IDLE_THRESHOLD_SECONDS && !session._idleEmitted) {
1079
+ session._idleEmitted = true;
1080
+ const idleMsg = JSON.stringify({
1081
+ type: 'session.idle',
1082
+ session_id: id,
1083
+ idleSeconds,
1084
+ lastActivityAt: session.lastActivityAt,
1085
+ timestamp: new Date().toISOString()
1086
+ });
1087
+ busClients.forEach(client => {
1088
+ if (client.readyState === 1) client.send(idleMsg);
1089
+ });
1090
+ console.log(`[IDLE] Session ${id} idle for ${idleSeconds}s`);
1091
+ }
1092
+ // Reset idle flag when activity resumes
1093
+ if (idleSeconds !== null && idleSeconds < IDLE_THRESHOLD_SECONDS) {
1094
+ session._idleEmitted = false;
1095
+ }
1056
1096
  }
1057
1097
  }, 10000);
1058
1098
 
@@ -1085,6 +1125,7 @@ wss.on('connection', (ws, req) => {
1085
1125
  command: 'wrapped',
1086
1126
  cwd: process.cwd(),
1087
1127
  createdAt: new Date().toISOString(),
1128
+ lastActivityAt: new Date().toISOString(),
1088
1129
  clients: new Set([ws]),
1089
1130
  isClosing: false
1090
1131
  };
@@ -1123,8 +1164,9 @@ wss.on('connection', (ws, req) => {
1123
1164
 
1124
1165
  if (activeSession.type === 'wrapped') {
1125
1166
  if (ws === activeSession.ownerWs) {
1126
- // Owner sending output -> broadcast to other clients
1167
+ // Owner sending output -> broadcast to other clients + update activity
1127
1168
  if (type === 'output') {
1169
+ activeSession.lastActivityAt = new Date().toISOString();
1128
1170
  activeSession.clients.forEach(client => {
1129
1171
  if (client !== ws && client.readyState === 1) {
1130
1172
  client.send(JSON.stringify({ type: 'output', data }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.41",
3
+ "version": "0.1.42",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "aigentry-telepty": "install.js",