green-screen-proxy 0.3.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.
Files changed (67) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +32 -0
  3. package/dist/hp6530/connection.d.ts +51 -0
  4. package/dist/hp6530/connection.js +258 -0
  5. package/dist/hp6530/constants.d.ts +64 -0
  6. package/dist/hp6530/constants.js +135 -0
  7. package/dist/hp6530/encoder.d.ts +37 -0
  8. package/dist/hp6530/encoder.js +89 -0
  9. package/dist/hp6530/parser.d.ts +45 -0
  10. package/dist/hp6530/parser.js +255 -0
  11. package/dist/hp6530/screen.d.ts +104 -0
  12. package/dist/hp6530/screen.js +252 -0
  13. package/dist/mock/mock-routes.d.ts +2 -0
  14. package/dist/mock/mock-routes.js +231 -0
  15. package/dist/protocols/hp6530-handler.d.ts +29 -0
  16. package/dist/protocols/hp6530-handler.js +64 -0
  17. package/dist/protocols/index.d.ts +11 -0
  18. package/dist/protocols/index.js +27 -0
  19. package/dist/protocols/tn3270-handler.d.ts +26 -0
  20. package/dist/protocols/tn3270-handler.js +61 -0
  21. package/dist/protocols/tn5250-handler.d.ts +26 -0
  22. package/dist/protocols/tn5250-handler.js +62 -0
  23. package/dist/protocols/types.d.ts +59 -0
  24. package/dist/protocols/types.js +7 -0
  25. package/dist/protocols/vt-handler.d.ts +30 -0
  26. package/dist/protocols/vt-handler.js +67 -0
  27. package/dist/routes.d.ts +2 -0
  28. package/dist/routes.js +141 -0
  29. package/dist/server.d.ts +1 -0
  30. package/dist/server.js +34 -0
  31. package/dist/session.d.ts +32 -0
  32. package/dist/session.js +88 -0
  33. package/dist/tn3270/connection.d.ts +31 -0
  34. package/dist/tn3270/connection.js +266 -0
  35. package/dist/tn3270/constants.d.ts +262 -0
  36. package/dist/tn3270/constants.js +261 -0
  37. package/dist/tn3270/encoder.d.ts +24 -0
  38. package/dist/tn3270/encoder.js +97 -0
  39. package/dist/tn3270/parser.d.ts +22 -0
  40. package/dist/tn3270/parser.js +284 -0
  41. package/dist/tn3270/screen.d.ts +89 -0
  42. package/dist/tn3270/screen.js +207 -0
  43. package/dist/tn5250/connection.d.ts +41 -0
  44. package/dist/tn5250/connection.js +254 -0
  45. package/dist/tn5250/constants.d.ts +128 -0
  46. package/dist/tn5250/constants.js +156 -0
  47. package/dist/tn5250/ebcdic.d.ts +10 -0
  48. package/dist/tn5250/ebcdic.js +89 -0
  49. package/dist/tn5250/encoder.d.ts +30 -0
  50. package/dist/tn5250/encoder.js +121 -0
  51. package/dist/tn5250/parser.d.ts +33 -0
  52. package/dist/tn5250/parser.js +412 -0
  53. package/dist/tn5250/screen.d.ts +80 -0
  54. package/dist/tn5250/screen.js +155 -0
  55. package/dist/vt/connection.d.ts +45 -0
  56. package/dist/vt/connection.js +229 -0
  57. package/dist/vt/constants.d.ts +97 -0
  58. package/dist/vt/constants.js +163 -0
  59. package/dist/vt/encoder.d.ts +30 -0
  60. package/dist/vt/encoder.js +55 -0
  61. package/dist/vt/parser.d.ts +36 -0
  62. package/dist/vt/parser.js +534 -0
  63. package/dist/vt/screen.d.ts +101 -0
  64. package/dist/vt/screen.js +424 -0
  65. package/dist/websocket.d.ts +6 -0
  66. package/dist/websocket.js +50 -0
  67. package/package.json +57 -0
