@myrialabs/ptykit 0.0.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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -0
  3. package/dist/client/fit.d.ts +29 -0
  4. package/dist/client/fit.d.ts.map +1 -0
  5. package/dist/client/fit.js +45 -0
  6. package/dist/client/fit.js.map +1 -0
  7. package/dist/client/index.d.ts +10 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +9 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/persistence.d.ts +15 -0
  12. package/dist/client/persistence.d.ts.map +1 -0
  13. package/dist/client/persistence.js +47 -0
  14. package/dist/client/persistence.js.map +1 -0
  15. package/dist/client/pty-kit-client.d.ts +122 -0
  16. package/dist/client/pty-kit-client.d.ts.map +1 -0
  17. package/dist/client/pty-kit-client.js +245 -0
  18. package/dist/client/pty-kit-client.js.map +1 -0
  19. package/dist/client/terminal.d.ts +77 -0
  20. package/dist/client/terminal.d.ts.map +1 -0
  21. package/dist/client/terminal.js +112 -0
  22. package/dist/client/terminal.js.map +1 -0
  23. package/dist/client/ws-core.d.ts +88 -0
  24. package/dist/client/ws-core.d.ts.map +1 -0
  25. package/dist/client/ws-core.js +324 -0
  26. package/dist/client/ws-core.js.map +1 -0
  27. package/dist/core/backend.d.ts +52 -0
  28. package/dist/core/backend.d.ts.map +1 -0
  29. package/dist/core/backend.js +11 -0
  30. package/dist/core/backend.js.map +1 -0
  31. package/dist/core/detect.d.ts +21 -0
  32. package/dist/core/detect.d.ts.map +1 -0
  33. package/dist/core/detect.js +82 -0
  34. package/dist/core/detect.js.map +1 -0
  35. package/dist/core/env.d.ts +30 -0
  36. package/dist/core/env.d.ts.map +1 -0
  37. package/dist/core/env.js +68 -0
  38. package/dist/core/env.js.map +1 -0
  39. package/dist/core/index.d.ts +11 -0
  40. package/dist/core/index.d.ts.map +1 -0
  41. package/dist/core/index.js +10 -0
  42. package/dist/core/index.js.map +1 -0
  43. package/dist/core/pty-kit.d.ts +90 -0
  44. package/dist/core/pty-kit.d.ts.map +1 -0
  45. package/dist/core/pty-kit.js +187 -0
  46. package/dist/core/pty-kit.js.map +1 -0
  47. package/dist/core/scrollback.d.ts +43 -0
  48. package/dist/core/scrollback.d.ts.map +1 -0
  49. package/dist/core/scrollback.js +79 -0
  50. package/dist/core/scrollback.js.map +1 -0
  51. package/dist/core/session.d.ts +100 -0
  52. package/dist/core/session.d.ts.map +1 -0
  53. package/dist/core/session.js +264 -0
  54. package/dist/core/session.js.map +1 -0
  55. package/dist/core/shell.d.ts +24 -0
  56. package/dist/core/shell.d.ts.map +1 -0
  57. package/dist/core/shell.js +55 -0
  58. package/dist/core/shell.js.map +1 -0
  59. package/dist/index.d.ts +11 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +11 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/server/connection.d.ts +38 -0
  64. package/dist/server/connection.d.ts.map +1 -0
  65. package/dist/server/connection.js +67 -0
  66. package/dist/server/connection.js.map +1 -0
  67. package/dist/server/ids.d.ts +2 -0
  68. package/dist/server/ids.d.ts.map +1 -0
  69. package/dist/server/ids.js +7 -0
  70. package/dist/server/ids.js.map +1 -0
  71. package/dist/server/index.d.ts +6 -0
  72. package/dist/server/index.d.ts.map +1 -0
  73. package/dist/server/index.js +6 -0
  74. package/dist/server/index.js.map +1 -0
  75. package/dist/server/pty-kit-server.d.ts +101 -0
  76. package/dist/server/pty-kit-server.d.ts.map +1 -0
  77. package/dist/server/pty-kit-server.js +361 -0
  78. package/dist/server/pty-kit-server.js.map +1 -0
  79. package/dist/server/transport-bun.d.ts +26 -0
  80. package/dist/server/transport-bun.d.ts.map +1 -0
  81. package/dist/server/transport-bun.js +79 -0
  82. package/dist/server/transport-bun.js.map +1 -0
  83. package/dist/server/transport-node.d.ts +20 -0
  84. package/dist/server/transport-node.d.ts.map +1 -0
  85. package/dist/server/transport-node.js +77 -0
  86. package/dist/server/transport-node.js.map +1 -0
  87. package/dist/shared/index.d.ts +260 -0
  88. package/dist/shared/index.d.ts.map +1 -0
  89. package/dist/shared/index.js +85 -0
  90. package/dist/shared/index.js.map +1 -0
  91. package/package.json +108 -0
  92. package/src/client/svelte/PtyTerminal.svelte +146 -0
  93. package/src/client/svelte/index.d.ts +84 -0
  94. package/src/client/svelte/index.js +4 -0
  95. package/src/client/svelte/svelte-compile.test.ts +11 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core PTY session engine — barrel.
