@seflless/ghosttown 1.7.0 → 2.0.0

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.
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Output Recorder
3
+ *
4
+ * Records PTY output to disk for scrollback persistence.
5
+ * Uses JSONL format for append-only, corruption-isolated storage.
6
+ *
7
+ * Each line in the JSONL file is a chunk of PTY output with timestamp:
8
+ * {"t": 1234567890123, "d": "raw output data"}
9
+ *
10
+ * The recorder batches writes to disk for performance (flushes every 100ms
11
+ * or when buffer exceeds 64KB).
12
+ */
13
+ import { EventEmitter } from 'events';
14
+ import { existsSync, mkdirSync } from 'fs';
15
+ import path from 'path';
16
+ import fs from 'fs/promises';
17
+ const DEFAULTS = {
18
+ maxChunks: 50000, // ~50K chunks (each chunk is a PTY output event)
19
+ flushInterval: 100, // 100ms
20
+ maxBufferSize: 65536, // 64KB
21
+ };
22
+ /**
23
+ * Records PTY output to disk with batched writes.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const recorder = new OutputRecorder({
28
+ * filePath: '/path/to/scrollback.jsonl'
29
+ * });
30
+ *
31
+ * await recorder.init();
32
+ *
33
+ * // Record output as it comes from PTY
34
+ * recorder.record('Hello, world!\r\n');
35
+ * recorder.record('\x1b[32mGreen text\x1b[0m\r\n');
36
+ *
37
+ * // Flush pending writes
38
+ * await recorder.flush();
39
+ *
40
+ * // Read all chunks
41
+ * const chunks = await recorder.readAll();
42
+ *
43
+ * // Clean up
44
+ * await recorder.close();
45
+ * ```
46
+ */
47
+ export class OutputRecorder extends EventEmitter {
48
+ constructor(config) {
49
+ super();
50
+ this.buffer = [];
51
+ this.bufferSize = 0;
52
+ this.flushTimer = null;
53
+ this.isWriting = false;
54
+ this.pendingFlush = null;
55
+ this.closed = false;
56
+ this.chunkCount = 0; // Track total chunks on disk
57
+ this.config = {
58
+ filePath: config.filePath,
59
+ maxChunks: config.maxChunks ?? DEFAULTS.maxChunks,
60
+ flushInterval: config.flushInterval ?? DEFAULTS.flushInterval,
61
+ maxBufferSize: config.maxBufferSize ?? DEFAULTS.maxBufferSize,
62
+ };
63
+ }
64
+ /**
65
+ * Initialize the recorder.
66
+ * Creates the directory and counts existing chunks.
67
+ */
68
+ async init() {
69
+ // Ensure directory exists
70
+ const dir = path.dirname(this.config.filePath);
71
+ if (!existsSync(dir)) {
72
+ mkdirSync(dir, { recursive: true });
73
+ }
74
+ // Count existing chunks
75
+ if (existsSync(this.config.filePath)) {
76
+ this.chunkCount = await this.countChunks();
77
+ }
78
+ // Start flush timer
79
+ this.flushTimer = setInterval(() => {
80
+ if (this.buffer.length > 0 && !this.isWriting) {
81
+ this.flush().catch((err) => this.emit('error', err));
82
+ }
83
+ }, this.config.flushInterval);
84
+ }
85
+ /**
86
+ * Record a chunk of output.
87
+ * The data is buffered and flushed to disk periodically.
88
+ */
89
+ record(data) {
90
+ if (this.closed) {
91
+ throw new Error('OutputRecorder is closed');
92
+ }
93
+ const chunk = {
94
+ t: Date.now(),
95
+ d: data,
96
+ };
97
+ this.buffer.push(chunk);
98
+ this.bufferSize += data.length;
99
+ // Flush if buffer exceeds size limit
100
+ if (this.bufferSize >= this.config.maxBufferSize) {
101
+ this.flush().catch((err) => this.emit('error', err));
102
+ }
103
+ }
104
+ /**
105
+ * Flush buffered chunks to disk.
106
+ * Returns immediately if already flushing (coalesces concurrent calls).
107
+ */
108
+ async flush() {
109
+ if (this.closed || this.buffer.length === 0) {
110
+ return;
111
+ }
112
+ // Coalesce concurrent flush calls
113
+ if (this.pendingFlush) {
114
+ return this.pendingFlush;
115
+ }
116
+ this.pendingFlush = this.doFlush();
117
+ try {
118
+ await this.pendingFlush;
119
+ }
120
+ finally {
121
+ this.pendingFlush = null;
122
+ }
123
+ }
124
+ /**
125
+ * Internal flush implementation.
126
+ */
127
+ async doFlush() {
128
+ if (this.buffer.length === 0) {
129
+ return;
130
+ }
131
+ this.isWriting = true;
132
+ try {
133
+ // Grab current buffer and reset
134
+ const chunks = this.buffer;
135
+ this.buffer = [];
136
+ this.bufferSize = 0;
137
+ // Convert to JSONL
138
+ const lines = chunks.map((c) => JSON.stringify(c)).join('\n') + '\n';
139
+ // Append to file
140
+ await fs.appendFile(this.config.filePath, lines, 'utf-8');
141
+ this.chunkCount += chunks.length;
142
+ this.emit('flush', chunks.length);
143
+ // Trim if over limit
144
+ if (this.chunkCount > this.config.maxChunks) {
145
+ await this.trimOldChunks();
146
+ }
147
+ }
148
+ finally {
149
+ this.isWriting = false;
150
+ }
151
+ }
152
+ /**
153
+ * Read all chunks from disk.
154
+ * Returns chunks in chronological order.
155
+ */
156
+ async readAll() {
157
+ // Flush pending writes first
158
+ await this.flush();
159
+ if (!existsSync(this.config.filePath)) {
160
+ return [];
161
+ }
162
+ const content = await fs.readFile(this.config.filePath, 'utf-8');
163
+ const lines = content.trim().split('\n').filter(Boolean);
164
+ const chunks = [];
165
+ for (const line of lines) {
166
+ try {
167
+ chunks.push(JSON.parse(line));
168
+ }
169
+ catch {
170
+ // Skip malformed lines
171
+ }
172
+ }
173
+ return chunks;
174
+ }
175
+ /**
176
+ * Read chunks with pagination.
177
+ * @param offset Number of chunks to skip from the start
178
+ * @param limit Maximum number of chunks to return
179
+ */
180
+ async read(offset, limit) {
181
+ const all = await this.readAll();
182
+ return all.slice(offset, offset + limit);
183
+ }
184
+ /**
185
+ * Get the total number of chunks (on disk + in buffer).
186
+ */
187
+ getChunkCount() {
188
+ return this.chunkCount + this.buffer.length;
189
+ }
190
+ /**
191
+ * Clear all recorded data.
192
+ */
193
+ async clear() {
194
+ this.buffer = [];
195
+ this.bufferSize = 0;
196
+ this.chunkCount = 0;
197
+ if (existsSync(this.config.filePath)) {
198
+ await fs.unlink(this.config.filePath);
199
+ }
200
+ }
201
+ /**
202
+ * Close the recorder and flush remaining data.
203
+ */
204
+ async close() {
205
+ if (this.closed)
206
+ return;
207
+ this.closed = true;
208
+ // Stop flush timer
209
+ if (this.flushTimer) {
210
+ clearInterval(this.flushTimer);
211
+ this.flushTimer = null;
212
+ }
213
+ // Final flush
214
+ await this.flush();
215
+ this.removeAllListeners();
216
+ }
217
+ /**
218
+ * Count chunks in the file.
219
+ */
220
+ async countChunks() {
221
+ if (!existsSync(this.config.filePath)) {
222
+ return 0;
223
+ }
224
+ const content = await fs.readFile(this.config.filePath, 'utf-8');
225
+ return content.trim().split('\n').filter(Boolean).length;
226
+ }
227
+ /**
228
+ * Trim old chunks to stay under the limit.
229
+ * Removes the oldest chunks by rewriting the file.
230
+ */
231
+ async trimOldChunks() {
232
+ if (!existsSync(this.config.filePath)) {
233
+ return;
234
+ }
235
+ const content = await fs.readFile(this.config.filePath, 'utf-8');
236
+ const lines = content.trim().split('\n').filter(Boolean);
237
+ if (lines.length <= this.config.maxChunks) {
238
+ return;
239
+ }
240
+ // Keep only the most recent chunks
241
+ const keepCount = Math.floor(this.config.maxChunks * 0.9); // Keep 90% of max
242
+ const trimmedLines = lines.slice(-keepCount);
243
+ await fs.writeFile(this.config.filePath, trimmedLines.join('\n') + '\n', 'utf-8');
244
+ this.chunkCount = trimmedLines.length;
245
+ }
246
+ }
247
+ //# sourceMappingURL=output-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-recorder.js","sourceRoot":"","sources":["output-recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Session Manager
3
+ *
4
+ * Core session management without tmux. Handles:
5
+ * - Direct PTY spawning via node-pty
6
+ * - Session persistence to disk
7
+ * - Session restoration on server restart
8
+ * - Multi-client connections
9
+ */
10
+ import { EventEmitter } from 'events';
11
+ import type { IPty } from '@lydell/node-pty';
12
+ import { HistoryReplay } from './history-replay.js';
13
+ import { type OutputChunk } from './output-recorder.js';
14
+ import type { CreateSessionOptions, Session, SessionId, SessionInfo, SessionManagerConfig, SessionPaths } from './types.js';
15
+ /**
16
+ * Get the default shell for the current platform.
17
+ */
18
+ declare function getDefaultShell(): string;
19
+ /**
20
+ * Get default environment variables for terminal sessions.
21
+ */
22
+ declare function getDefaultEnv(): Record<string, string>;
23
+ /**
24
+ * SessionManager handles all session lifecycle operations.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const manager = new SessionManager();
29
+ * await manager.init();
30
+ *
31
+ * // Create a new session
32
+ * const session = await manager.createSession({ name: 'dev' });
33
+ *
34
+ * // Get the PTY for I/O
35
+ * const pty = manager.getPty(session.id);
36
+ * pty.onData((data) => console.log(data));
37
+ * pty.write('echo hello\n');
38
+ *
39
+ * // List all sessions
40
+ * const sessions = await manager.listSessions();
41
+ *
42
+ * // Delete a session
43
+ * await manager.deleteSession(session.id);
44
+ * ```
45
+ */
46
+ export declare class SessionManager extends EventEmitter {
47
+ private config;
48
+ private paths;
49
+ private sessions;
50
+ private ptyProcesses;
51
+ private outputRecorders;
52
+ private initialized;
53
+ constructor(config?: SessionManagerConfig);
54
+ /**
55
+ * Initialize the session manager.
56
+ * Loads persisted sessions from disk.
57
+ */
58
+ init(): Promise<void>;
59
+ /**
60
+ * Create a new session.
61
+ */
62
+ createSession(options?: CreateSessionOptions): Promise<Session>;
63
+ /**
64
+ * Get a session by ID.
65
+ */
66
+ getSession(sessionId: SessionId): Session | undefined;
67
+ /**
68
+ * Get the PTY process for a session.
69
+ * Returns undefined if the process is not running.
70
+ */
71
+ getPty(sessionId: SessionId): IPty | undefined;
72
+ /**
73
+ * List all sessions with summary info.
74
+ */
75
+ listSessions(): Promise<SessionInfo[]>;
76
+ /**
77
+ * Delete a session and all associated data.
78
+ */
79
+ deleteSession(sessionId: SessionId): Promise<void>;
80
+ /**
81
+ * Rename a session.
82
+ */
83
+ renameSession(sessionId: SessionId, newName: string): Promise<void>;
84
+ /**
85
+ * Resize a session's terminal.
86
+ */
87
+ resizeSession(sessionId: SessionId, cols: number, rows: number): Promise<void>;
88
+ /**
89
+ * Connect to a session, respawning the process if needed.
90
+ * Returns the PTY for I/O.
91
+ */
92
+ connectToSession(sessionId: SessionId): Promise<IPty>;
93
+ /**
94
+ * Write data to a session's PTY.
95
+ */
96
+ write(sessionId: SessionId, data: string): void;
97
+ /**
98
+ * Get the storage paths for session data.
99
+ */
100
+ getPaths(): SessionPaths;
101
+ /**
102
+ * Get the buffered output for a session (for replay on reconnect).
103
+ * Returns the concatenated output as a single string.
104
+ * Note: This loads all scrollback into memory. For large histories,
105
+ * use getScrollbackChunks() instead.
106
+ */
107
+ getScrollback(sessionId: SessionId): Promise<string>;
108
+ /**
109
+ * Get scrollback chunks for a session with pagination.
110
+ * @param sessionId Session ID
111
+ * @param offset Number of chunks to skip from the start
112
+ * @param limit Maximum number of chunks to return
113
+ */
114
+ getScrollbackChunks(sessionId: SessionId, offset?: number, limit?: number): Promise<OutputChunk[]>;
115
+ /**
116
+ * Get the total number of scrollback chunks for a session.
117
+ */
118
+ getScrollbackLength(sessionId: SessionId): number;
119
+ /**
120
+ * Create a history replay for streaming scrollback to a client.
121
+ * The replay emits 'data', 'progress', and 'complete' events.
122
+ */
123
+ createHistoryReplay(sessionId: SessionId): Promise<HistoryReplay | null>;
124
+ /**
125
+ * Spawn a PTY process for a session.
126
+ */
127
+ private spawnProcess;
128
+ /**
129
+ * Persist a session's metadata to disk.
130
+ */
131
+ private persistSession;
132
+ /**
133
+ * Load persisted sessions from disk.
134
+ */
135
+ private loadPersistedSessions;
136
+ /**
137
+ * Generate a display name for a new session.
138
+ * Returns the next available number (1, 2, 3, ...).
139
+ */
140
+ private generateDisplayName;
141
+ /**
142
+ * Clean up all resources.
143
+ */
144
+ destroy(): Promise<void>;
145
+ }
146
+ export declare function getSessionManager(config?: SessionManagerConfig): SessionManager;
147
+ export { getDefaultShell, getDefaultEnv };