@dmsdc-ai/aigentry-telepty 0.5.7 → 0.5.9
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/CHANGELOG.md +23 -0
- package/daemon.js +22 -2
- package/install.js +231 -56
- package/package.json +4 -4
- package/session-state.js +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to `@dmsdc-ai/aigentry-telepty` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.9] - 2026-06-08
|
|
8
|
+
|
|
9
|
+
### Fixed — managed service install never started the daemon (#41)
|
|
10
|
+
|
|
11
|
+
- **The launchd/systemd/Windows service install generated an `env: node`
|
|
12
|
+
invocation that exited 127** under a minimal service-manager PATH (the daemon
|
|
13
|
+
never started when managed by launchd/systemd). **Fix:** `install.js` now uses
|
|
14
|
+
the absolute `process.execPath` + `cli.js` path for launchd/systemd/Windows
|
|
15
|
+
service generation, sets the daemon `PATH` via EnvironmentVariables, and adds
|
|
16
|
+
managed-instance live assertions so a managed daemon actually starts. Landed on
|
|
17
|
+
`main` at commit `7b2ab92`.
|
|
18
|
+
|
|
19
|
+
### Changed — CI test wiring
|
|
20
|
+
|
|
21
|
+
- Wired `test/install-service-generation.test.js` (the #41 regression test) into
|
|
22
|
+
the `test`, `test:ci`, and `test:watch` script file lists so CI's
|
|
23
|
+
`npm run test:ci` actually exercises the service-install generation.
|
|
24
|
+
|
|
25
|
+
### Docs
|
|
26
|
+
|
|
27
|
+
- Landed the #42 cross-machine relay/broker (hub) mode ADR and MVP
|
|
28
|
+
implementation spec.
|
|
29
|
+
|
|
7
30
|
## [0.5.2] - 2026-06-06
|
|
8
31
|
|
|
9
32
|
### Fixed — submit handshake confirmation (#507-B / #508)
|
package/daemon.js
CHANGED
|
@@ -1617,6 +1617,24 @@ function resolveSessionAlias(requestedId) {
|
|
|
1617
1617
|
return candidates[0];
|
|
1618
1618
|
}
|
|
1619
1619
|
|
|
1620
|
+
// #548 (alias-cascade shared-fate): resolveSessionAlias' most-recent-wins fuzzy match is correct for
|
|
1621
|
+
// READ/inject ("talk to the current `coder`"), but DESTRUCTIVE ops (DELETE / kill) must NEVER cascade
|
|
1622
|
+
// across distinct sids that merely share an alias. The incident: cleaning an already-gone `coder-532`
|
|
1623
|
+
// fuzzy-fell-through to its live sibling `coder-533` (same `coder` track) and KILLED it. Distinct sids
|
|
1624
|
+
// = distinct lifecycles. This resolver enforces "destroy exactly the session you named":
|
|
1625
|
+
// - exact sid match → that sid;
|
|
1626
|
+
// - a fully-qualified sid (ends in `-<digits>`) with no exact match → null (a stale/duplicate DELETE
|
|
1627
|
+
// of a gone sid must NOT fall through to a sibling — this is the exact #548 cascade);
|
|
1628
|
+
// - a bare alias → resolve ONLY when a single session carries it (unambiguous); multiple siblings → null
|
|
1629
|
+
// (refuse rather than silently pick most-recent and kill the wrong one).
|
|
1630
|
+
function resolveSessionForDestroy(requestedId) {
|
|
1631
|
+
if (sessions[requestedId]) return requestedId;
|
|
1632
|
+
if (/-\d+$/.test(requestedId)) return null;
|
|
1633
|
+
const baseAlias = requestedId.replace(/-\d+$/, '');
|
|
1634
|
+
const candidates = Object.keys(sessions).filter(id => id.replace(/-\d+$/, '') === baseAlias);
|
|
1635
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1620
1638
|
app.post('/api/sessions/spawn', (req, res) => {
|
|
1621
1639
|
const { session_id, command, args = [], cwd = process.cwd(), cols = 80, rows = 30, type = 'AGENT' } = req.body;
|
|
1622
1640
|
if (!session_id) return res.status(400).json({ error: 'session_id is strictly required.' });
|
|
@@ -2823,7 +2841,8 @@ app.patch('/api/sessions/:id', (req, res) => {
|
|
|
2823
2841
|
|
|
2824
2842
|
app.post('/api/sessions/:id/kill', async (req, res) => {
|
|
2825
2843
|
const requestedId = req.params.id;
|
|
2826
|
-
|
|
2844
|
+
// #548: destructive op — must not cascade across alias-sharing siblings.
|
|
2845
|
+
const resolvedId = resolveSessionForDestroy(requestedId);
|
|
2827
2846
|
if (!resolvedId) return res.status(404).json({ error: 'Session not found', requested: requestedId });
|
|
2828
2847
|
|
|
2829
2848
|
try {
|
|
@@ -2852,7 +2871,8 @@ app.post('/api/sessions/:id/kill', async (req, res) => {
|
|
|
2852
2871
|
|
|
2853
2872
|
app.delete('/api/sessions/:id', (req, res) => {
|
|
2854
2873
|
const requestedId = req.params.id;
|
|
2855
|
-
|
|
2874
|
+
// #548: destructive op — must not cascade across alias-sharing siblings.
|
|
2875
|
+
const resolvedId = resolveSessionForDestroy(requestedId);
|
|
2856
2876
|
if (!resolvedId) return res.status(404).json({ error: 'Session not found', requested: requestedId });
|
|
2857
2877
|
const session = sessions[resolvedId];
|
|
2858
2878
|
const id = resolvedId;
|
package/install.js
CHANGED
|
@@ -5,9 +5,6 @@ const os = require('os');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { cleanupDaemonProcesses } = require('./daemon-control');
|
|
8
|
-
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
9
|
-
|
|
10
|
-
console.log("🚀 Installing @dmsdc-ai/aigentry-telepty...");
|
|
11
8
|
|
|
12
9
|
function run(cmd) {
|
|
13
10
|
try {
|
|
@@ -18,6 +15,10 @@ function run(cmd) {
|
|
|
18
15
|
}
|
|
19
16
|
}
|
|
20
17
|
|
|
18
|
+
function shellQuote(value) {
|
|
19
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
20
|
+
}
|
|
21
|
+
|
|
21
22
|
function resolveInstalledPackageRoot() {
|
|
22
23
|
try {
|
|
23
24
|
const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
@@ -27,6 +28,15 @@ function resolveInstalledPackageRoot() {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
function resolveDaemonLaunchOptions(options = {}) {
|
|
32
|
+
const packageRoot = options.packageRoot || __dirname;
|
|
33
|
+
const nodeBin = options.nodeBin || process.execPath;
|
|
34
|
+
const cliJs = options.cliJs || path.join(packageRoot, 'cli.js');
|
|
35
|
+
const logDir = options.logDir || path.join(os.homedir(), '.telepty', 'logs');
|
|
36
|
+
|
|
37
|
+
return { nodeBin, cliJs, logDir };
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
function cleanupLocalDaemons() {
|
|
31
41
|
console.log('🧹 Cleaning up existing telepty daemons...');
|
|
32
42
|
const results = cleanupDaemonProcesses();
|
|
@@ -36,6 +46,161 @@ function cleanupLocalDaemons() {
|
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
function escapeXml(value) {
|
|
50
|
+
return String(value)
|
|
51
|
+
.replace(/&/g, '&')
|
|
52
|
+
.replace(/</g, '<')
|
|
53
|
+
.replace(/>/g, '>')
|
|
54
|
+
.replace(/"/g, '"')
|
|
55
|
+
.replace(/'/g, ''');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function uniquePathEntries(entries) {
|
|
59
|
+
const seen = new Set();
|
|
60
|
+
const result = [];
|
|
61
|
+
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
if (!entry || seen.has(entry)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
seen.add(entry);
|
|
67
|
+
result.push(entry);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildDaemonPath(nodeBin, baseEntries) {
|
|
74
|
+
return uniquePathEntries([path.dirname(nodeBin), ...baseEntries]).join(':');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function systemdExecArg(value) {
|
|
78
|
+
const text = String(value);
|
|
79
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(text)) {
|
|
80
|
+
return text;
|
|
81
|
+
}
|
|
82
|
+
return `"${text.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function quoteWindowsArg(value) {
|
|
86
|
+
return `"${String(value).replace(/"/g, '\\"')}"`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function buildLaunchdPlist(options = {}) {
|
|
90
|
+
const label = options.label || 'com.aigentry.telepty';
|
|
91
|
+
const nodeBin = options.nodeBin || process.execPath;
|
|
92
|
+
const cliJs = options.cliJs || path.join(__dirname, 'cli.js');
|
|
93
|
+
const logDir = options.logDir || path.join(os.homedir(), '.telepty', 'logs');
|
|
94
|
+
const daemonPath = buildDaemonPath(nodeBin, ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin']);
|
|
95
|
+
const stdoutPath = path.join(logDir, 'launchd.out.log');
|
|
96
|
+
const stderrPath = path.join(logDir, 'launchd.err.log');
|
|
97
|
+
|
|
98
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
99
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
100
|
+
<plist version="1.0">
|
|
101
|
+
<dict>
|
|
102
|
+
<key>Label</key>
|
|
103
|
+
<string>${escapeXml(label)}</string>
|
|
104
|
+
<key>ProgramArguments</key>
|
|
105
|
+
<array>
|
|
106
|
+
<string>${escapeXml(nodeBin)}</string>
|
|
107
|
+
<string>${escapeXml(cliJs)}</string>
|
|
108
|
+
<string>daemon</string>
|
|
109
|
+
</array>
|
|
110
|
+
<key>EnvironmentVariables</key>
|
|
111
|
+
<dict>
|
|
112
|
+
<key>PATH</key>
|
|
113
|
+
<string>${escapeXml(daemonPath)}</string>
|
|
114
|
+
</dict>
|
|
115
|
+
<key>StandardOutPath</key>
|
|
116
|
+
<string>${escapeXml(stdoutPath)}</string>
|
|
117
|
+
<key>StandardErrorPath</key>
|
|
118
|
+
<string>${escapeXml(stderrPath)}</string>
|
|
119
|
+
<key>RunAtLoad</key>
|
|
120
|
+
<true/>
|
|
121
|
+
<key>KeepAlive</key>
|
|
122
|
+
<true/>
|
|
123
|
+
</dict>
|
|
124
|
+
</plist>`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildSystemdService(options = {}) {
|
|
128
|
+
const nodeBin = options.nodeBin || process.execPath;
|
|
129
|
+
const cliJs = options.cliJs || path.join(__dirname, 'cli.js');
|
|
130
|
+
const user = options.user;
|
|
131
|
+
const userLine = user ? `User=${user}\n` : '';
|
|
132
|
+
const daemonPath = buildDaemonPath(nodeBin, ['/usr/local/bin', '/usr/bin', '/bin']);
|
|
133
|
+
const wantedBy = options.wantedBy || 'multi-user.target';
|
|
134
|
+
|
|
135
|
+
return `[Unit]
|
|
136
|
+
Description=Telepty Daemon
|
|
137
|
+
After=network.target
|
|
138
|
+
|
|
139
|
+
[Service]
|
|
140
|
+
ExecStart=${systemdExecArg(nodeBin)} ${systemdExecArg(cliJs)} daemon
|
|
141
|
+
Restart=always
|
|
142
|
+
${userLine}Environment=PATH=${daemonPath}
|
|
143
|
+
Environment=NODE_ENV=production
|
|
144
|
+
|
|
145
|
+
[Install]
|
|
146
|
+
WantedBy=${wantedBy}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function buildWindowsAutostartCommand(options = {}) {
|
|
150
|
+
const nodeBin = options.nodeBin || process.execPath;
|
|
151
|
+
const cliJs = options.cliJs || path.join(__dirname, 'cli.js');
|
|
152
|
+
const taskName = options.taskName || 'telepty-daemon';
|
|
153
|
+
const taskCommand = `${quoteWindowsArg(nodeBin)} ${quoteWindowsArg(cliJs)} daemon`;
|
|
154
|
+
|
|
155
|
+
return `schtasks /create /tn ${quoteWindowsArg(taskName)} /sc onlogon /rl LIMITED /f /tr ${quoteWindowsArg(taskCommand)}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function buildWindowsRunTaskCommand(options = {}) {
|
|
159
|
+
const taskName = options.taskName || 'telepty-daemon';
|
|
160
|
+
return `schtasks /run /tn ${quoteWindowsArg(taskName)}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function buildWindowsQueryTaskCommand(options = {}) {
|
|
164
|
+
const taskName = options.taskName || 'telepty-daemon';
|
|
165
|
+
return `schtasks /query /tn ${quoteWindowsArg(taskName)} /fo LIST`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function assertLaunchdServiceLive(label = 'com.aigentry.telepty') {
|
|
169
|
+
let output = '';
|
|
170
|
+
try {
|
|
171
|
+
output = execSync(`launchctl list ${shellQuote(label)}`, { encoding: 'utf8' });
|
|
172
|
+
} catch (e) {
|
|
173
|
+
throw new Error(`launchd service ${label} was not found after load`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const pidMatch = output.match(/"PID"\s*=\s*([0-9]+)/);
|
|
177
|
+
if (!pidMatch || Number(pidMatch[1]) <= 0) {
|
|
178
|
+
throw new Error(`launchd service ${label} loaded but has no live PID. launchctl output:\n${output}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function assertSystemdServiceLive(serviceName = 'telepty', options = {}) {
|
|
183
|
+
const scope = options.user ? '--user ' : '';
|
|
184
|
+
try {
|
|
185
|
+
execSync(`systemctl ${scope}is-active --quiet ${serviceName}`, { stdio: 'ignore' });
|
|
186
|
+
} catch (e) {
|
|
187
|
+
throw new Error(`systemd service ${serviceName} is not active after start`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function assertWindowsTaskRunning(taskName = 'telepty-daemon') {
|
|
192
|
+
let output = '';
|
|
193
|
+
try {
|
|
194
|
+
output = execSync(buildWindowsQueryTaskCommand({ taskName }), { encoding: 'utf8' });
|
|
195
|
+
} catch (e) {
|
|
196
|
+
throw new Error(`Windows scheduled task ${taskName} was not found after creation`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!/^Status:\s*Running$/im.test(output)) {
|
|
200
|
+
throw new Error(`Windows scheduled task ${taskName} started but is not running. schtasks output:\n${output}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
39
204
|
async function installSkills() {
|
|
40
205
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
41
206
|
console.log('⏭️ Skipping interactive skill installation (no TTY).');
|
|
@@ -46,6 +211,7 @@ async function installSkills() {
|
|
|
46
211
|
console.log('\n📋 Telepty skill installation');
|
|
47
212
|
|
|
48
213
|
try {
|
|
214
|
+
const { runInteractiveSkillInstaller } = require('./skill-installer');
|
|
49
215
|
await runInteractiveSkillInstaller({
|
|
50
216
|
packageRoot: resolveInstalledPackageRoot(),
|
|
51
217
|
cwd: process.cwd()
|
|
@@ -55,7 +221,9 @@ async function installSkills() {
|
|
|
55
221
|
}
|
|
56
222
|
}
|
|
57
223
|
|
|
58
|
-
|
|
224
|
+
async function main() {
|
|
225
|
+
console.log("🚀 Installing @dmsdc-ai/aigentry-telepty...");
|
|
226
|
+
|
|
59
227
|
// 1. Install globally via npm
|
|
60
228
|
console.log("📦 Installing package globally...");
|
|
61
229
|
run("npm install -g @dmsdc-ai/aigentry-telepty");
|
|
@@ -63,12 +231,10 @@ async function installSkills() {
|
|
|
63
231
|
// 2. Install telepty skills for supported clients
|
|
64
232
|
await installSkills();
|
|
65
233
|
|
|
66
|
-
// 3.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
} catch (e) {
|
|
71
|
-
teleptyPath = 'telepty'; // fallback
|
|
234
|
+
// 3. Resolve daemon entrypoint without relying on service-manager PATH.
|
|
235
|
+
const launchOptions = resolveDaemonLaunchOptions({ packageRoot: resolveInstalledPackageRoot() });
|
|
236
|
+
if (!fs.existsSync(launchOptions.cliJs)) {
|
|
237
|
+
throw new Error(`Cannot find daemon entrypoint: ${launchOptions.cliJs}`);
|
|
72
238
|
}
|
|
73
239
|
|
|
74
240
|
// 4. Setup OS-specific autostart or background daemon
|
|
@@ -76,86 +242,95 @@ async function installSkills() {
|
|
|
76
242
|
|
|
77
243
|
if (platform === 'win32') {
|
|
78
244
|
cleanupLocalDaemons();
|
|
79
|
-
console.log("⚙️ Setting up Windows
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
subprocess.unref();
|
|
86
|
-
console.log("✅ Windows daemon started in background.");
|
|
245
|
+
console.log("⚙️ Setting up Windows scheduled task...");
|
|
246
|
+
run(buildWindowsAutostartCommand(launchOptions));
|
|
247
|
+
run(buildWindowsRunTaskCommand());
|
|
248
|
+
assertWindowsTaskRunning();
|
|
249
|
+
console.log("✅ Windows scheduled task installed and started.");
|
|
87
250
|
|
|
88
251
|
} else if (platform === 'darwin') {
|
|
89
252
|
console.log("⚙️ Setting up macOS launchd service...");
|
|
90
253
|
const plistPath = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.aigentry.telepty.plist');
|
|
91
254
|
fs.mkdirSync(path.dirname(plistPath), { recursive: true });
|
|
92
|
-
|
|
255
|
+
fs.mkdirSync(launchOptions.logDir, { recursive: true });
|
|
256
|
+
try { execSync(`launchctl unload ${shellQuote(plistPath)} 2>/dev/null`); } catch(e){}
|
|
93
257
|
cleanupLocalDaemons();
|
|
94
258
|
|
|
95
|
-
const plistContent =
|
|
96
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
97
|
-
<plist version="1.0">
|
|
98
|
-
<dict>
|
|
99
|
-
<key>Label</key>
|
|
100
|
-
<string>com.aigentry.telepty</string>
|
|
101
|
-
<key>ProgramArguments</key>
|
|
102
|
-
<array>
|
|
103
|
-
<string>${teleptyPath}</string>
|
|
104
|
-
<string>daemon</string>
|
|
105
|
-
</array>
|
|
106
|
-
<key>RunAtLoad</key>
|
|
107
|
-
<true/>
|
|
108
|
-
<key>KeepAlive</key>
|
|
109
|
-
<true/>
|
|
110
|
-
</dict>
|
|
111
|
-
</plist>`;
|
|
259
|
+
const plistContent = buildLaunchdPlist(launchOptions);
|
|
112
260
|
|
|
113
261
|
fs.writeFileSync(plistPath, plistContent);
|
|
114
|
-
run(`launchctl load
|
|
262
|
+
run(`launchctl load ${shellQuote(plistPath)}`);
|
|
263
|
+
assertLaunchdServiceLive();
|
|
115
264
|
console.log("✅ macOS LaunchAgent installed and started.");
|
|
116
265
|
|
|
117
266
|
} else {
|
|
118
267
|
// Linux
|
|
268
|
+
let hasSystemd = false;
|
|
119
269
|
try {
|
|
120
270
|
execSync('systemctl --version', { stdio: 'ignore' });
|
|
271
|
+
hasSystemd = true;
|
|
272
|
+
} catch(e) {}
|
|
273
|
+
|
|
274
|
+
if (hasSystemd) {
|
|
121
275
|
if (process.getuid && process.getuid() === 0) {
|
|
122
276
|
console.log("⚙️ Setting up systemd service for Linux...");
|
|
123
277
|
try { execSync('systemctl stop telepty', { stdio: 'ignore' }); } catch(e) {}
|
|
124
278
|
cleanupLocalDaemons();
|
|
125
|
-
const serviceContent =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
[Service]
|
|
130
|
-
ExecStart=${teleptyPath} daemon
|
|
131
|
-
Restart=always
|
|
132
|
-
User=${process.env.SUDO_USER || process.env.USER || 'root'}
|
|
133
|
-
Environment=PATH=/usr/bin:/usr/local/bin:$PATH
|
|
134
|
-
Environment=NODE_ENV=production
|
|
135
|
-
|
|
136
|
-
[Install]
|
|
137
|
-
WantedBy=multi-user.target`;
|
|
279
|
+
const serviceContent = buildSystemdService({
|
|
280
|
+
...launchOptions,
|
|
281
|
+
user: process.env.SUDO_USER || process.env.USER || 'root'
|
|
282
|
+
});
|
|
138
283
|
|
|
139
284
|
fs.writeFileSync('/etc/systemd/system/telepty.service', serviceContent);
|
|
140
285
|
run('systemctl daemon-reload');
|
|
141
286
|
run('systemctl enable telepty');
|
|
142
287
|
run('systemctl start telepty');
|
|
288
|
+
assertSystemdServiceLive();
|
|
143
289
|
console.log("✅ Systemd service installed and started.");
|
|
144
290
|
process.exit(0);
|
|
145
291
|
}
|
|
146
|
-
} catch(e) {}
|
|
147
292
|
|
|
148
|
-
|
|
149
|
-
|
|
293
|
+
console.log("⚙️ Setting up user systemd service for Linux...");
|
|
294
|
+
const userServicePath = path.join(os.homedir(), '.config', 'systemd', 'user', 'telepty.service');
|
|
295
|
+
fs.mkdirSync(path.dirname(userServicePath), { recursive: true });
|
|
296
|
+
cleanupLocalDaemons();
|
|
297
|
+
fs.writeFileSync(userServicePath, buildSystemdService({
|
|
298
|
+
...launchOptions,
|
|
299
|
+
wantedBy: 'default.target'
|
|
300
|
+
}));
|
|
301
|
+
run('systemctl --user daemon-reload');
|
|
302
|
+
run('systemctl --user enable telepty');
|
|
303
|
+
run('systemctl --user start telepty');
|
|
304
|
+
assertSystemdServiceLive('telepty', { user: true });
|
|
305
|
+
console.log("✅ User systemd service installed and started.");
|
|
306
|
+
process.exit(0);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Fallback for Linux without systemd
|
|
310
|
+
console.log("⚠️ Skipping persistent systemd setup. Starting daemon for this session only...");
|
|
150
311
|
cleanupLocalDaemons();
|
|
151
|
-
const subprocess = spawn(
|
|
312
|
+
const subprocess = spawn(launchOptions.nodeBin, [launchOptions.cliJs, 'daemon'], {
|
|
152
313
|
detached: true,
|
|
153
314
|
stdio: 'ignore'
|
|
154
315
|
});
|
|
155
316
|
subprocess.unref();
|
|
156
|
-
console.log("✅ Linux daemon started in background
|
|
317
|
+
console.log("✅ Linux daemon started in background for the current session.");
|
|
157
318
|
}
|
|
158
319
|
|
|
159
320
|
console.log("\n🎉 Installation complete! Telepty daemon is running.");
|
|
160
321
|
console.log("👉 Try running: telepty attach\n");
|
|
161
|
-
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (require.main === module) {
|
|
325
|
+
main().catch((e) => {
|
|
326
|
+
console.error('❌ Installation failed:', e.message);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
module.exports = {
|
|
332
|
+
buildLaunchdPlist,
|
|
333
|
+
buildSystemdService,
|
|
334
|
+
buildWindowsAutostartCommand,
|
|
335
|
+
resolveDaemonLaunchOptions,
|
|
336
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dmsdc-ai/aigentry-telepty",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"main": "daemon.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aigentry-telepty": "install.js",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"postinstall": "node scripts/postinstall.js",
|
|
38
|
-
"test": "node --test test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js && git diff --exit-code tests/snippet-protocol/v1/",
|
|
39
|
-
"test:watch": "node --test --watch test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js",
|
|
40
|
-
"test:ci": "node --test --test-reporter=spec test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js && git diff --exit-code tests/snippet-protocol/v1/",
|
|
38
|
+
"test": "node --require ./test-support/setup-env.js --test test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/install-service-generation.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js && git diff --exit-code tests/snippet-protocol/v1/",
|
|
39
|
+
"test:watch": "node --require ./test-support/setup-env.js --test --watch test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/install-service-generation.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js",
|
|
40
|
+
"test:ci": "node --require ./test-support/setup-env.js --test --test-reporter=spec test/auth.test.js test/http-auth.test.js test/daemon.test.js test/daemon-singleton.test.js test/integration/daemon-launch.test.js test/cli.test.js test/telepty-kill.test.js test/idle-ttl.test.js test/telepty-clean-older-than.test.js test/lifecycle-transport-agnostic.test.js test/skill-installer.test.js test/interactive-terminal.test.js test/runtime-info.test.js test/session-routing.test.js test/session-state.test.js test/session-store-persistence.test.js test/mailbox-lock.test.js test/report-enforcement.test.js test/enforce-report.test.js test/peer-inject-validator.test.js test/enforce-submit-gate.test.js test/submit-gate.test.js test/submit-via-pty.test.js test/prompt-symbol-registry.test.js test/inject-submit-flags.test.js test/inject-submit-force-env.test.js test/host-spec.test.js test/cross-host-inject.test.js test/cross-machine-ssh-routing.test.js test/init.test.js test/install-service-generation.test.js test/win-resolve-executable.test.js test/version-handshake.test.js test/win-kill-process.test.js test/daemon-control-port-owner.test.js test/banner-stderr-jq-safety.test.js test/bridge-supervisor-ipc.test.js test/bridge-j3-shim.test.js test/bridge-e2e.test.js test/release-0.4.5-bugfixes.test.js && git diff --exit-code tests/snippet-protocol/v1/",
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"regen-fixtures": "node scripts/regen-snippet-fixtures.js"
|
|
43
43
|
},
|
package/session-state.js
CHANGED
|
@@ -96,6 +96,14 @@ const THINKING_PATTERNS = [
|
|
|
96
96
|
/\breading\b/i, // Claude Code "Reading..."
|
|
97
97
|
/\bsearching\b/i, // Claude Code "Searching..."
|
|
98
98
|
/\bplanning\b/i, // Claude Code "Planning..."
|
|
99
|
+
// codex CLI active-work markers (#558): codex emits NO braille spinner and NO OSC 133, so its
|
|
100
|
+
// "busy" state went unrecognized → blank sidebar pill. These are high-signal codex/claude markers
|
|
101
|
+
// shown ONLY while actively generating/running tools. ("Working" is intentionally NOT matched —
|
|
102
|
+
// it false-positives on common dev output like "working tree" / "working directory".)
|
|
103
|
+
/\besc to interrupt\b/i, // codex + claude: shown only during active generation / tool run
|
|
104
|
+
/\bstarting mcp servers?\b/i,// codex: MCP bootstrap on launch
|
|
105
|
+
/\bbooting mcp server\b/i, // codex: MCP server boot
|
|
106
|
+
/\bexploring\b/i, // codex activity verb (parallels Claude Code "Searching")
|
|
99
107
|
/\.{3,}\s*$/, // trailing dots "..."
|
|
100
108
|
];
|
|
101
109
|
|