@dmsdc-ai/aigentry-telepty 0.5.9 → 0.6.1

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/cross-machine.js CHANGED
@@ -8,6 +8,7 @@ const { getSharedContextPromptPath } = require('./shared-context');
8
8
  const { parseHostSpec } = require('./host-spec');
9
9
 
10
10
  const PEERS_PATH = path.join(os.homedir(), '.telepty', 'peers.json');
11
+ const BROKER_CONFIG_PATH = path.join(os.homedir(), '.telepty', 'broker.json');
11
12
  const CONTROL_DIR = path.join(os.homedir(), '.telepty', 'ssh');
12
13
 
13
14
  function getPeerTransport(entry) {
@@ -34,6 +35,30 @@ function savePeers(data) {
34
35
  } catch {}
35
36
  }
36
37
 
38
+ function normalizeBrokerUrl(url) {
39
+ const value = String(url || '').trim();
40
+ if (!value) return '';
41
+ try {
42
+ const parsed = new URL(value);
43
+ return parsed.toString().replace(/\/+$/, '');
44
+ } catch {
45
+ return '';
46
+ }
47
+ }
48
+
49
+ function loadBrokerConfig() {
50
+ try {
51
+ if (!fs.existsSync(BROKER_CONFIG_PATH)) return null;
52
+ return JSON.parse(fs.readFileSync(BROKER_CONFIG_PATH, 'utf8'));
53
+ } catch { return null; }
54
+ }
55
+
56
+ function saveBrokerConfig(config) {
57
+ fs.mkdirSync(path.dirname(BROKER_CONFIG_PATH), { recursive: true });
58
+ fs.writeFileSync(BROKER_CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
59
+ try { fs.chmodSync(BROKER_CONFIG_PATH, 0o600); } catch {}
60
+ }
61
+
37
62
  // In-memory active peers
38
63
  const activePeers = new Map(); // name -> { target, controlSocket, connectedAt, machineId }
39
64
 
@@ -469,6 +494,97 @@ async function discoverHttpRemoteSessions(options = {}) {
469
494
  return results.flat();
470
495
  }
471
496
 
497
+ // ── Broker peer support (opt-in relay discovery) ────────────────────────────
498
+ // connect-broker stores the node's broker credentials in broker.json (0600) and
499
+ // records a non-secret transport='broker' peer in peers.json. Discovery remains
500
+ // default-OFF: no broker peer entry means list discovery never calls the broker.
501
+
502
+ async function connectBroker(url, options = {}) {
503
+ const brokerUrl = normalizeBrokerUrl(url);
504
+ if (!brokerUrl) {
505
+ return { success: false, error: 'connect-broker requires a valid broker URL.' };
506
+ }
507
+
508
+ const node = String(options.node || '').trim();
509
+ if (!node) {
510
+ return { success: false, error: 'connect-broker requires --node <name>.' };
511
+ }
512
+
513
+ const jwt = String(options.jwt || '').trim();
514
+ if (!jwt) {
515
+ return { success: false, error: 'connect-broker requires a node JWT.' };
516
+ }
517
+
518
+ const connectedAt = new Date().toISOString();
519
+ const config = {
520
+ url: brokerUrl,
521
+ node,
522
+ jwt,
523
+ pin: options.pin || null,
524
+ accept_from: null
525
+ };
526
+ saveBrokerConfig(config);
527
+
528
+ const peers = loadPeers();
529
+ peers.peers[node] = {
530
+ transport: 'broker',
531
+ node,
532
+ url: brokerUrl,
533
+ machineId: node,
534
+ lastConnected: connectedAt
535
+ };
536
+ savePeers(peers);
537
+
538
+ return { success: true, name: node, node, url: brokerUrl };
539
+ }
540
+
541
+ function listBrokerPeers() {
542
+ const peers = loadPeers().peers || {};
543
+ return Object.entries(peers)
544
+ .filter(([, entry]) => getPeerTransport(entry) === 'broker')
545
+ .map(([name, entry]) => ({
546
+ name,
547
+ node: entry.node || entry.machineId || name,
548
+ url: entry.url,
549
+ machineId: entry.machineId || entry.node || name,
550
+ lastConnected: entry.lastConnected
551
+ }));
552
+ }
553
+
554
+ async function listBrokerRemoteSessions(options = {}) {
555
+ const config = options.config || loadBrokerConfig();
556
+ const brokerUrl = normalizeBrokerUrl(options.url || (config && config.url));
557
+ const jwt = String(options.jwt || (config && config.jwt) || '').trim();
558
+ if (!brokerUrl || !jwt) return [];
559
+
560
+ try {
561
+ const res = await fetch(`${brokerUrl}/broker/sessions`, {
562
+ signal: AbortSignal.timeout(options.timeoutMs || 3000),
563
+ headers: { Authorization: `Bearer ${jwt}` }
564
+ });
565
+ if (!res.ok) return [];
566
+ const body = await res.json();
567
+ const sessions = Array.isArray(body) ? body : body && body.sessions;
568
+ if (!Array.isArray(sessions)) return [];
569
+ return sessions.map((session) => {
570
+ const base = (session && typeof session === 'object') ? session : { id: session };
571
+ const node = base.peerName || base.host || base.node || base.machineId || base.machine_id;
572
+ return {
573
+ ...base,
574
+ host: node,
575
+ peerName: node
576
+ };
577
+ });
578
+ } catch {
579
+ return [];
580
+ }
581
+ }
582
+
583
+ async function discoverBrokerRemoteSessions(options = {}) {
584
+ if (listBrokerPeers().length === 0) return [];
585
+ return listBrokerRemoteSessions(options);
586
+ }
587
+
472
588
  module.exports = {
473
589
  connect,
474
590
  disconnect,
@@ -490,9 +606,16 @@ module.exports = {
490
606
  listHttpPeers,
491
607
  listHttpRemoteSessions,
492
608
  discoverHttpRemoteSessions,
609
+ // Broker peer transport (opt-in relay discovery)
610
+ connectBroker,
611
+ listBrokerPeers,
612
+ listBrokerRemoteSessions,
613
+ discoverBrokerRemoteSessions,
614
+ loadBrokerConfig,
493
615
  // File-backed SSH peer enumeration (cross-process — #411)
494
616
  listSshPeers,
495
617
  getSshPeerHandle,
496
618
  getPeerTransport,
497
- PEERS_PATH
619
+ PEERS_PATH,
620
+ BROKER_CONFIG_PATH
498
621
  };
package/daemon-control.js CHANGED
@@ -102,6 +102,14 @@ function isLikelyTeleptyDaemon(commandLine) {
102
102
  return false;
103
103
  }
104
104
 
105
+ // telepty#44: the running daemon sets process.title = 'telepty-daemon' (daemon.js:188).
106
+ // On macOS/Linux that REPLACES the command field `ps -axo command=` returns, so the
107
+ // daemon's own title (hyphen) — not its launch command line — is what the process scan
108
+ // and port-owner confirmation see. Recognize it so the stop path is no longer blind to it.
109
+ if (text.includes('telepty-daemon')) {
110
+ return true;
111
+ }
112
+
105
113
  if (text.includes('telepty daemon')) {
106
114
  return true;
107
115
  }
@@ -336,6 +344,7 @@ module.exports = {
336
344
  cleanupDaemonProcesses,
337
345
  clearDaemonState,
338
346
  findPortOwnerPid,
347
+ isLikelyTeleptyDaemon,
339
348
  isProcessRunning,
340
349
  listDaemonProcesses,
341
350
  pidMatchesTeleptyCmdline,