3
+ */
4
+ export { detectBackendName, isBun, loadBackend, resetBackendCache } from './detect.js';
5
+ export { buildPtyEnv, getCleanSpawnEnv } from './env.js';
6
+ export { checkShell, isWindows, resolveCwd, resolveShell } from './shell.js';
7
+ export { MemoryBufferStore } from './scrollback.js';
8
+ export { Session, } from './session.js';
9
+ export { PtyKit, } from './pty-kit.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAmB,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAwB,MAAM,YAAY,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAoB,MAAM,iBAAiB,CAAC;AACtE,OAAO,EACN,OAAO,GAKP,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,MAAM,GAGN,MAAM,cAAc,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `PtyKit` — the session manager.
3
+ *
4
+ * Owns `Map<sessionId, Session>`, the auto-detected backend, the scrollback
5
+ * store, and session lifecycle: idempotent create (R3), no idle TTL by default
6
+ * (R4), retain-exited window for reconnect (R8). It is transport-agnostic — the
7
+ * WebSocket server wraps it.
8
+ */
9
+ import type { PtyBackend, PtyBackendName } from './backend.js';
10
+ import type { EnvOptions } from './env.js';
11
+ import { type BufferStore } from './scrollback.js';
12
+ import { Session } from './session.js';
13
+ import { type Logger } from '../shared/index.js';
14
+ export interface PtyKitOptions {
15
+ /** Environment hygiene (R2). */
16
+ env?: EnvOptions;
17
+ /** Headless scrollback lines (R6). Default 5000. */
18
+ scrollback?: number;
19
+ /** Scrollback strategy. Only `memory` ships in v1; inject a custom `store` to extend. */
20
+ buffer?: {
21
+ strategy?: 'memory';
22
+ store?: BufferStore;
23
+ };
24
+ /** Auto-kill sessions idle longer than this (ms). Default `null` = never (R4). */
25
+ idleTtl?: number | null;
26
+ /** Keep exited sessions this long for reconnect replay (ms). Default 5 min (R8). `null` = forever. */
27
+ retainExitedMs?: number | null;
28
+ /** Idle `\r` fallback delay after spawn (ms). Default 350 (R18). */
29
+ idleFallbackMs?: number;
30
+ /** Grace before SIGKILL after Ctrl+C on `kill` (ms). Default 1000 (R19). */
31
+ killGraceMs?: number;
32
+ /** Inject a backend (tests / forcing a specific one). */
33
+ backend?: PtyBackend;
34
+ /** Force the auto-detect choice. */
35
+ preferBackend?: PtyBackendName;
36
+ /** Diagnostics sink. Off by default — the core stays silent. */
37
+ logger?: Logger;
38
+ }
39
+ export interface CreateSessionOptions {
40
+ sessionId: string;
41
+ namespace: string;
42
+ streamId?: string;
43
+ shell?: string;
44
+ cwd?: string;
45
+ cols?: number;
46
+ rows?: number;
47
+ }
48
+ export declare class PtyKit {
49
+ private readonly sessions;
50
+ private readonly buffers;
51
+ private readonly options;
52
+ private readonly logger;
53
+ private readonly retainExitedMs;
54
+ private readonly idleTtl;
55
+ private backend;
56
+ private readonly removalTimers;
57
+ private idleSweeper;
58
+ private disposed;
59
+ constructor(options?: PtyKitOptions);
60
+ /** The active backend, once resolved. `null` until the first `createSession`. */
61
+ get activeBackend(): PtyBackend | null;
62
+ private resolveBackend;
63
+ /**
64
+ * Create a session, or reuse an existing active one with the same id (R3).
65
+ * If a retained-exited session holds the id, it is discarded and replaced.
66
+ * Throws if the id is held by a different namespace (anti-hijack defense).
67
+ */
68
+ createSession(opts: CreateSessionOptions): Promise<Session>;
69
+ getSession(sessionId: string): Session | undefined;
70
+ /** All sessions in a namespace. */
71
+ list(namespace: string): Session[];
72
+ write(sessionId: string, data: string): boolean;
73
+ resize(sessionId: string, cols: number, rows: number): boolean;
74
+ cancel(sessionId: string): boolean;
75
+ clear(sessionId: string): boolean;
76
+ /** Serialized scrollback frame, ready to replay to a client (R6/R7). */
77
+ getSerializedState(sessionId: string): string;
78
+ /**
79
+ * Kill a session and remove it immediately (no retain window — the caller
80
+ * asked for it gone). Returns false if no such session.
81
+ */
82
+ killSession(sessionId: string, signal?: string): boolean;
83
+ private scheduleRemoval;
84
+ private sweepIdle;
85
+ /** Fully remove a session: dispose PTY subscriptions, timers, and scrollback. */
86
+ private remove;
87
+ /** Kill and remove every session. */
88
+ dispose(): void;
89
+ }
90
+ //# sourceMappingURL=pty-kit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-kit.d.ts","sourceRoot":"","sources":["../../src/core/pty-kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * `PtyKit` — the session manager.
3
+ *
4
+ * Owns `Map<sessionId, Session>`, the auto-detected backend, the scrollback
5
+ * store, and session lifecycle: idempotent create (R3), no idle TTL by default
6
+ * (R4), retain-exited window for reconnect (R8). It is transport-agnostic — the
7
+ * WebSocket server wraps it.
8
+ */
9
+ import { loadBackend } from './detect.js';
10
+ import { MemoryBufferStore } from './scrollback.js';
11
+ import { Session } from './session.js';
12
+ import { resolveCwd } from './shell.js';
13
+ import { silentLogger } from '../shared/index.js';
14
+ const DEFAULT_RETAIN_EXITED_MS = 5 * 60_000;
15
+ export class PtyKit {
16
+ sessions = new Map();
17
+ buffers;
18
+ options;
19
+ logger;
20
+ retainExitedMs;
21
+ idleTtl;
22
+ backend;
23
+ removalTimers = new Map();
24
+ idleSweeper = null;
25
+ disposed = false;
26
+ constructor(options = {}) {
27
+ this.options = options;
28
+ this.logger = options.logger ?? silentLogger;
29
+ this.backend = options.backend ?? null;
30
+ this.retainExitedMs =
31
+ options.retainExitedMs === undefined ? DEFAULT_RETAIN_EXITED_MS : options.retainExitedMs;
32
+ this.idleTtl = options.idleTtl ?? null;
33
+ this.buffers = options.buffer?.store ?? new MemoryBufferStore(options.scrollback ?? 5000);
34
+ if (this.idleTtl !== null && this.idleTtl > 0) {
35
+ this.idleSweeper = setInterval(() => this.sweepIdle(), Math.min(this.idleTtl, 30_000));
36
+ // Don't keep the process alive solely for the sweeper.
37
+ this.idleSweeper?.unref?.();
38
+ }
39
+ }
40
+ /** The active backend, once resolved. `null` until the first `createSession`. */
41
+ get activeBackend() {
42
+ return this.backend;
43
+ }
44
+ async resolveBackend() {
45
+ if (!this.backend) {
46
+ this.backend = await loadBackend(this.options.preferBackend);
47
+ }
48
+ return this.backend;
49
+ }
50
+ /**
51
+ * Create a session, or reuse an existing active one with the same id (R3).
52
+ * If a retained-exited session holds the id, it is discarded and replaced.
53
+ * Throws if the id is held by a different namespace (anti-hijack defense).
54
+ */
55
+ async createSession(opts) {
56
+ if (this.disposed)
57
+ throw new Error('PtyKit has been disposed');
58
+ const existing = this.sessions.get(opts.sessionId);
59
+ if (existing) {
60
+ if (existing.namespace !== opts.namespace) {
61
+ throw new Error('Access denied');
62
+ }
63
+ if (existing.status === 'active') {
64
+ existing.lastActivityAt = new Date();
65
+ return existing;
66
+ }
67
+ // Exited remnant — drop it and spawn fresh under the same id.
68
+ this.remove(opts.sessionId);
69
+ }
70
+ const backend = await this.resolveBackend();
71
+ const cwd = resolveCwd(opts.cwd);
72
+ const cols = opts.cols ?? 80;
73
+ const rows = opts.rows ?? 24;
74
+ const streamId = opts.streamId ?? `${opts.sessionId}-stream`;
75
+ const session = new Session({
76
+ sessionId: opts.sessionId,
77
+ namespace: opts.namespace,
78
+ streamId,
79
+ shell: opts.shell,
80
+ cwd,
81
+ cols,
82
+ rows,
83
+ env: this.options.env,
84
+ backend,
85
+ buffers: this.buffers,
86
+ idleFallbackMs: this.options.idleFallbackMs,
87
+ killGraceMs: this.options.killGraceMs,
88
+ logger: this.logger,
89
+ });
90
+ // Schedule retain-then-remove when the process exits on its own (R8).
91
+ session.addExitListener(() => this.scheduleRemoval(opts.sessionId));
92
+ this.sessions.set(opts.sessionId, session);
93
+ return session;
94
+ }
95
+ getSession(sessionId) {
96
+ return this.sessions.get(sessionId);
97
+ }
98
+ /** All sessions in a namespace. */
99
+ list(namespace) {
100
+ return [...this.sessions.values()].filter((s) => s.namespace === namespace);
101
+ }
102
+ write(sessionId, data) {
103
+ return this.sessions.get(sessionId)?.write(data) ?? false;
104
+ }
105
+ resize(sessionId, cols, rows) {
106
+ return this.sessions.get(sessionId)?.resize(cols, rows) ?? false;
107
+ }
108
+ cancel(sessionId) {
109
+ const session = this.sessions.get(sessionId);
110
+ if (!session)
111
+ return false;
112
+ session.cancel();
113
+ return true;
114
+ }
115
+ clear(sessionId) {
116
+ const session = this.sessions.get(sessionId);
117
+ if (!session)
118
+ return false;
119
+ session.clearScreen();
120
+ return true;
121
+ }
122
+ /** Serialized scrollback frame, ready to replay to a client (R6/R7). */
123
+ getSerializedState(sessionId) {
124
+ return this.sessions.get(sessionId)?.serialize() ?? '';
125
+ }
126
+ /**
127
+ * Kill a session and remove it immediately (no retain window — the caller
128
+ * asked for it gone). Returns false if no such session.
129
+ */
130
+ killSession(sessionId, signal) {
131
+ const session = this.sessions.get(sessionId);
132
+ if (!session)
133
+ return false;
134
+ session.kill(signal);
135
+ this.remove(sessionId);
136
+ return true;
137
+ }
138
+ scheduleRemoval(sessionId) {
139
+ if (this.retainExitedMs === null)
140
+ return; // keep forever until killed/disposed
141
+ if (this.removalTimers.has(sessionId))
142
+ return;
143
+ const timer = setTimeout(() => {
144
+ this.removalTimers.delete(sessionId);
145
+ this.remove(sessionId);
146
+ }, this.retainExitedMs);
147
+ timer?.unref?.();
148
+ this.removalTimers.set(sessionId, timer);
149
+ }
150
+ sweepIdle() {
151
+ if (this.idleTtl === null)
152
+ return;
153
+ const cutoff = Date.now() - this.idleTtl;
154
+ for (const session of this.sessions.values()) {
155
+ if (session.status === 'active' && session.lastActivityAt.getTime() < cutoff) {
156
+ this.killSession(session.sessionId, 'SIGKILL');
157
+ }
158
+ }
159
+ }
160
+ /** Fully remove a session: dispose PTY subscriptions, timers, and scrollback. */
161
+ remove(sessionId) {
162
+ const session = this.sessions.get(sessionId);
163
+ if (!session)
164
+ return;
165
+ session.dispose();
166
+ this.buffers.dispose(sessionId);
167
+ this.sessions.delete(sessionId);
168
+ const timer = this.removalTimers.get(sessionId);
169
+ if (timer) {
170
+ clearTimeout(timer);
171
+ this.removalTimers.delete(sessionId);
172
+ }
173
+ }
174
+ /** Kill and remove every session. */
175
+ dispose() {
176
+ this.disposed = true;
177
+ if (this.idleSweeper) {
178
+ clearInterval(this.idleSweeper);
179
+ this.idleSweeper = null;
180
+ }
181
+ for (const sessionId of [...this.sessions.keys()]) {
182
+ this.sessions.get(sessionId)?.kill('SIGKILL');
183
+ this.remove(sessionId);
184
+ }
185
+ }
186
+ }
187
+ //# sourceMappingURL=pty-kit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-kit.js","sourceRoot":"","sources":["../../src/core/pty-kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Scrollback persistence (R6).
3
+ *
4
+ * Scrollback lives in a headless xterm per session, serialized on demand via
5
+ * `@xterm/addon-serialize`. Replay is therefore a single structured ANSI frame
6
+ * (grid + scrollback + attributes), not a raw byte history — so it is correct
7
+ * after `clear`, compact, and immune to partial-byte corruption.
8
+ *
9
+ * `BufferStore` is the seam that lets a disk-backed strategy be added later
10
+ * WITHOUT changing the public API. Only `MemoryBufferStore` ships in v1; build a
11
+ * disk-backed store only if real memory footprint data demands it.
12
+ */
13
+ /** Per-session scrollback store. Implementations must be persist-first safe. */
14
+ export interface BufferStore {
15
+ /** Allocate a buffer for a session (idempotent). */
16
+ create(sessionId: string, cols: number, rows: number): void;
17
+ /** Append raw PTY output to the session's buffer. */
18
+ write(sessionId: string, data: string): void;
19
+ /** Serialize the session's current screen + scrollback to a replayable frame. */
20
+ serialize(sessionId: string): string;
21
+ /** Clear the session's buffer (sync with a client-side clear). */
22
+ clear(sessionId: string): void;
23
+ /** Resize the session's buffer to match the PTY. */
24
+ resize(sessionId: string, cols: number, rows: number): void;
25
+ /** Number of lines currently held (for status reporting). */
26
+ length(sessionId: string): number;
27
+ /** Dispose the session's buffer and free memory. */
28
+ dispose(sessionId: string): void;
29
+ }
30
+ /** In-memory scrollback via `@xterm/headless` + `@xterm/addon-serialize`. */
31
+ export declare class MemoryBufferStore implements BufferStore {
32
+ private readonly entries;
33
+ private readonly scrollback;
34
+ constructor(scrollback?: number);
35
+ create(sessionId: string, cols: number, rows: number): void;
36
+ write(sessionId: string, data: string): void;
37
+ serialize(sessionId: string): string;
38
+ clear(sessionId: string): void;
39
+ resize(sessionId: string, cols: number, rows: number): void;
40
+ length(sessionId: string): number;
41
+ dispose(sessionId: string): void;
42
+ }
43
+ //# sourceMappingURL=scrollback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrollback.d.ts","sourceRoot":"","sources":["../../src/core/scrollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC3B,oDAAoD;IACpD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5D,qDAAqD;IACrD,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,iFAAiF;IACjF,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,kEAAkE;IAClE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,oDAAoD;IACpD,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5D,6DAA6D;IAC7D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,oDAAoD;IACpD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAOD,6EAA6E;AAC7E,qBAAa,iBAAkB,YAAW,WAAW;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,UAAU,SAAO;IAI7B,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAe3D,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5C,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIpC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAKjC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAehC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Scrollback persistence (R6).
3
+ *
4
+ * Scrollback lives in a headless xterm per session, serialized on demand via
5
+ * `@xterm/addon-serialize`. Replay is therefore a single structured ANSI frame
6
+ * (grid + scrollback + attributes), not a raw byte history — so it is correct
7
+ * after `clear`, compact, and immune to partial-byte corruption.
8
+ *
9
+ * `BufferStore` is the seam that lets a disk-backed strategy be added later
10
+ * WITHOUT changing the public API. Only `MemoryBufferStore` ships in v1; build a
11
+ * disk-backed store only if real memory footprint data demands it.
12
+ */
13
+ // @xterm/headless and @xterm/addon-serialize are CJS/UMD packages. Node's ESM
14
+ // `cjs-module-lexer` fails to detect their named exports, so a plain
15
+ // `import { Terminal }` throws under Node ("does not provide an export named
16
+ // 'Terminal'"). Default-import the module object and destructure — this is the
17
+ // only form that works on BOTH Node ESM and Bun.
18
+ import headlessPkg from '@xterm/headless';
19
+ import serializePkg from '@xterm/addon-serialize';
20
+ const { Terminal } = headlessPkg;
21
+ const { SerializeAddon } = serializePkg;
22
+ /** In-memory scrollback via `@xterm/headless` + `@xterm/addon-serialize`. */
23
+ export class MemoryBufferStore {
24
+ entries = new Map();
25
+ scrollback;
26
+ constructor(scrollback = 5000) {
27
+ this.scrollback = scrollback;
28
+ }
29
+ create(sessionId, cols, rows) {
30
+ if (this.entries.has(sessionId))
31
+ return;
32
+ const terminal = new Terminal({
33
+ scrollback: this.scrollback,
34
+ cols,
35
+ rows,
36
+ allowProposedApi: true,
37
+ });
38
+ const serialize = new SerializeAddon();
39
+ // @xterm/headless and the serialize addon are versioned independently;
40
+ // their addon interfaces are structurally compatible at runtime.
41
+ terminal.loadAddon(serialize);
42
+ this.entries.set(sessionId, { terminal, serialize });
43
+ }
44
+ write(sessionId, data) {
45
+ this.entries.get(sessionId)?.terminal.write(data);
46
+ }
47
+ serialize(sessionId) {
48
+ return this.entries.get(sessionId)?.serialize.serialize() ?? '';
49
+ }
50
+ clear(sessionId) {
51
+ this.entries.get(sessionId)?.terminal.clear();
52
+ }
53
+ resize(sessionId, cols, rows) {
54
+ this.entries.get(sessionId)?.terminal.resize(cols, rows);
55
+ }
56
+ length(sessionId) {
57
+ const entry = this.entries.get(sessionId);
58
+ return entry ? entry.terminal.buffer.active.length : 0;
59
+ }
60
+ dispose(sessionId) {
61
+ const entry = this.entries.get(sessionId);
62
+ if (!entry)
63
+ return;
64
+ try {
65
+ entry.serialize.dispose();
66
+ }
67
+ catch {
68
+ // addon already disposed
69
+ }
70
+ try {
71
+ entry.terminal.dispose();
72
+ }
73
+ catch {
74
+ // terminal already disposed
75
+ }
76
+ this.entries.delete(sessionId);
77
+ }
78
+ }
79
+ //# sourceMappingURL=scrollback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrollback.js","sourceRoot":"","sources":["../../src/core/scrollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,8EAA8E;AAC9E,qEAAqE;AACrE,6EAA6E;AAC7E,+EAA+E;AAC/E,iDAAiD;AACjD,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAElD,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC;AACjC,MAAM,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC;AA2BxC,6EAA6E;AAC7E,MAAM,OAAO,iBAAiB;IACZ,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC3C,UAAU,CAAS;IAEpC,YAAY,UAAU,GAAG,IAAI;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,SAAiB,EAAE,IAAY,EAAE,IAAY;QACnD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO;QACxC,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;YAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,IAAI;YACJ,IAAI;YACJ,gBAAgB,EAAE,IAAI;SACtB,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,cAAc,EAAE,CAAC;QACvC,uEAAuE;QACvE,iEAAiE;QACjE,QAAQ,CAAC,SAAS,CAAC,SAAgB,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,SAAiB,EAAE,IAAY;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;IAED,SAAS,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,SAAiB;QACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,SAAiB,EAAE,IAAY,EAAE,IAAY;QACnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,SAAiB;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,CAAC,SAAiB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC;YACJ,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACR,yBAAyB;QAC1B,CAAC;QACD,IAAI,CAAC;YACJ,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACR,4BAA4B;QAC7B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;CACD"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * A single PTY session: process handle + output pipeline + scrollback binding.
3
+ *
4
+ * Output pipeline (R5, R7a):
5
+ * 1. Persist every chunk to the headless terminal FIRST — even with zero
6
+ * listeners — so scrollback stays accurate while clients are disconnected.
7
+ * 2. Micro-task batch high-frequency output (`queueMicrotask`).
8
+ * 3. Stamp a monotonic `seq` per flush for client-side dedup.
9
+ * 4. Fan out the batched chunk to every listener.
10
+ *
11
+ * Lifecycle: idle `\r` fallback after 350ms of silence (R18); kill = Ctrl+C
12
+ * then SIGKILL after 1s, or a direct signal (R19).
13
+ */
14
+ import type { PtyBackend, PtyExitEvent } from './backend.js';
15
+ import type { BufferStore } from './scrollback.js';
16
+ import { type EnvOptions } from './env.js';
17
+ import { type Logger } from '../shared/index.js';
18
+ /** Output listener: receives a batched chunk and its monotonic sequence. */
19
+ export type SessionDataListener = (data: string, seq: number) => void;
20
+ /** Exit listener. */
21
+ export type SessionExitListener = (event: PtyExitEvent) => void;
22
+ export interface SessionConfig {
23
+ sessionId: string;
24
+ namespace: string;
25
+ streamId: string;
26
+ shell?: string;
27
+ cwd: string;
28
+ cols: number;
29
+ rows: number;
30
+ env?: EnvOptions;
31
+ backend: PtyBackend;
32
+ buffers: BufferStore;
33
+ idleFallbackMs?: number;
34
+ killGraceMs?: number;
35
+ logger?: Logger;
36
+ }
37
+ export interface SessionInfo {
38
+ sessionId: string;
39
+ namespace: string;
40
+ streamId: string;
41
+ pid: number;
42
+ cwd: string;
43
+ cols: number;
44
+ rows: number;
45
+ createdAt: Date;
46
+ lastActivityAt: Date;
47
+ status: 'active' | 'exited';
48
+ exitCode?: number;
49
+ }
50
+ export declare class Session {
51
+ readonly sessionId: string;
52
+ readonly namespace: string;
53
+ readonly streamId: string;
54
+ readonly createdAt: Date;
55
+ cwd: string;
56
+ cols: number;
57
+ rows: number;
58
+ lastActivityAt: Date;
59
+ status: 'active' | 'exited';
60
+ exitCode?: number;
61
+ /** Monotonic sequence, incremented once per flushed batch (R5). */
62
+ outputSeq: number;
63
+ private readonly handle;
64
+ private readonly buffers;
65
+ private readonly logger;
66
+ private readonly killGraceMs;
67
+ private readonly dataListeners;
68
+ private readonly exitListeners;
69
+ private pendingOutput;
70
+ private flushScheduled;
71
+ private receivedInitialOutput;
72
+ private readonly dataSub;
73
+ private readonly exitSub;
74
+ private idleTimer;
75
+ private killTimer;
76
+ constructor(config: SessionConfig);
77
+ get pid(): number;
78
+ info(): SessionInfo;
79
+ private onData;
80
+ private onExit;
81
+ addDataListener(listener: SessionDataListener): void;
82
+ removeDataListener(listener: SessionDataListener): void;
83
+ addExitListener(listener: SessionExitListener): void;
84
+ removeExitListener(listener: SessionExitListener): void;
85
+ /** Clear ALL listeners — call before attaching fresh ones to avoid double output (R7c). */
86
+ clearListeners(): void;
87
+ get dataListenerCount(): number;
88
+ write(data: string): boolean;
89
+ resize(cols: number, rows: number): boolean;
90
+ /** Send Ctrl+C (R19). */
91
+ cancel(): void;
92
+ /** Kill: Ctrl+C then SIGKILL after the grace window, or a direct signal (R19). */
93
+ kill(signal?: string): void;
94
+ serialize(): string;
95
+ clearScreen(): void;
96
+ bufferLength(): number;
97
+ /** Tear down PTY subscriptions and timers. Does NOT dispose scrollback. */
98
+ dispose(): void;
99
+ }
100
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}