@dmsdc-ai/aigentry-telepty 0.1.6 → 0.1.7

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.
@@ -1,55 +0,0 @@
1
- # telepty
2
-
3
- **Description:**
4
- Help the user interact with the `telepty` daemon, check their current session ID, list active sessions, and inject commands or JSON events into remote or local PTY sessions. All operations are performed using standard CLI commands or `curl`.
5
-
6
- **Trigger:**
7
- When the user asks about their current session ID, wants to check active sessions, wants to inject a prompt/command into a specific session, wants to send a JSON event via the bus, wants to subscribe/listen to the bus, or wants to update telepty.
8
-
9
- **Instructions:**
10
- 1. **To check the current session ID:**
11
- - Execute `run_shell_command` with `echo $TELEPTY_SESSION_ID`.
12
- - If the value is empty, inform the user that the current shell is *not* running inside a telepty spawned session.
13
- - If it has a value, output it clearly.
14
- 2. **To list all sessions:**
15
- - Run `telepty list`.
16
- 3. **To send a message/command to another agent, you must choose ONE of three methods depending on the user's intent:**
17
-
18
- **Method A: Prompt Injection (Active Interruption)**
19
- - Use this when you want the receiving AI to IMMEDIATELY read and execute the message as a prompt.
20
- - Run: `telepty inject <target_session_id> "<prompt text>"`
21
- - (For multiple: `telepty multicast <id1>,<id2> "<prompt>"`)
22
-
23
- **Method B: Log Injection (Visual Notification)**
24
- - Use this when you want the message to appear immediately on the receiving terminal's screen for the user to see, but WITHOUT forcing the AI to execute it as a prompt.
25
- - Run: `telepty inject <target_session_id> "echo '\x1b[33m[📬 Message from $TELEPTY_SESSION_ID]\x1b[0m <message text>'"`
26
-
27
- **Method C: Background JSON Bus (Passive/Silent)**
28
- - Use this for structured data transfer that the other AI will read later from its log file, without disturbing its current terminal screen.
29
- - Run:
30
- ```bash
31
- TOKEN=$(cat ~/.telepty/config.json | grep authToken | cut -d '"' -f 4)
32
- curl -s -X POST http://127.0.0.1:3848/api/bus/publish -H "Content-Type: application/json" -H "x-telepty-token: $TOKEN" -d '{"type": "bg_message", "payload": "..."}'
33
- ```
34
- 4. **To subscribe to the Event Bus (Listen for JSON events):**
35
- - Run `nohup telepty listen > .telepty_bus_events.log 2>&1 &`
36
- 5. **To physically OPEN a new Terminal Window for the user (macOS):**
37
- - If the user asks you to "open a new telepty terminal" or "방 파줘", you can physically spawn a new Ghostty/Terminal window on their screen that is already attached to a telepty session.
38
- - Run this shell command (replace `<ID>` and `<CMD>`):
39
- ```bash
40
- cat << 'EOF' > /tmp/telepty-auto.command
41
- #!/bin/bash
42
- telepty spawn --id <ID> <CMD>
43
- EOF
44
- chmod +x /tmp/telepty-auto.command
45
- open -a Ghostty /tmp/telepty-auto.command || open /tmp/telepty-auto.command
46
- ```
47
- 6. **To rename a session:**
48
- - Run `telepty rename <old_id> <new_id>`
49
- - This updates the session key, Ghostty tab title, and broadcasts a `session_rename` event on the bus.
50
- 7. **Terminal Title Convention:**
51
- - Each telepty session displays its ID in the Ghostty tab title.
52
- - Local: `⚡ telepty :: {session_id}`
53
- - Remote: `⚡ telepty :: {session_id} @ {host}`
54
- 8. **To update telepty:**
55
- - Run `telepty update`.
@@ -1,26 +0,0 @@
1
- name: Regression Tests
2
- on:
3
- push:
4
- branches: [ "main" ]
5
- pull_request:
6
- branches: [ "main" ]
7
- workflow_dispatch:
8
-
9
- jobs:
10
- test:
11
- strategy:
12
- fail-fast: false
13
- matrix:
14
- os: [ubuntu-latest, macos-latest, windows-latest]
15
- node: [20]
16
- runs-on: ${{ matrix.os }}
17
- steps:
18
- - uses: actions/checkout@v4
19
- - uses: actions/setup-node@v4
20
- with:
21
- node-version: ${{ matrix.node }}
22
- cache: npm
23
- - name: Install dependencies
24
- run: npm ci
25
- - name: Run regression suite
26
- run: npm run test:ci
Binary file
Binary file
package/test/auth.test.js DELETED
@@ -1,56 +0,0 @@
1
- 'use strict';
2
-
3
- const { test, before, after } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const fs = require('fs');
6
- const os = require('os');
7
- const path = require('path');
8
-
9
- // Use a temp directory to isolate tests from the real ~/.telepty config.
10
- // We patch os.homedir() before requiring auth.js so the module-level
11
- // constants (CONFIG_DIR, CONFIG_FILE) resolve to the temp dir.
12
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'telepty-test-'));
13
- const originalHomedir = os.homedir.bind(os);
14
-
15
- before(() => {
16
- os.homedir = () => tmpDir;
17
- // Ensure auth.js is loaded fresh with the patched homedir
18
- delete require.cache[require.resolve('../auth.js')];
19
- });
20
-
21
- after(() => {
22
- os.homedir = originalHomedir;
23
- delete require.cache[require.resolve('../auth.js')];
24
- fs.rmSync(tmpDir, { recursive: true, force: true });
25
- });
26
-
27
- test('getConfig() returns an object with authToken property', () => {
28
- const { getConfig } = require('../auth.js');
29
- const config = getConfig();
30
- assert.ok(config !== null && typeof config === 'object', 'config should be an object');
31
- assert.ok('authToken' in config, 'config should have authToken property');
32
- assert.equal(typeof config.authToken, 'string', 'authToken should be a string');
33
- });
34
-
35
- test('authToken is a valid UUID v4 format', () => {
36
- const { getConfig } = require('../auth.js');
37
- const config = getConfig();
38
- const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
39
- assert.match(config.authToken, uuidV4Regex, 'authToken should be a valid UUID v4');
40
- });
41
-
42
- test('config object has createdAt field', () => {
43
- const { getConfig } = require('../auth.js');
44
- const config = getConfig();
45
- assert.ok('createdAt' in config, 'config should have createdAt property');
46
- const date = new Date(config.createdAt);
47
- assert.ok(!isNaN(date.getTime()), 'createdAt should be a valid ISO date string');
48
- });
49
-
50
- test('calling getConfig() twice returns the same token (persistence)', () => {
51
- const { getConfig } = require('../auth.js');
52
- const config1 = getConfig();
53
- const config2 = getConfig();
54
- assert.equal(config1.authToken, config2.authToken, 'authToken should be identical across calls');
55
- assert.equal(config1.createdAt, config2.createdAt, 'createdAt should be identical across calls');
56
- });
package/test/cli.test.js DELETED
@@ -1,57 +0,0 @@
1
- 'use strict';
2
-
3
- const { afterEach, beforeEach, test } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const { createSessionId, startTestDaemon, stripAnsi, waitFor } = require('../test-support/daemon-harness');
6
-
7
- let harness;
8
-
9
- function collectJsonMessages(ws) {
10
- const messages = [];
11
- ws.on('message', (chunk) => {
12
- try {
13
- messages.push(JSON.parse(chunk.toString()));
14
- } catch {
15
- // Ignore malformed payloads in tests.
16
- }
17
- });
18
- return messages;
19
- }
20
-
21
- beforeEach(async () => {
22
- harness = await startTestDaemon();
23
- });
24
-
25
- afterEach(async () => {
26
- await harness.stop();
27
- });
28
-
29
- test('telepty list prints active sessions from the configured host and port', async () => {
30
- const sessionId = createSessionId('cli-list');
31
- await harness.spawnSession(sessionId);
32
-
33
- const result = await harness.runCli(['list']);
34
- assert.equal(result.code, 0, result.stderr);
35
-
36
- const output = stripAnsi(`${result.stdout}\n${result.stderr}`);
37
- assert.match(output, new RegExp(sessionId));
38
- assert.match(output, /Active Sessions/i);
39
- });
40
-
41
- test('telepty inject forwards input to the target PTY session', async () => {
42
- const sessionId = createSessionId('cli-inject');
43
- await harness.spawnSession(sessionId);
44
-
45
- const ws = await harness.connectSession(sessionId);
46
- const outputs = collectJsonMessages(ws);
47
- const token = createSessionId('cli-token');
48
-
49
- const result = await harness.runCli(['inject', sessionId, `echo ${token}`]);
50
- assert.equal(result.code, 0, result.stderr);
51
-
52
- await waitFor(() => outputs.some((message) => (
53
- message.type === 'output' && String(message.data).includes(token)
54
- )), { timeoutMs: 7000, description: 'CLI inject output' });
55
-
56
- ws.close();
57
- });
@@ -1,415 +0,0 @@
1
- 'use strict';
2
-
3
- const { afterEach, beforeEach, test } = require('node:test');
4
- const assert = require('node:assert/strict');
5
- const { createSessionId, delay, startTestDaemon, waitFor } = require('../test-support/daemon-harness');
6
-
7
- let harness;
8
-
9
- function collectJsonMessages(ws) {
10
- const messages = [];
11
- ws.on('message', (chunk) => {
12
- try {
13
- messages.push(JSON.parse(chunk.toString()));
14
- } catch {
15
- // Ignore malformed payloads in tests.
16
- }
17
- });
18
- return messages;
19
- }
20
-
21
- beforeEach(async () => {
22
- harness = await startTestDaemon();
23
- });
24
-
25
- afterEach(async () => {
26
- await harness.stop();
27
- });
28
-
29
- test('GET /api/sessions returns an empty array on a fresh daemon', async () => {
30
- const result = await harness.request('/api/sessions');
31
- assert.equal(result.status, 200);
32
- assert.deepEqual(result.body, []);
33
- });
34
-
35
- test('spawned sessions appear in the list and duplicate IDs are rejected', async () => {
36
- const sessionId = createSessionId('spawn');
37
- const first = await harness.spawnSession(sessionId);
38
- assert.equal(first.status, 201);
39
- assert.equal(first.body.session_id, sessionId);
40
-
41
- const duplicate = await harness.spawnSession(sessionId);
42
- assert.equal(duplicate.status, 409);
43
- assert.match(duplicate.body.error, /already active/i);
44
-
45
- const list = await harness.request('/api/sessions');
46
- assert.equal(list.status, 200);
47
- assert.equal(list.body.length, 1);
48
- assert.equal(list.body[0].id, sessionId);
49
- assert.equal(list.body[0].active_clients, 0);
50
- });
51
-
52
- test('PATCH /api/sessions/:id renames the session and publishes a bus event', async () => {
53
- const originalId = createSessionId('rename');
54
- const newId = `${originalId}-renamed`;
55
- await harness.spawnSession(originalId);
56
-
57
- const bus = await harness.connectBus();
58
- const messages = collectJsonMessages(bus);
59
-
60
- const rename = await harness.request(`/api/sessions/${encodeURIComponent(originalId)}`, {
61
- method: 'PATCH',
62
- body: { new_id: newId }
63
- });
64
-
65
- assert.equal(rename.status, 200);
66
- assert.equal(rename.body.new_id, newId);
67
-
68
- await waitFor(() => messages.find((message) => (
69
- message.type === 'session_rename' &&
70
- message.old_id === originalId &&
71
- message.new_id === newId
72
- )), { description: 'rename bus event' });
73
-
74
- const list = await harness.request('/api/sessions');
75
- assert.equal(list.status, 200);
76
- assert.equal(list.body.length, 1);
77
- assert.equal(list.body[0].id, newId);
78
-
79
- bus.close();
80
- });
81
-
82
- test('inject and multicast endpoints report success and partial failure correctly', async () => {
83
- const sessionId = createSessionId('inject');
84
- const missingId = createSessionId('missing');
85
- await harness.spawnSession(sessionId);
86
-
87
- const inject = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}/inject`, {
88
- method: 'POST',
89
- body: { prompt: 'echo injected' }
90
- });
91
- assert.equal(inject.status, 200);
92
- assert.equal(inject.body.success, true);
93
-
94
- const injectMissingPrompt = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}/inject`, {
95
- method: 'POST',
96
- body: {}
97
- });
98
- assert.equal(injectMissingPrompt.status, 400);
99
-
100
- const multicast = await harness.request('/api/sessions/multicast/inject', {
101
- method: 'POST',
102
- body: {
103
- session_ids: [sessionId, missingId],
104
- prompt: 'echo multicast'
105
- }
106
- });
107
-
108
- assert.equal(multicast.status, 200);
109
- assert.deepEqual(multicast.body.results.successful, [sessionId]);
110
- assert.equal(multicast.body.results.failed.length, 1);
111
- assert.equal(multicast.body.results.failed[0].id, missingId);
112
- });
113
-
114
- test('broadcast inject publishes a single bus event with all successful target IDs', async () => {
115
- const sessionA = createSessionId('broadcast-a');
116
- const sessionB = createSessionId('broadcast-b');
117
- await harness.spawnSession(sessionA);
118
- await harness.spawnSession(sessionB);
119
-
120
- const bus = await harness.connectBus();
121
- const messages = collectJsonMessages(bus);
122
-
123
- const prompt = `echo ${createSessionId('broadcast-token')}`;
124
- const broadcast = await harness.request('/api/sessions/broadcast/inject', {
125
- method: 'POST',
126
- body: { prompt }
127
- });
128
-
129
- assert.equal(broadcast.status, 200);
130
- assert.equal(broadcast.body.results.successful.length, 2);
131
-
132
- await waitFor(() => messages.filter((message) => (
133
- message.type === 'injection' &&
134
- message.target_agent === 'all' &&
135
- message.content === prompt
136
- )).length === 1, { description: 'single broadcast bus event' });
137
-
138
- await delay(100);
139
-
140
- const event = messages.find((message) => message.type === 'injection' && message.content === prompt);
141
- assert.equal(messages.filter((message) => message.type === 'injection' && message.content === prompt).length, 1);
142
- assert.deepEqual(event.session_ids.slice().sort(), [sessionA, sessionB].sort());
143
-
144
- bus.close();
145
- });
146
-
147
- test('session WebSocket updates active client counts and relays PTY output', async () => {
148
- const sessionId = createSessionId('ws');
149
- await harness.spawnSession(sessionId);
150
-
151
- const firstClient = await harness.connectSession(sessionId);
152
- const secondClient = await harness.connectSession(sessionId);
153
- const outputs = collectJsonMessages(firstClient);
154
-
155
- await waitFor(async () => {
156
- const list = await harness.request('/api/sessions');
157
- const session = list.body.find((item) => item.id === sessionId);
158
- return session && session.active_clients === 2;
159
- }, { description: 'two attached websocket clients' });
160
-
161
- const token = createSessionId('ws-output');
162
- firstClient.send(JSON.stringify({ type: 'input', data: `echo ${token}\r` }));
163
-
164
- await waitFor(() => outputs.some((message) => (
165
- message.type === 'output' && String(message.data).includes(token)
166
- )), { timeoutMs: 7000, description: 'PTY output over websocket' });
167
-
168
- secondClient.close();
169
-
170
- await waitFor(async () => {
171
- const list = await harness.request('/api/sessions');
172
- const session = list.body.find((item) => item.id === sessionId);
173
- return session && session.active_clients === 1;
174
- }, { description: 'one attached websocket client after close' });
175
-
176
- firstClient.close();
177
-
178
- await waitFor(async () => {
179
- const list = await harness.request('/api/sessions');
180
- const session = list.body.find((item) => item.id === sessionId);
181
- return session && session.active_clients === 0;
182
- }, { description: 'zero attached websocket clients after close' });
183
- });
184
-
185
- test('DELETE /api/sessions/:id closes the session without crashing the daemon', async () => {
186
- const sessionId = createSessionId('delete');
187
- await harness.spawnSession(sessionId);
188
-
189
- const destroy = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}`, {
190
- method: 'DELETE'
191
- });
192
- assert.equal(destroy.status, 200);
193
- assert.equal(destroy.body.status, 'closing');
194
-
195
- await waitFor(async () => {
196
- const list = await harness.request('/api/sessions');
197
- return list.status === 200 && !list.body.some((session) => session.id === sessionId);
198
- }, { description: 'session removal after delete' });
199
-
200
- await delay(200);
201
- assert.equal(harness.isAlive(), true, harness.getLogs().stderr || harness.getLogs().stdout);
202
-
203
- const healthCheck = await harness.request('/api/sessions');
204
- assert.equal(healthCheck.status, 200);
205
- });
206
-
207
- // --- Wrapped session (register) tests ---
208
-
209
- test('POST /api/sessions/register creates a wrapped session with correct type', async () => {
210
- const sessionId = createSessionId('register');
211
- const result = await harness.registerSession(sessionId);
212
- assert.equal(result.status, 201);
213
- assert.equal(result.body.session_id, sessionId);
214
- assert.equal(result.body.type, 'wrapped');
215
-
216
- const list = await harness.request('/api/sessions');
217
- assert.equal(list.body.length, 1);
218
- assert.equal(list.body[0].id, sessionId);
219
- assert.equal(list.body[0].type, 'wrapped');
220
- });
221
-
222
- test('register rejects missing session_id and duplicate IDs', async () => {
223
- const noId = await harness.registerSession(undefined, { session_id: undefined });
224
- assert.equal(noId.status, 400);
225
-
226
- const sessionId = createSessionId('dup-reg');
227
- await harness.registerSession(sessionId);
228
- const duplicate = await harness.registerSession(sessionId);
229
- assert.equal(duplicate.status, 409);
230
- assert.match(duplicate.body.error, /already active/i);
231
- });
232
-
233
- test('register and spawn share the same namespace (cross-type duplicate rejection)', async () => {
234
- const sessionId = createSessionId('cross');
235
- await harness.spawnSession(sessionId);
236
- const dup = await harness.registerSession(sessionId);
237
- assert.equal(dup.status, 409);
238
-
239
- const sessionId2 = createSessionId('cross2');
240
- await harness.registerSession(sessionId2);
241
- const dup2 = await harness.spawnSession(sessionId2);
242
- assert.equal(dup2.status, 409);
243
- });
244
-
245
- test('register publishes a session_register bus event', async () => {
246
- const bus = await harness.connectBus();
247
- const messages = collectJsonMessages(bus);
248
-
249
- const sessionId = createSessionId('bus-reg');
250
- await harness.registerSession(sessionId);
251
-
252
- await waitFor(() => messages.find((message) => (
253
- message.type === 'session_register' &&
254
- message.session_id === sessionId
255
- )), { description: 'register bus event' });
256
-
257
- bus.close();
258
- });
259
-
260
- test('inject on wrapped session without owner returns 503', async () => {
261
- const sessionId = createSessionId('no-owner');
262
- await harness.registerSession(sessionId);
263
-
264
- const inject = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}/inject`, {
265
- method: 'POST',
266
- body: { prompt: 'hello' }
267
- });
268
- assert.equal(inject.status, 503);
269
- assert.match(inject.body.error, /not connected/i);
270
- });
271
-
272
- test('inject on wrapped session forwards to owner WebSocket', async () => {
273
- const sessionId = createSessionId('owner-inject');
274
- await harness.registerSession(sessionId);
275
-
276
- // First WebSocket connector becomes owner
277
- const ownerWs = await harness.connectSession(sessionId);
278
- const ownerMessages = collectJsonMessages(ownerWs);
279
-
280
- const inject = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}/inject`, {
281
- method: 'POST',
282
- body: { prompt: 'injected-text' }
283
- });
284
- assert.equal(inject.status, 200);
285
- assert.equal(inject.body.success, true);
286
-
287
- await waitFor(() => ownerMessages.find((message) => (
288
- message.type === 'inject' && String(message.data).includes('injected-text')
289
- )), { description: 'inject message forwarded to owner' });
290
-
291
- ownerWs.close();
292
- });
293
-
294
- test('wrapped session owner output broadcasts to attached clients', async () => {
295
- const sessionId = createSessionId('owner-broadcast');
296
- await harness.registerSession(sessionId);
297
-
298
- const ownerWs = await harness.connectSession(sessionId);
299
- const viewerWs = await harness.connectSession(sessionId);
300
- const viewerMessages = collectJsonMessages(viewerWs);
301
-
302
- // Owner sends output
303
- ownerWs.send(JSON.stringify({ type: 'output', data: 'hello-viewer' }));
304
-
305
- await waitFor(() => viewerMessages.find((message) => (
306
- message.type === 'output' && String(message.data).includes('hello-viewer')
307
- )), { description: 'owner output relayed to viewer' });
308
-
309
- ownerWs.close();
310
- viewerWs.close();
311
- });
312
-
313
- test('wrapped session non-owner input forwards to owner as inject', async () => {
314
- const sessionId = createSessionId('viewer-input');
315
- await harness.registerSession(sessionId);
316
-
317
- const ownerWs = await harness.connectSession(sessionId);
318
- const ownerMessages = collectJsonMessages(ownerWs);
319
- const viewerWs = await harness.connectSession(sessionId);
320
-
321
- viewerWs.send(JSON.stringify({ type: 'input', data: 'viewer-typing' }));
322
-
323
- await waitFor(() => ownerMessages.find((message) => (
324
- message.type === 'inject' && String(message.data).includes('viewer-typing')
325
- )), { description: 'viewer input forwarded to owner as inject' });
326
-
327
- ownerWs.close();
328
- viewerWs.close();
329
- });
330
-
331
- test('DELETE on wrapped session removes it without crashing the daemon', async () => {
332
- const sessionId = createSessionId('del-wrap');
333
- await harness.registerSession(sessionId);
334
-
335
- const destroy = await harness.request(`/api/sessions/${encodeURIComponent(sessionId)}`, {
336
- method: 'DELETE'
337
- });
338
- assert.equal(destroy.status, 200);
339
- assert.equal(destroy.body.status, 'closing');
340
-
341
- const list = await harness.request('/api/sessions');
342
- assert.equal(list.body.some((s) => s.id === sessionId), false);
343
-
344
- await delay(200);
345
- assert.equal(harness.isAlive(), true, harness.getLogs().stderr || harness.getLogs().stdout);
346
- });
347
-
348
- test('wrapped session auto-cleans when owner disconnects and no other clients remain', async () => {
349
- const sessionId = createSessionId('auto-clean');
350
- await harness.registerSession(sessionId);
351
-
352
- const ownerWs = await harness.connectSession(sessionId);
353
-
354
- await waitFor(async () => {
355
- const list = await harness.request('/api/sessions');
356
- const session = list.body.find((s) => s.id === sessionId);
357
- return session && session.active_clients === 1;
358
- }, { description: 'owner connected' });
359
-
360
- ownerWs.close();
361
-
362
- await waitFor(async () => {
363
- const list = await harness.request('/api/sessions');
364
- return !list.body.some((s) => s.id === sessionId);
365
- }, { description: 'wrapped session auto-removed after owner disconnect' });
366
- });
367
-
368
- test('multicast inject handles mixed spawned and wrapped sessions', async () => {
369
- const spawnedId = createSessionId('multi-spawn');
370
- const wrappedId = createSessionId('multi-wrap');
371
- await harness.spawnSession(spawnedId);
372
- await harness.registerSession(wrappedId);
373
-
374
- // Wrapped session without owner should fail
375
- const result = await harness.request('/api/sessions/multicast/inject', {
376
- method: 'POST',
377
- body: { session_ids: [spawnedId, wrappedId], prompt: 'echo mixed' }
378
- });
379
-
380
- assert.equal(result.status, 200);
381
- assert.deepEqual(result.body.results.successful, [spawnedId]);
382
- assert.equal(result.body.results.failed.length, 1);
383
- assert.equal(result.body.results.failed[0].id, wrappedId);
384
- assert.match(result.body.results.failed[0].error, /not connected/i);
385
- });
386
-
387
- test('spawned shells strip parent Claude session markers from the environment', async () => {
388
- const marker = createSessionId('claude-env');
389
- const localHarness = await startTestDaemon({ env: { CLAUDECODE: marker } });
390
-
391
- try {
392
- const sessionId = createSessionId('env');
393
- await localHarness.spawnSession(sessionId);
394
- const ws = await localHarness.connectSession(sessionId);
395
- const outputs = collectJsonMessages(ws);
396
-
397
- const command = process.platform === 'win32'
398
- ? "if ($env:CLAUDECODE) { Write-Output $env:CLAUDECODE } else { Write-Output '__unset__' }\r"
399
- : "if [ -n \"${CLAUDECODE}\" ]; then printf '%s\\n' \"$CLAUDECODE\"; else printf '__unset__\\n'; fi\r";
400
-
401
- ws.send(JSON.stringify({ type: 'input', data: command }));
402
-
403
- await waitFor(() => outputs.some((message) => (
404
- message.type === 'output' && String(message.data).includes('__unset__')
405
- )), { timeoutMs: 7000, description: 'sanitized Claude session marker' });
406
-
407
- assert.equal(outputs.some((message) => (
408
- message.type === 'output' && String(message.data).includes(marker)
409
- )), false);
410
-
411
- ws.close();
412
- } finally {
413
- await localHarness.stop();
414
- }
415
- });
package/test-pty.js DELETED
@@ -1,14 +0,0 @@
1
- const pty = require('node-pty');
2
- try {
3
- const p = pty.spawn('/bin/bash', [], {
4
- name: 'xterm-color',
5
- cols: 80,
6
- rows: 30,
7
- cwd: process.env.HOME,
8
- env: process.env
9
- });
10
- console.log('Success, PID:', p.pid);
11
- p.kill();
12
- } catch (e) {
13
- console.error('Error:', e);
14
- }