@@ -0,0 +1,155 @@
1
+ import { createHash } from 'crypto';
2
+ import { SCREEN } from './constants.js';
3
+ export class ScreenBuffer {
4
+ rows;
5
+ cols;
6
+ /** Character buffer stored as UTF-8 characters */
7
+ buffer;
8
+ /** Attribute buffer (display attribute per cell) */
9
+ attrBuffer;
10
+ fields = [];
11
+ cursorRow = 0;
12
+ cursorCol = 0;
13
+ constructor(rows = SCREEN.ROWS_24, cols = SCREEN.COLS_80) {
14
+ this.rows = rows;
15
+ this.cols = cols;
16
+ const size = rows * cols;
17
+ this.buffer = new Array(size).fill(' ');
18
+ this.attrBuffer = new Array(size).fill(0x20); // normal
19
+ }
20
+ get size() {
21
+ return this.rows * this.cols;
22
+ }
23
+ /** Convert row,col to linear offset */
24
+ offset(row, col) {
25
+ return row * this.cols + col;
26
+ }
27
+ /** Convert linear offset to row,col */
28
+ toRowCol(offset) {
29
+ return {
30
+ row: Math.floor(offset / this.cols),
31
+ col: offset % this.cols,
32
+ };
33
+ }
34
+ /** Set a character at row,col */
35
+ setChar(row, col, char) {
36
+ const off = this.offset(row, col);
37
+ if (off >= 0 && off < this.size) {
38
+ this.buffer[off] = char;
39
+ }
40
+ }
41
+ /** Get a character at row,col */
42
+ getChar(row, col) {
43
+ const off = this.offset(row, col);
44
+ return off >= 0 && off < this.size ? this.buffer[off] : ' ';
45
+ }
46
+ /** Set attribute at row,col */
47
+ setAttr(row, col, attr) {
48
+ const off = this.offset(row, col);
49
+ if (off >= 0 && off < this.size) {
50
+ this.attrBuffer[off] = attr;
51
+ }
52
+ }
53
+ /** Set character at a linear address */
54
+ setCharAt(addr, char) {
55
+ if (addr >= 0 && addr < this.size) {
56
+ this.buffer[addr] = char;
57
+ }
58
+ }
59
+ /** Set attribute at a linear address */
60
+ setAttrAt(addr, attr) {
61
+ if (addr >= 0 && addr < this.size) {
62
+ this.attrBuffer[addr] = attr;
63
+ }
64
+ }
65
+ /** Clear the entire screen */
66
+ clear() {
67
+ this.buffer.fill(' ');
68
+ this.attrBuffer.fill(0x20);
69
+ this.fields = [];
70
+ this.cursorRow = 0;
71
+ this.cursorCol = 0;
72
+ }
73
+ /** Fill range [start, end) with a character */
74
+ fillRange(start, end, char) {
75
+ for (let i = start; i < end && i < this.size; i++) {
76
+ this.buffer[i] = char;
77
+ }
78
+ }
79
+ /** Get the content of a field as a string */
80
+ getFieldValue(field) {
81
+ const start = this.offset(field.row, field.col);
82
+ return this.buffer.slice(start, start + field.length).join('');
83
+ }
84
+ /** Set the content of a field */
85
+ setFieldValue(field, value) {
86
+ const start = this.offset(field.row, field.col);
87
+ for (let i = 0; i < field.length; i++) {
88
+ this.buffer[start + i] = i < value.length ? value[i] : ' ';
89
+ }
90
+ field.modified = true;
91
+ }
92
+ /** Find the field at cursor position */
93
+ getFieldAtCursor() {
94
+ return this.getFieldAt(this.cursorRow, this.cursorCol);
95
+ }
96
+ /** Find the field containing a given position */
97
+ getFieldAt(row, col) {
98
+ const pos = this.offset(row, col);
99
+ for (const field of this.fields) {
100
+ const start = this.offset(field.row, field.col);
101
+ if (pos >= start && pos < start + field.length) {
102
+ return field;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ /** Whether a field is an input field (not bypass/protected) */
108
+ isInputField(field) {
109
+ return (field.ffw1 & 0x20) === 0; // BYPASS bit not set
110
+ }
111
+ /** Whether a field is highlighted (high intensity) */
112
+ isHighlighted(field) {
113
+ return field.attribute === 0x22;
114
+ }
115
+ /** Whether a field is reverse video */
116
+ isReverse(field) {
117
+ return field.attribute === 0x21;
118
+ }
119
+ /** Whether a field is non-display (password) */
120
+ isNonDisplay(field) {
121
+ return field.attribute === 0x27;
122
+ }
123
+ /** Convert screen buffer to the ScreenData format expected by the frontend */
124
+ toScreenData() {
125
+ // Build content as newline-separated rows
126
+ const lines = [];
127
+ for (let r = 0; r < this.rows; r++) {
128
+ const start = r * this.cols;
129
+ lines.push(this.buffer.slice(start, start + this.cols).join(''));
130
+ }
131
+ const content = lines.join('\n');
132
+ // Map fields to frontend format
133
+ const fields = this.fields.map(f => ({
134
+ row: f.row,
135
+ col: f.col,
136
+ length: f.length,
137
+ is_input: this.isInputField(f),
138
+ is_protected: !this.isInputField(f),
139
+ is_highlighted: this.isHighlighted(f) || undefined,
140
+ is_reverse: this.isReverse(f) || undefined,
141
+ }));
142
+ // Generate screen signature
143
+ const hash = createHash('md5').update(content).digest('hex').substring(0, 12);
144
+ return {
145
+ content,
146
+ cursor_row: this.cursorRow,
147
+ cursor_col: this.cursorCol,
148
+ rows: this.rows,
149
+ cols: this.cols,
150
+ fields,
151
+ screen_signature: hash,
152
+ timestamp: new Date().toISOString(),
153
+ };
154
+ }
155
+ }
@@ -0,0 +1,45 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface VTConnectionOptions {
3
+ terminalType?: string;
4
+ rows?: number;
5
+ cols?: number;
6
+ }
7
+ /**
8
+ * Manages a TCP/Telnet connection for VT terminal sessions.
9
+ *
10
+ * Unlike TN5250/TN3270, VT data is a continuous byte stream — there is no
11
+ * IAC EOR record framing. Telnet IAC commands are handled inline and stripped
12
+ * from the data stream; all remaining bytes are emitted as 'data' events.
13
+ */
14
+ export declare class VTConnection extends EventEmitter {
15
+ private socket;
16
+ private host;
17
+ private port;
18
+ private connected;
19
+ private recvBuffer;
20
+ private terminalType;
21
+ private rows;
22
+ private cols;
23
+ get isConnected(): boolean;
24
+ get remoteHost(): string;
25
+ get remotePort(): number;
26
+ connect(host: string, port: number, options?: VTConnectionOptions): Promise<void>;
27
+ disconnect(): void;
28
+ /** Send raw bytes over the socket */
29
+ sendRaw(data: Buffer): void;
30
+ private cleanup;
31
+ private onData;
32
+ /**
33
+ * Process the receive buffer. Strip out Telnet IAC commands and emit
34
+ * the remaining application data as 'data' events.
35
+ */
36
+ private processBuffer;
37
+ /** Find IAC SE after position `start` (which points to IAC SB) */
38
+ private findSubnegEnd;
39
+ private handleNegotiation;
40
+ private handleSubnegotiation;
41
+ private sendTerminalType;
42
+ /** Send NAWS (Negotiate About Window Size) subnegotiation */
43
+ private sendNAWS;
44
+ private sendTelnet;
45
+ }
@@ -0,0 +1,229 @@
1
+ import * as net from 'net';
2
+ import { EventEmitter } from 'events';
3
+ import { TELNET, DEFAULT_TERMINAL_TYPE, DEFAULT_ROWS, DEFAULT_COLS } from './constants.js';
4
+ /**
5
+ * Manages a TCP/Telnet connection for VT terminal sessions.
6
+ *
7
+ * Unlike TN5250/TN3270, VT data is a continuous byte stream — there is no
8
+ * IAC EOR record framing. Telnet IAC commands are handled inline and stripped
9
+ * from the data stream; all remaining bytes are emitted as 'data' events.
10
+ */
11
+ export class VTConnection extends EventEmitter {
12
+ socket = null;
13
+ host = '';
14
+ port = 23;
15
+ connected = false;
16
+ recvBuffer = Buffer.alloc(0);
17
+ terminalType = DEFAULT_TERMINAL_TYPE;
18
+ rows = DEFAULT_ROWS;
19
+ cols = DEFAULT_COLS;
20
+ get isConnected() {
21
+ return this.connected;
22
+ }
23
+ get remoteHost() {
24
+ return this.host;
25
+ }
26
+ get remotePort() {
27
+ return this.port;
28
+ }
29
+ connect(host, port, options) {
30
+ return new Promise((resolve, reject) => {
31
+ if (this.socket) {
32
+ this.disconnect();
33
+ }
34
+ this.host = host;
35
+ this.port = port;
36
+ this.recvBuffer = Buffer.alloc(0);
37
+ if (options?.terminalType)
38
+ this.terminalType = options.terminalType;
39
+ if (options?.rows)
40
+ this.rows = options.rows;
41
+ if (options?.cols)
42
+ this.cols = options.cols;
43
+ this.socket = new net.Socket();
44
+ this.socket.setTimeout(30000);
45
+ const onError = (err) => {
46
+ this.cleanup();
47
+ reject(err);
48
+ };
49
+ this.socket.once('error', onError);
50
+ this.socket.connect(port, host, () => {
51
+ this.connected = true;
52
+ this.socket.removeListener('error', onError);
53
+ this.socket.on('error', (err) => {
54
+ this.emit('error', err);
55
+ this.cleanup();
56
+ });
57
+ this.socket.on('close', () => {
58
+ this.cleanup();
59
+ this.emit('disconnected');
60
+ });
61
+ this.socket.on('timeout', () => {
62
+ this.emit('error', new Error('Connection timeout'));
63
+ });
64
+ this.socket.on('data', (data) => this.onData(data));
65
+ this.emit('connected');
66
+ resolve();
67
+ });
68
+ });
69
+ }
70
+ disconnect() {
71
+ if (this.socket) {
72
+ this.socket.destroy();
73
+ this.cleanup();
74
+ }
75
+ }
76
+ /** Send raw bytes over the socket */
77
+ sendRaw(data) {
78
+ if (this.socket && this.connected) {
79
+ this.socket.write(data);
80
+ }
81
+ }
82
+ cleanup() {
83
+ this.connected = false;
84
+ if (this.socket) {
85
+ this.socket.removeAllListeners();
86
+ this.socket.destroy();
87
+ this.socket = null;
88
+ }
89
+ }
90
+ onData(data) {
91
+ this.recvBuffer = Buffer.concat([this.recvBuffer, data]);
92
+ this.processBuffer();
93
+ }
94
+ /**
95
+ * Process the receive buffer. Strip out Telnet IAC commands and emit
96
+ * the remaining application data as 'data' events.
97
+ */
98
+ processBuffer() {
99
+ const appData = [];
100
+ let i = 0;
101
+ while (i < this.recvBuffer.length) {
102
+ const byte = this.recvBuffer[i];
103
+ if (byte === TELNET.IAC) {
104
+ if (i + 1 >= this.recvBuffer.length) {
105
+ // Incomplete IAC sequence — keep remainder for next chunk
106
+ break;
107
+ }
108
+ const cmd = this.recvBuffer[i + 1];
109
+ // IAC IAC = escaped 0xFF literal byte
110
+ if (cmd === TELNET.IAC) {
111
+ appData.push(0xff);
112
+ i += 2;
113
+ continue;
114
+ }
115
+ // Subnegotiation: IAC SB ... IAC SE
116
+ if (cmd === TELNET.SB) {
117
+ const seIdx = this.findSubnegEnd(i);
118
+ if (seIdx === -1)
119
+ break; // Wait for more data
120
+ const subData = this.recvBuffer.subarray(i + 2, seIdx);
121
+ this.handleSubnegotiation(subData);
122
+ i = seIdx + 2; // skip past IAC SE
123
+ continue;
124
+ }
125
+ // DO/DONT/WILL/WONT: 3-byte commands
126
+ if (cmd === TELNET.DO || cmd === TELNET.DONT || cmd === TELNET.WILL || cmd === TELNET.WONT) {
127
+ if (i + 2 >= this.recvBuffer.length)
128
+ break; // Wait for option byte
129
+ const option = this.recvBuffer[i + 2];
130
+ this.handleNegotiation(cmd, option);
131
+ i += 3;
132
+ continue;
133
+ }
134
+ // Other 2-byte IAC commands (GA, NOP, etc.) — skip
135
+ i += 2;
136
+ continue;
137
+ }
138
+ // Regular data byte
139
+ appData.push(byte);
140
+ i++;
141
+ }
142
+ // Keep unprocessed bytes
143
+ this.recvBuffer = this.recvBuffer.subarray(i);
144
+ // Emit application data
145
+ if (appData.length > 0) {
146
+ this.emit('data', Buffer.from(appData));
147
+ }
148
+ }
149
+ /** Find IAC SE after position `start` (which points to IAC SB) */
150
+ findSubnegEnd(start) {
151
+ for (let j = start + 2; j < this.recvBuffer.length - 1; j++) {
152
+ if (this.recvBuffer[j] === TELNET.IAC && this.recvBuffer[j + 1] === TELNET.SE) {
153
+ return j;
154
+ }
155
+ }
156
+ return -1;
157
+ }
158
+ handleNegotiation(cmd, option) {
159
+ switch (cmd) {
160
+ case TELNET.DO:
161
+ if (option === TELNET.OPT_TTYPE ||
162
+ option === TELNET.OPT_NAWS ||
163
+ option === TELNET.OPT_BINARY ||
164
+ option === TELNET.OPT_SGA) {
165
+ this.sendTelnet(TELNET.WILL, option);
166
+ // After agreeing to NAWS, immediately send window size
167
+ if (option === TELNET.OPT_NAWS) {
168
+ this.sendNAWS();
169
+ }
170
+ }
171
+ else {
172
+ this.sendTelnet(TELNET.WONT, option);
173
+ }
174
+ break;
175
+ case TELNET.WILL:
176
+ if (option === TELNET.OPT_ECHO ||
177
+ option === TELNET.OPT_SGA ||
178
+ option === TELNET.OPT_BINARY) {
179
+ this.sendTelnet(TELNET.DO, option);
180
+ }
181
+ else {
182
+ this.sendTelnet(TELNET.DONT, option);
183
+ }
184
+ break;
185
+ case TELNET.DONT:
186
+ this.sendTelnet(TELNET.WONT, option);
187
+ break;
188
+ case TELNET.WONT:
189
+ this.sendTelnet(TELNET.DONT, option);
190
+ break;
191
+ }
192
+ }
193
+ handleSubnegotiation(data) {
194
+ if (data.length === 0)
195
+ return;
196
+ const option = data[0];
197
+ if (option === TELNET.OPT_TTYPE && data.length >= 2 && data[1] === TELNET.TTYPE_SEND) {
198
+ this.sendTerminalType();
199
+ }
200
+ }
201
+ sendTerminalType() {
202
+ const typeStr = this.terminalType;
203
+ const buf = Buffer.alloc(4 + typeStr.length + 2);
204
+ let i = 0;
205
+ buf[i++] = TELNET.IAC;
206
+ buf[i++] = TELNET.SB;
207
+ buf[i++] = TELNET.OPT_TTYPE;
208
+ buf[i++] = TELNET.TTYPE_IS;
209
+ for (let j = 0; j < typeStr.length; j++) {
210
+ buf[i++] = typeStr.charCodeAt(j);
211
+ }
212
+ buf[i++] = TELNET.IAC;
213
+ buf[i++] = TELNET.SE;
214
+ this.sendRaw(buf);
215
+ }
216
+ /** Send NAWS (Negotiate About Window Size) subnegotiation */
217
+ sendNAWS() {
218
+ const buf = Buffer.from([
219
+ TELNET.IAC, TELNET.SB, TELNET.OPT_NAWS,
220
+ (this.cols >> 8) & 0xff, this.cols & 0xff,
221
+ (this.rows >> 8) & 0xff, this.rows & 0xff,
222
+ TELNET.IAC, TELNET.SE,
223
+ ]);
224
+ this.sendRaw(buf);
225
+ }
226
+ sendTelnet(cmd, option) {
227
+ this.sendRaw(Buffer.from([TELNET.IAC, cmd, option]));
228
+ }
229
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * VT100/VT220/VT320 terminal constants and escape sequences.
3
+ */
4
+ export declare const ESC = 27;
5
+ export declare const CSI_CHAR = 91;
6
+ export declare const CSI = "\u001B[";
7
+ export declare const NUL = 0;
8
+ export declare const BEL = 7;
9
+ export declare const BS = 8;
10
+ export declare const HT = 9;
11
+ export declare const LF = 10;
12
+ export declare const VT = 11;
13
+ export declare const FF = 12;
14
+ export declare const CR = 13;
15
+ export declare const SO = 14;
16
+ export declare const SI = 15;
17
+ export declare const DEL = 127;
18
+ export declare const TELNET: {
19
+ readonly IAC: 255;
20
+ readonly DONT: 254;
21
+ readonly DO: 253;
22
+ readonly WONT: 252;
23
+ readonly WILL: 251;
24
+ readonly SB: 250;
25
+ readonly SE: 240;
26
+ readonly GA: 249;
27
+ readonly NOP: 241;
28
+ readonly OPT_BINARY: 0;
29
+ readonly OPT_ECHO: 1;
30
+ readonly OPT_SGA: 3;
31
+ readonly OPT_TTYPE: 24;
32
+ readonly OPT_NAWS: 31;
33
+ readonly TTYPE_IS: 0;
34
+ readonly TTYPE_SEND: 1;
35
+ };
36
+ export declare const DEFAULT_ROWS = 24;
37
+ export declare const DEFAULT_COLS = 80;
38
+ export declare const TERMINAL_TYPES: {
39
+ readonly VT220: "VT220";
40
+ readonly VT320: "VT320";
41
+ readonly XTERM: "xterm";
42
+ };
43
+ export declare const DEFAULT_TERMINAL_TYPE: "VT220";
44
+ export declare const VT_KEYS: Record<string, string>;
45
+ export declare const SGR: {
46
+ readonly RESET: 0;
47
+ readonly BOLD: 1;
48
+ readonly DIM: 2;
49
+ readonly ITALIC: 3;
50
+ readonly UNDERLINE: 4;
51
+ readonly BLINK: 5;
52
+ readonly RAPID_BLINK: 6;
53
+ readonly REVERSE: 7;
54
+ readonly HIDDEN: 8;
55
+ readonly STRIKETHROUGH: 9;
56
+ readonly NORMAL_INTENSITY: 22;
57
+ readonly NO_ITALIC: 23;
58
+ readonly NO_UNDERLINE: 24;
59
+ readonly NO_BLINK: 25;
60
+ readonly NO_REVERSE: 27;
61
+ readonly NO_HIDDEN: 28;
62
+ readonly NO_STRIKETHROUGH: 29;
63
+ readonly FG_BLACK: 30;
64
+ readonly FG_RED: 31;
65
+ readonly FG_GREEN: 32;
66
+ readonly FG_YELLOW: 33;
67
+ readonly FG_BLUE: 34;
68
+ readonly FG_MAGENTA: 35;
69
+ readonly FG_CYAN: 36;
70
+ readonly FG_WHITE: 37;
71
+ readonly FG_DEFAULT: 39;
72
+ readonly BG_BLACK: 40;
73
+ readonly BG_RED: 41;
74
+ readonly BG_GREEN: 42;
75
+ readonly BG_YELLOW: 43;
76
+ readonly BG_BLUE: 44;
77
+ readonly BG_MAGENTA: 45;
78
+ readonly BG_CYAN: 46;
79
+ readonly BG_WHITE: 47;
80
+ readonly BG_DEFAULT: 49;
81
+ readonly FG_BRIGHT_BLACK: 90;
82
+ readonly FG_BRIGHT_RED: 91;
83
+ readonly FG_BRIGHT_GREEN: 92;
84
+ readonly FG_BRIGHT_YELLOW: 93;
85
+ readonly FG_BRIGHT_BLUE: 94;
86
+ readonly FG_BRIGHT_MAGENTA: 95;
87
+ readonly FG_BRIGHT_CYAN: 96;
88
+ readonly FG_BRIGHT_WHITE: 97;
89
+ readonly BG_BRIGHT_BLACK: 100;
90
+ readonly BG_BRIGHT_RED: 101;
91
+ readonly BG_BRIGHT_GREEN: 102;
92
+ readonly BG_BRIGHT_YELLOW: 103;
93
+ readonly BG_BRIGHT_BLUE: 104;
94
+ readonly BG_BRIGHT_MAGENTA: 105;
95
+ readonly BG_BRIGHT_CYAN: 106;
96
+ readonly BG_BRIGHT_WHITE: 107;
97
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * VT100/VT220/VT320 terminal constants and escape sequences.
3
+ */
4
+ // ---------------------------------------------------------------------------
5
+ // Control characters
6
+ // ---------------------------------------------------------------------------
7
+ export const ESC = 0x1b;
8
+ export const CSI_CHAR = 0x5b; // '['
9
+ export const CSI = '\x1b['; // Control Sequence Introducer
10
+ export const NUL = 0x00;
11
+ export const BEL = 0x07;
12
+ export const BS = 0x08;
13
+ export const HT = 0x09;
14
+ export const LF = 0x0a;
15
+ export const VT = 0x0b;
16
+ export const FF = 0x0c;
17
+ export const CR = 0x0d;
18
+ export const SO = 0x0e; // Shift Out (G1 charset)
19
+ export const SI = 0x0f; // Shift In (G0 charset)
20
+ export const DEL = 0x7f;
21
+ // ---------------------------------------------------------------------------
22
+ // Telnet constants (standard RFC 854 / RFC 855)
23
+ // ---------------------------------------------------------------------------
24
+ export const TELNET = {
25
+ IAC: 0xff,
26
+ DONT: 0xfe,
27
+ DO: 0xfd,
28
+ WONT: 0xfc,
29
+ WILL: 0xfb,
30
+ SB: 0xfa,
31
+ SE: 0xf0,
32
+ GA: 0xf9,
33
+ NOP: 0xf1,
34
+ // Options
35
+ OPT_BINARY: 0x00,
36
+ OPT_ECHO: 0x01,
37
+ OPT_SGA: 0x03, // Suppress Go Ahead
38
+ OPT_TTYPE: 0x18, // Terminal Type
39
+ OPT_NAWS: 0x1f, // Negotiate About Window Size
40
+ // Subnegotiation
41
+ TTYPE_IS: 0x00,
42
+ TTYPE_SEND: 0x01,
43
+ };
44
+ // ---------------------------------------------------------------------------
45
+ // Screen defaults
46
+ // ---------------------------------------------------------------------------
47
+ export const DEFAULT_ROWS = 24;
48
+ export const DEFAULT_COLS = 80;
49
+ // ---------------------------------------------------------------------------
50
+ // Terminal type strings
51
+ // ---------------------------------------------------------------------------
52
+ export const TERMINAL_TYPES = {
53
+ VT220: 'VT220',
54
+ VT320: 'VT320',
55
+ XTERM: 'xterm',
56
+ };
57
+ export const DEFAULT_TERMINAL_TYPE = TERMINAL_TYPES.VT220;
58
+ // ---------------------------------------------------------------------------
59
+ // VT220 function key escape sequences (sent by client)
60
+ // ---------------------------------------------------------------------------
61
+ export const VT_KEYS = {
62
+ // F1-F5 (VT220 sends SS3 sequences for F1-F4)
63
+ F1: '\x1bOP',
64
+ F2: '\x1bOQ',
65
+ F3: '\x1bOR',
66
+ F4: '\x1bOS',
67
+ F5: '\x1b[15~',
68
+ // F6-F12
69
+ F6: '\x1b[17~',
70
+ F7: '\x1b[18~',
71
+ F8: '\x1b[19~',
72
+ F9: '\x1b[20~',
73
+ F10: '\x1b[21~',
74
+ F11: '\x1b[23~',
75
+ F12: '\x1b[24~',
76
+ // F13-F24 (shifted function keys on VT220)
77
+ F13: '\x1b[25~',
78
+ F14: '\x1b[26~',
79
+ F15: '\x1b[28~',
80
+ F16: '\x1b[29~',
81
+ F17: '\x1b[31~',
82
+ F18: '\x1b[32~',
83
+ F19: '\x1b[33~',
84
+ F20: '\x1b[34~',
85
+ // Arrow keys
86
+ UP: '\x1b[A',
87
+ DOWN: '\x1b[B',
88
+ RIGHT: '\x1b[C',
89
+ LEFT: '\x1b[D',
90
+ // Editing keys
91
+ HOME: '\x1b[1~',
92
+ INSERT: '\x1b[2~',
93
+ DELETE: '\x1b[3~',
94
+ END: '\x1b[4~',
95
+ PAGEUP: '\x1b[5~',
96
+ PAGEDOWN: '\x1b[6~',
97
+ // Special keys
98
+ ENTER: '\r',
99
+ TAB: '\t',
100
+ BACKSPACE: '\x7f',
101
+ ESCAPE: '\x1b',
102
+ };
103
+ // ---------------------------------------------------------------------------
104
+ // SGR (Select Graphic Rendition) attribute codes
105
+ // ---------------------------------------------------------------------------
106
+ export const SGR = {
107
+ RESET: 0,
108
+ BOLD: 1,
109
+ DIM: 2,
110
+ ITALIC: 3,
111
+ UNDERLINE: 4,
112
+ BLINK: 5,
113
+ RAPID_BLINK: 6,
114
+ REVERSE: 7,
115
+ HIDDEN: 8,
116
+ STRIKETHROUGH: 9,
117
+ // Turn off attributes
118
+ NORMAL_INTENSITY: 22,
119
+ NO_ITALIC: 23,
120
+ NO_UNDERLINE: 24,
121
+ NO_BLINK: 25,
122
+ NO_REVERSE: 27,
123
+ NO_HIDDEN: 28,
124
+ NO_STRIKETHROUGH: 29,
125
+ // Foreground colors
126
+ FG_BLACK: 30,
127
+ FG_RED: 31,
128
+ FG_GREEN: 32,
129
+ FG_YELLOW: 33,
130
+ FG_BLUE: 34,
131
+ FG_MAGENTA: 35,
132
+ FG_CYAN: 36,
133
+ FG_WHITE: 37,
134
+ FG_DEFAULT: 39,
135
+ // Background colors
136
+ BG_BLACK: 40,
137
+ BG_RED: 41,
138
+ BG_GREEN: 42,
139
+ BG_YELLOW: 43,
140
+ BG_BLUE: 44,
141
+ BG_MAGENTA: 45,
142
+ BG_CYAN: 46,
143
+ BG_WHITE: 47,
144
+ BG_DEFAULT: 49,
145
+ // Bright foreground colors
146
+ FG_BRIGHT_BLACK: 90,
147
+ FG_BRIGHT_RED: 91,
148
+ FG_BRIGHT_GREEN: 92,
149
+ FG_BRIGHT_YELLOW: 93,
150
+ FG_BRIGHT_BLUE: 94,
151
+ FG_BRIGHT_MAGENTA: 95,
152
+ FG_BRIGHT_CYAN: 96,
153
+ FG_BRIGHT_WHITE: 97,
154
+ // Bright background colors
155
+ BG_BRIGHT_BLACK: 100,
156
+ BG_BRIGHT_RED: 101,
157
+ BG_BRIGHT_GREEN: 102,
158
+ BG_BRIGHT_YELLOW: 103,
159
+ BG_BRIGHT_BLUE: 104,
160
+ BG_BRIGHT_MAGENTA: 105,
161
+ BG_BRIGHT_CYAN: 106,
162
+ BG_BRIGHT_WHITE: 107,
163
+ };