@dmsdc-ai/aigentry-telepty 0.1.78 → 0.1.80

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 (3) hide show
  1. package/cli.js +4 -0
  2. package/daemon.js +77 -4
  3. package/package.json +1 -1
package/cli.js CHANGED
@@ -1100,6 +1100,10 @@ async function main() {
1100
1100
  scheduleIdleFlush();
1101
1101
  }
1102
1102
  }
1103
+ // Reset readyNotified so next prompt detection re-notifies daemon (auto-report)
1104
+ if (rawData && rawData !== '\r') {
1105
+ readyNotified = false;
1106
+ }
1103
1107
  } else if (msg.type === 'resize') {
1104
1108
  child.resize(msg.cols, msg.rows);
1105
1109
  }
package/daemon.js CHANGED
@@ -13,6 +13,7 @@ const terminalBackend = require('./terminal-backend');
13
13
  const config = getConfig();
14
14
  const EXPECTED_TOKEN = config.authToken;
15
15
  const MACHINE_ID = process.env.TELEPTY_MACHINE_ID || os.hostname();
16
+ const net = require('net');
16
17
  const fs = require('fs');
17
18
  const SESSION_PERSIST_PATH = require('path').join(os.homedir(), '.config', 'aigentry-telepty', 'sessions.json');
18
19
  const SESSION_STALE_SECONDS = Math.max(1, Number(process.env.TELEPTY_SESSION_STALE_SECONDS || 60));
