@dmsdc-ai/aigentry-telepty 0.1.9 → 0.1.11

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/README.md CHANGED
@@ -20,6 +20,12 @@ Open PowerShell as Administrator and run:
20
20
  iwr -useb https://raw.githubusercontent.com/dmsdc-ai/aigentry-telepty/main/install.ps1 | iex
21
21
  ```
22
22
 
23
+ You can also launch the installer through npm without downloading the script first:
24
+
25
+ ```bash
26
+ npx --yes @dmsdc-ai/aigentry-telepty@latest
27
+ ```
28
+
23
29
  *These single commands will install the package globally and automatically configure it to run as a background service specific to your OS (`systemd` for Linux, `launchd` for macOS, or a detached background process for Windows).*
24
30
  The installer now stops older local telepty daemons before starting the new one, so updates do not leave duplicate background processes behind.
25
31
 
@@ -57,11 +63,7 @@ npm run test:watch
57
63
 
58
64
  The automated suite covers config generation, daemon HTTP APIs, WebSocket attach/output flow, bus events, session deletion regressions, and CLI smoke tests against a real daemon process.
59
65
 
60
- If you ever need to manually clear stale local daemon processes:
61
-
62
- ```bash
63
- telepty cleanup-daemons
64
- ```
66
+ If the local daemon ever gets stuck or duplicated, open `telepty` and choose `Repair local daemon`.
65
67
 
66
68
  ## Skill Installation
67
69
 
package/cli.js CHANGED
@@ -10,7 +10,8 @@ const updateNotifier = require('update-notifier');
10
10
  const pkg = require('./package.json');
11
11
  const { getConfig } = require('./auth');
12
12
  const { cleanupDaemonProcesses } = require('./daemon-control');
13
- const { attachInteractiveTerminal } = require('./interactive-terminal');
13
+ const { attachInteractiveTerminal, getTerminalSize } = require('./interactive-terminal');
14
+ const { getRuntimeInfo } = require('./runtime-info');
14
15
  const { runInteractiveSkillInstaller } = require('./skill-installer');
15
16
  const args = process.argv.slice(2);
16
17
 
@@ -55,6 +56,20 @@ function startDetachedDaemon() {
55
56
  cp.unref();
56
57
  }
57
58
 
59
+ async function repairLocalDaemon(options = {}) {
60
+ const restart = options.restart !== false;
61
+ const results = cleanupDaemonProcesses();
62
+
63
+ if (!restart) {
64
+ return { stopped: results.stopped.length, failed: results.failed.length, meta: null };
65
+ }
66
+
67
+ startDetachedDaemon();
68
+ await new Promise((resolve) => setTimeout(resolve, 1000));
69
+ const meta = await getDaemonMeta('127.0.0.1');
70
+ return { stopped: results.stopped.length, failed: results.failed.length, meta };
71
+ }
72
+
58
73
  async function discoverSessions() {
59
74
  await ensureDaemonRunning();
60
75
  const hosts = ['127.0.0.1'];
@@ -129,7 +144,7 @@ async function ensureDaemonRunning(options = {}) {
129
144
 
130
145
  const meta = await getDaemonMeta('127.0.0.1');
131
146
  if (!meta || !requiredCapabilities.every((item) => meta.capabilities.includes(item))) {
132
- console.error('❌ Failed to start a compatible local telepty daemon. Try `telepty cleanup-daemons` or rerun the installer.');
147
+ console.error('❌ Failed to start a compatible local telepty daemon. Open telepty and choose "Repair local daemon", or rerun the installer.');
133
148
  }
134
149
  }
135
150
 
@@ -144,7 +159,10 @@ async function manageInteractiveAttach(sessionId, targetHost) {
144
159
  console.log(`\n\x1b[32mEntered room '${sessionId}'.\x1b[0m\n`);
145
160
  cleanupTerminal = attachInteractiveTerminal(process.stdin, process.stdout, {
146
161
  onData: (d) => ws.send(JSON.stringify({ type: 'input', data: d.toString() })),
147
- onResize: () => ws.send(JSON.stringify({ type: 'resize', cols: process.stdout.columns, rows: process.stdout.rows }))
162
+ onResize: () => {
163
+ const size = getTerminalSize(process.stdout, { cols: 80, rows: 30 });
164
+ ws.send(JSON.stringify({ type: 'resize', cols: size.cols, rows: size.rows }));
165
+ }
148
166
  });
149
167
  });
150
168
  ws.on('message', m => {
@@ -178,8 +196,10 @@ async function manageInteractiveAttach(sessionId, targetHost) {
178
196
  }
179
197
 
180
198
  async function manageInteractive() {
199
+ const runtimeInfo = getRuntimeInfo(__dirname);
181
200
  console.clear();
182
201
  console.log('\x1b[36m\x1b[1m⚡ Telepty Agent Manager\x1b[0m\n');
202
+ console.log(`\x1b[90mVersion ${runtimeInfo.version} Updated ${runtimeInfo.updatedAtLabel}\x1b[0m\n`);
183
203
 
184
204
  while (true) {
185
205
  const response = await prompts({
@@ -192,6 +212,7 @@ async function manageInteractive() {
192
212
  { title: '🔌 Allow inject (Run CLI with inject)', value: 'allow' },
193
213
  { title: '💬 Send message to a room (Inject command)', value: 'inject' },
194
214
  { title: '📋 View all open rooms (List sessions)', value: 'list' },
215
+ { title: '🧹 Repair local daemon', value: 'repair-daemon' },
195
216
  { title: '🧠 Install telepty skills', value: 'install-skills' },
196
217
  { title: '🔄 Update telepty to latest version', value: 'update' },
197
218
  { title: '❌ Exit', value: 'exit' }
@@ -203,7 +224,7 @@ async function manageInteractive() {
203
224
  try {
204
225
  execSync('npm install -g @dmsdc-ai/aigentry-telepty@latest', { stdio: 'inherit' });
205
226
  console.log('\n\x1b[32m✅ Update complete! Restarting daemon...\x1b[0m');
206
- cleanupDaemonProcesses();
227
+ await repairLocalDaemon({ restart: true });
207
228
  } catch (e) {
208
229
  console.error('\n❌ Update failed.\n');
209
230
  }
@@ -223,6 +244,17 @@ async function manageInteractive() {
223
244
  continue;
224
245
  }
225
246
 
247
+ if (response.action === 'repair-daemon') {
248
+ console.log('\n\x1b[36m🧹 Repairing local telepty daemon...\x1b[0m');
249
+ const result = await repairLocalDaemon({ restart: true });
250
+ if (result.meta) {
251
+ console.log(`✅ Local daemon is healthy. Version ${result.meta.version}, pid ${result.meta.pid}, stopped ${result.stopped} old daemon(s).\n`);
252
+ } else {
253
+ console.log(`⚠️ Daemon cleanup ran, but a fresh local daemon did not respond. Stopped ${result.stopped} old daemon(s).\n`);
254
+ }
255
+ continue;
256
+ }
257
+
226
258
  if (response.action === 'install-skills') {
227
259
  try {
228
260
  await runInteractiveSkillInstaller({ packageRoot: __dirname, cwd: process.cwd() });
@@ -518,7 +550,8 @@ async function main() {
518
550
  child.write(data.toString());
519
551
  },
520
552
  onResize: () => {
521
- child.resize(process.stdout.columns, process.stdout.rows);
553
+ const size = getTerminalSize(process.stdout, { cols: 120, rows: 40 });
554
+ child.resize(size.cols, size.rows);
522
555
  }
523
556
  });
524
557
 
@@ -594,10 +627,11 @@ async function main() {
594
627
  ws.send(JSON.stringify({ type: 'input', data: data.toString() }));
595
628
  },
596
629
  onResize: () => {
630
+ const size = getTerminalSize(process.stdout, { cols: 80, rows: 30 });
597
631
  ws.send(JSON.stringify({
598
632
  type: 'resize',
599
- cols: process.stdout.columns,
600
- rows: process.stdout.rows
633
+ cols: size.cols,
634
+ rows: size.rows
601
635
  }));
602
636
  }
603
637
  });
@@ -776,7 +810,6 @@ Usage:
776
810
  telepty multicast <id1,id2> "<prompt>" Inject text into multiple specific sessions
777
811
  telepty broadcast "<prompt>" Inject text into ALL active sessions
778
812
  telepty rename <old_id> <new_id> Rename a session (updates terminal title too)
779
- telepty cleanup-daemons Stop old local telepty daemon processes
780
813
  telepty listen Listen to the event bus and print JSON to stdout
781
814
  telepty monitor Human-readable real-time billboard of bus events
782
815
  telepty update Update telepty to the latest version
@@ -1,5 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ function getTerminalSize(output, fallback = {}) {
4
+ const envCols = Number.parseInt(process.env.COLUMNS || '', 10);
5
+ const envRows = Number.parseInt(process.env.LINES || '', 10);
6
+ const fallbackCols = Number.isInteger(fallback.cols) && fallback.cols > 0 ? fallback.cols : 120;
7
+ const fallbackRows = Number.isInteger(fallback.rows) && fallback.rows > 0 ? fallback.rows : 40;
8
+
9
+ const cols = Number.isInteger(output && output.columns) && output.columns > 0
10
+ ? output.columns
11
+ : (Number.isInteger(envCols) && envCols > 0 ? envCols : fallbackCols);
12
+ const rows = Number.isInteger(output && output.rows) && output.rows > 0
13
+ ? output.rows
14
+ : (Number.isInteger(envRows) && envRows > 0 ? envRows : fallbackRows);
15
+
16
+ return { cols, rows };
17
+ }
18
+
3
19
  function removeListener(stream, eventName, handler) {
4
20
  if (!handler || !stream) {
5
21
  return;
@@ -50,5 +66,6 @@ function attachInteractiveTerminal(input, output, handlers = {}) {
50
66
  }
51
67
 
52
68
  module.exports = {
53
- attachInteractiveTerminal
69
+ attachInteractiveTerminal,
70
+ getTerminalSize
54
71
  };
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
+ "aigentry-telepty": "install.js",
6
7
  "telepty": "cli.js",
7
8
  "telepty-install": "install.js"
8
9
  },
9
10
  "scripts": {
10
- "test": "node --test test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js",
11
- "test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js",
12
- "test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js"
11
+ "test": "node --test test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js",
12
+ "test:watch": "node --test --watch test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js",
13
+ "test:ci": "node --test --test-reporter=spec test/auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/cli.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js"
13
14
  },
14
15
  "keywords": [],
15
16
  "author": "",
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function pad(value) {
7
+ return String(value).padStart(2, '0');
8
+ }
9
+
10
+ function formatTimestamp(date) {
11
+ const year = date.getFullYear();
12
+ const month = pad(date.getMonth() + 1);
13
+ const day = pad(date.getDate());
14
+ const hours = pad(date.getHours());
15
+ const minutes = pad(date.getMinutes());
16
+ const seconds = pad(date.getSeconds());
17
+ const offsetMinutes = -date.getTimezoneOffset();
18
+ const sign = offsetMinutes >= 0 ? '+' : '-';
19
+ const absoluteOffset = Math.abs(offsetMinutes);
20
+ const offsetHours = pad(Math.floor(absoluteOffset / 60));
21
+ const offsetRemainder = pad(absoluteOffset % 60);
22
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${sign}${offsetHours}:${offsetRemainder}`;
23
+ }
24
+
25
+ function getRuntimeInfo(packageRoot = __dirname) {
26
+ const packageJsonPath = path.join(packageRoot, 'package.json');
27
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
28
+ const packageStat = fs.statSync(packageJsonPath);
29
+ const updatedAt = packageStat.mtime;
30
+
31
+ return {
32
+ version: pkg.version || 'unknown',
33
+ updatedAt,
34
+ updatedAtLabel: formatTimestamp(updatedAt)
35
+ };
36
+ }
37
+
38
+ module.exports = {
39
+ formatTimestamp,
40
+ getRuntimeInfo
41
+ };
@@ -1,86 +1,99 @@
1
+ ---
2
+ name: telepty
3
+ description: Use telepty to inspect sessions, attach or inject into rooms, repair local daemon issues, and guide users through the TUI-first workflow when telepty is installed.
4
+ ---
5
+
1
6
  # telepty
2
7
 
3
- Use `telepty` to inspect active sessions, check the current telepty session ID, attach to sessions, inject commands, listen to the event bus, rename sessions, and update the daemon.
8
+ Use this skill when the user wants help operating `telepty`, recovering a broken local daemon, or managing telepty sessions in natural language.
4
9
 
5
- ## When To Use
10
+ ## Default approach
6
11
 
7
- Use this skill when the user asks to:
8
- - Check whether the current shell is running inside a telepty session
9
- - List or inspect telepty sessions
10
- - Attach to a telepty session
11
- - Inject a prompt or command into another telepty session
12
- - Listen to telepty bus events or publish a JSON payload
13
- - Rename a session
14
- - Update telepty
12
+ - For interactive human guidance, prefer the `telepty` TUI and point the user to the relevant menu action.
13
+ - For agent execution inside a CLI session, run the underlying `telepty` command directly.
14
+ - When the request is about a broken or duplicated local daemon, repair the daemon before doing session work.
15
15
 
16
- ## Commands
16
+ ## Common actions
17
17
 
18
- 1. Check the current telepty session:
18
+ 1. Check whether the current shell is already inside a telepty session:
19
19
 
20
20
  ```bash
21
21
  echo "$TELEPTY_SESSION_ID"
22
22
  ```
23
23
 
24
- 2. List sessions:
24
+ 2. Inspect active sessions:
25
25
 
26
26
  ```bash
27
27
  telepty list
28
28
  ```
29
29
 
30
- 3. Attach to a session:
30
+ 3. Attach to a room:
31
31
 
32
32
  ```bash
33
33
  telepty attach <session_id>
34
34
  ```
35
35
 
36
- 4. Inject a prompt or command:
36
+ 4. Inject a command or prompt:
37
37
 
38
38
  ```bash
39
39
  telepty inject <session_id> "<prompt text>"
40
40
  ```
41
41
 
42
- 5. Inject into multiple sessions:
43
-
44
- ```bash
45
- telepty multicast <id1,id2,...> "<prompt text>"
46
- ```
47
-
48
- 6. Broadcast to all sessions:
42
+ 5. Allow inject on a local CLI:
49
43
 
50
44
  ```bash
51
- telepty broadcast "<prompt text>"
45
+ telepty allow --id <session_id> <command> [args...]
52
46
  ```
53
47
 
54
- 7. Rename a session:
48
+ 6. Rename a room:
55
49
 
56
50
  ```bash
57
51
  telepty rename <old_id> <new_id>
58
52
  ```
59
53
 
60
- 8. Listen to the event bus:
54
+ 7. Listen to the event bus:
61
55
 
62
56
  ```bash
63
57
  telepty listen
64
58
  ```
65
59
 
66
- 9. Publish a JSON payload to the bus:
60
+ 8. Update telepty:
67
61
 
68
62
  ```bash
69
- TOKEN=$(grep authToken ~/.telepty/config.json | cut -d '"' -f 4)
70
- curl -s -X POST http://127.0.0.1:3848/api/bus/publish \
71
- -H "Content-Type: application/json" \
72
- -H "x-telepty-token: $TOKEN" \
73
- -d '{"type":"bg_message","payload":"..."}'
63
+ telepty update
74
64
  ```
75
65
 
76
- 10. Update telepty:
66
+ ## Local daemon recovery
67
+
68
+ When the user reports any of these symptoms, repair the local daemon first:
69
+
70
+ - `Failed to connect to local daemon`
71
+ - local sessions do not appear but remote sessions do
72
+ - duplicate or stale daemon processes
73
+ - install/update completed but `spawn` or `allow` still fails locally
74
+
75
+ ### Human-facing path
76
+
77
+ Tell the user to run `telepty` and choose `Repair local daemon`.
78
+
79
+ ### Agent execution path
80
+
81
+ Use the maintenance command directly:
77
82
 
78
83
  ```bash
79
- telepty update
84
+ telepty cleanup-daemons
85
+ telepty daemon
80
86
  ```
81
87
 
88
+ If the daemon still does not come up, rerun the installer.
89
+
82
90
  ## Notes
83
91
 
84
92
  - `TELEPTY_SESSION_ID` is only set inside telepty-managed sessions.
85
- - Use `telepty inject` when the target session should receive the command immediately.
86
- - Use the JSON bus when the payload should be delivered without interrupting the target shell.
93
+ - For non-interactive `telepty allow` use cases, set terminal dimensions if the environment does not provide them:
94
+
95
+ ```bash
96
+ COLUMNS=120 LINES=40 telepty allow --id <session_id> <command>
97
+ ```
98
+
99
+ - For interactive users, keep explanations centered on TUI actions instead of raw maintenance commands whenever possible.