@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.
- package/cli.js +4 -0
- package/daemon.js +77 -4
- 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
|
-
|
|
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
|