@@ -34,6 +35,8 @@ function persistSessions() {
34
35
  cmuxSurfaceId: s.cmuxSurfaceId || null,
35
36
  termProgram: s.termProgram || null,
36
37
  term: s.term || null,
38
+ delivery: s.delivery || null,
39
+ deliveryEndpoint: s.deliveryEndpoint || null,
37
40
  createdAt: s.createdAt,
38
41
  lastActivityAt: s.lastActivityAt || null,
39
42
  lastConnectedAt: s.lastConnectedAt || null,
@@ -160,6 +163,9 @@ if (!daemonClaim.claimed) {
160
163
  process.exit(0);
161
164
  }
162
165
 
166
+ const pendingReports = {}; // {targetSessionId: {source, injectedAt, injectId}}
167
+ const AUTO_REPORT_IDLE_SECONDS = Number(process.env.TELEPTY_AUTO_REPORT_IDLE_SECONDS) || 10;
168
+
163
169
  const sessions = {};
164
170
  const handoffs = {};
165
171
  const threads = {};
@@ -216,7 +222,7 @@ function getSessionHealthStatus(session, options = {}) {
216
222
  }
217
223
 
218
224
  if (session.type === 'aterm') {
219
- if (session.deliveryEndpoint) {
225
+ if (session.deliveryEndpoint || (session.delivery && session.delivery.address)) {
220
226
  return 'CONNECTED';
221
227
  }
222
228
  if (disconnectedMs !== null && disconnectedMs >= staleMs) {
@@ -423,6 +429,33 @@ function emitInjectFailureEvent(sessionId, code, error, extra = {}, session = nu
423
429
 
424
430
  async function writeDataToSession(id, session, data) {
425
431
  if (session.type === 'aterm') {
432
+ // UDS delivery via net.connect()
433
+ if (session.delivery && session.delivery.transport === 'unix_socket' && session.delivery.address) {
434
+ return new Promise((resolve) => {
435
+ const payload = JSON.stringify({ text: data, session_id: id }) + '\n';
436
+ const timeout = setTimeout(() => {
437
+ sock.destroy();
438
+ resolve(buildErrorBody('TIMEOUT', 'UDS delivery timed out.', { httpStatus: 504 }));
439
+ }, DELIVERY_TIMEOUT_MS);
440
+ const sock = net.connect(session.delivery.address, () => {
441
+ sock.end(payload);
442
+ });
443
+ sock.on('data', () => {}); // drain
444
+ sock.on('end', () => {
445
+ clearTimeout(timeout);
446
+ resolve({ success: true });
447
+ });
448
+ sock.on('error', (err) => {
449
+ clearTimeout(timeout);
450
+ resolve(buildErrorBody('DISCONNECTED', 'UDS endpoint is unreachable.', {
451
+ httpStatus: 503,
452
+ detail: err.message
453
+ }));
454
+ });
455
+ });
456
+ }
457
+
458
+ // HTTP delivery (backward compat)
426
459
  if (!session.deliveryEndpoint) {
427
460
  return buildErrorBody('DISCONNECTED', 'Delivery endpoint is missing.', { httpStatus: 503 });
428
461
  }
@@ -501,7 +534,7 @@ async function deliverInjectionToSession(id, session, prompt, options = {}) {
501
534
  strategy: session.type === 'wrapped'
502
535
  ? 'ws_split_cr'
503
536
  : session.type === 'aterm'
504
- ? 'aterm_endpoint'
537
+ ? (session.delivery && session.delivery.transport === 'unix_socket' ? 'aterm_uds' : 'aterm_endpoint')
505
538
  : 'pty_split_cr',
506
539
  submit: options.noEnter ? 'skipped' : 'deferred'
507
540
  };
@@ -565,6 +598,7 @@ function serializeSession(id, session, options = {}) {
565
598
  idleSeconds,
566
599
  active_clients: session.clients ? session.clients.size : 0,
567
600
  ready: session.ready || false,
601
+ delivery: session.delivery || null,
568
602
  deliveryEndpoint: session.deliveryEndpoint || null,
569
603
  healthStatus,
570
604
  healthReason,
@@ -784,6 +818,12 @@ app.post('/api/sessions/register', (req, res) => {
784
818
  if (Object.prototype.hasOwnProperty.call(req.body, 'term')) existing.term = term || null;
785
819
  if (req.body.delivery_type) existing.type = req.body.delivery_type;
786
820
  if (req.body.delivery_endpoint) existing.deliveryEndpoint = req.body.delivery_endpoint;
821
+ if (req.body.delivery) {
822
+ existing.delivery = req.body.delivery;
823
+ if (!existing.deliveryEndpoint && req.body.delivery.address) {
824
+ existing.deliveryEndpoint = req.body.delivery.address;
825
+ }
826
+ }
787
827
  if (req.body.delivery_type === 'aterm') {
788
828
  existing.ready = true;
789
829
  markSessionConnected(existing);
@@ -792,7 +832,8 @@ app.post('/api/sessions/register', (req, res) => {
792
832
  return res.status(200).json({ session_id, type: existing.type, command: existing.command, cwd: existing.cwd, reregistered: true });
793
833
  }
794
834
 
795
- const { delivery_type, delivery_endpoint } = req.body;
835
+ const { delivery_type, delivery_endpoint, delivery } = req.body;
836
+ const resolvedEndpoint = delivery_endpoint || (delivery && delivery.address) || null;
796
837
  const sessionRecord = {
797
838
  id: session_id,
798
839
  type: delivery_type || 'wrapped',
@@ -805,7 +846,8 @@ app.post('/api/sessions/register', (req, res) => {
805
846
  cmuxSurfaceId: cmux_surface_id || null,
806
847
  termProgram: term_program || null,
807
848
  term: term || null,
808
- deliveryEndpoint: delivery_endpoint || null,
849
+ delivery: delivery || null,
850
+ deliveryEndpoint: resolvedEndpoint,
809
851
  createdAt: new Date().toISOString(),
810
852
  lastActivityAt: new Date().toISOString(),
811
853
  lastConnectedAt: delivery_type === 'aterm' ? new Date().toISOString() : null,
@@ -1297,6 +1339,11 @@ app.post('/api/sessions/:id/inject', async (req, res) => {
1297
1339
  }
1298
1340
  });
1299
1341
 
1342
+ // Auto-report: track pending inject for idle notification back to source
1343
+ if (from) {
1344
+ pendingReports[id] = { source: from, injectedAt: injectTimestamp, injectId: inject_id };
1345
+ }
1346
+
1300
1347
  // Notify all attached viewers (telepty attach clients) about the inject
1301
1348
  // This enables aterm and other viewers to show inject events in real-time
1302
1349
  if (session.clients && session.clients.size > 0) {
@@ -1816,6 +1863,19 @@ setInterval(() => {
1816
1863
  });
1817
1864
  console.log(`[IDLE] Session ${id} idle for ${idleSeconds}s`);
1818
1865
  }
1866
+ // Auto-report for non-wrapped sessions: use idle threshold
1867
+ const pendingRpt = pendingReports[id];
1868
+ if (pendingRpt && session.type !== 'wrapped' && idleSeconds !== null && idleSeconds >= AUTO_REPORT_IDLE_SECONDS) {
1869
+ delete pendingReports[id];
1870
+ const elapsed = ((Date.now() - new Date(pendingRpt.injectedAt).getTime()) / 1000).toFixed(1);
1871
+ const reportMsg = `TASK_COMPLETE: ${id} is now idle after processing inject (${elapsed}s)`;
1872
+ const srcId = resolveSessionAlias(pendingRpt.source) || pendingRpt.source;
1873
+ const srcSession = sessions[srcId];
1874
+ if (srcSession) {
1875
+ deliverInjectionToSession(srcId, srcSession, reportMsg, { noEnter: false, source: 'auto_report' });
1876
+ console.log(`[AUTO-REPORT] ${id} → ${srcId}: idle after ${elapsed}s (threshold)`);
1877
+ }
1878
+ }
1819
1879
  // Reset idle flag when activity resumes
1820
1880
  if (idleSeconds !== null && idleSeconds < IDLE_THRESHOLD_SECONDS) {
1821
1881
  session._idleEmitted = false;
@@ -1977,6 +2037,19 @@ wss.on('connection', (ws, req) => {
1977
2037
  busClients.forEach(client => {
1978
2038
  if (client.readyState === 1) client.send(readyMsg);
1979
2039
  });
2040
+ // Auto-report: notify source that target completed inject task
2041
+ const pendingReport = pendingReports[sessionId];
2042
+ if (pendingReport) {
2043
+ delete pendingReports[sessionId];
2044
+ const elapsed = ((Date.now() - new Date(pendingReport.injectedAt).getTime()) / 1000).toFixed(1);
2045
+ const reportMsg = `TASK_COMPLETE: ${sessionId} is now idle after processing inject (${elapsed}s)`;
2046
+ const srcId = resolveSessionAlias(pendingReport.source) || pendingReport.source;
2047
+ const srcSession = sessions[srcId];
2048
+ if (srcSession) {
2049
+ deliverInjectionToSession(srcId, srcSession, reportMsg, { noEnter: false, source: 'auto_report' });
2050
+ console.log(`[AUTO-REPORT] ${sessionId} → ${srcId}: idle after ${elapsed}s`);
2051
+ }
2052
+ }
1980
2053
  }
1981
2054
  } else {
1982
2055
  // Non-owner client input -> forward to owner as inject
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "aigentry-telepty": "install.js",