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,231 @@
1
+ import { Router } from 'express';
2
+ import { createHash } from 'crypto';
3
+ const router = Router();
4
+ const state = {
5
+ currentScreen: 'signon',
6
+ cursorRow: 6,
7
+ cursorCol: 53,
8
+ username: '',
9
+ password: '',
10
+ connected: false,
11
+ };
12
+ function pad(str, len) {
13
+ return str.padEnd(len).substring(0, len);
14
+ }
15
+ function signonScreen() {
16
+ const lines = [
17
+ pad(' Sign On', 80),
18
+ pad(' System . . : PUB400', 80),
19
+ pad(' Subsystem . : QINTER', 80),
20
+ pad(' Display . . : QPADEV0001', 80),
21
+ pad('', 80),
22
+ pad('', 80),
23
+ pad(' User . . . . . . . . . . . . . . ' + pad(state.username, 10), 80),
24
+ pad(' Password . . . . . . . . . . . . ' + pad('', 10), 80),
25
+ pad(' Program/procedure . . . . . . . . ' + pad('', 10), 80),
26
+ pad(' Menu . . . . . . . . . . . . . . ' + pad('', 10), 80),
27
+ pad(' Current library . . . . . . . . . ' + pad('', 10), 80),
28
+ pad('', 80),
29
+ pad('', 80),
30
+ pad('', 80),
31
+ pad('', 80),
32
+ pad('', 80),
33
+ pad('', 80),
34
+ pad('', 80),
35
+ pad('', 80),
36
+ pad('', 80),
37
+ pad('', 80),
38
+ pad('', 80),
39
+ pad(' (C) COPYRIGHT IBM CORP. 1980, 2024.', 80),
40
+ pad('', 80),
41
+ ];
42
+ return lines.join('\n');
43
+ }
44
+ function menuScreen() {
45
+ const lines = [
46
+ pad(' MAIN AS/400 Main Menu ', 80),
47
+ pad(' System: PUB400', 80),
48
+ pad(' Select one of the following:', 80),
49
+ pad('', 80),
50
+ pad(' 1. User tasks', 80),
51
+ pad(' 2. Office tasks', 80),
52
+ pad(' 3. General system tasks', 80),
53
+ pad(' 4. Files, libraries, and folders', 80),
54
+ pad(' 5. Programming', 80),
55
+ pad(' 6. Communications', 80),
56
+ pad(' 7. Define or change the system', 80),
57
+ pad(' 8. Problem handling', 80),
58
+ pad(' 9. Display a menu', 80),
59
+ pad(' 10. Information Assistant options', 80),
60
+ pad(' 11. Client Access/400 tasks', 80),
61
+ pad('', 80),
62
+ pad(' 90. Sign off', 80),
63
+ pad('', 80),
64
+ pad('', 80),
65
+ pad(' Selection or command', 80),
66
+ pad(' ===> ' + pad('', 74), 80),
67
+ pad('', 80),
68
+ pad(' F3=Exit F4=Prompt F9=Retrieve F12=Cancel', 80),
69
+ pad(' F13=Information Assistant F23=Set initial menu', 80),
70
+ ];
71
+ return lines.join('\n');
72
+ }
73
+ function getScreenData() {
74
+ const content = state.currentScreen === 'signon' ? signonScreen() : menuScreen();
75
+ const hash = createHash('md5').update(content).digest('hex').substring(0, 12);
76
+ const fields = state.currentScreen === 'signon'
77
+ ? [
78
+ { row: 6, col: 53, length: 10, is_input: true, is_protected: false },
79
+ { row: 7, col: 53, length: 10, is_input: true, is_protected: false, is_reverse: true },
80
+ { row: 8, col: 53, length: 10, is_input: true, is_protected: false },
81
+ { row: 9, col: 53, length: 10, is_input: true, is_protected: false },
82
+ { row: 10, col: 53, length: 10, is_input: true, is_protected: false },
83
+ ]
84
+ : [
85
+ { row: 20, col: 7, length: 73, is_input: true, is_protected: false },
86
+ ];
87
+ return {
88
+ content,
89
+ cursor_row: state.cursorRow,
90
+ cursor_col: state.cursorCol,
91
+ rows: 24,
92
+ cols: 80,
93
+ fields,
94
+ screen_signature: hash,
95
+ timestamp: new Date().toISOString(),
96
+ };
97
+ }
98
+ // POST /connect
99
+ router.post('/connect', (_req, res) => {
100
+ state.connected = true;
101
+ state.currentScreen = 'signon';
102
+ state.cursorRow = 6;
103
+ state.cursorCol = 53;
104
+ state.username = '';
105
+ state.password = '';
106
+ const screen = getScreenData();
107
+ res.json({
108
+ success: true,
109
+ sessionId: 'mock-session',
110
+ cursor_row: screen.cursor_row,
111
+ cursor_col: screen.cursor_col,
112
+ content: screen.content,
113
+ screen_signature: screen.screen_signature,
114
+ });
115
+ });
116
+ // POST /disconnect
117
+ router.post('/disconnect', (_req, res) => {
118
+ state.connected = false;
119
+ state.currentScreen = 'signon';
120
+ res.json({ success: true });
121
+ });
122
+ // POST /reconnect
123
+ router.post('/reconnect', (_req, res) => {
124
+ state.connected = true;
125
+ state.currentScreen = 'signon';
126
+ state.cursorRow = 6;
127
+ state.cursorCol = 53;
128
+ state.username = '';
129
+ state.password = '';
130
+ const screen = getScreenData();
131
+ res.json({
132
+ success: true,
133
+ cursor_row: screen.cursor_row,
134
+ cursor_col: screen.cursor_col,
135
+ content: screen.content,
136
+ screen_signature: screen.screen_signature,
137
+ });
138
+ });
139
+ // GET /status
140
+ router.get('/status', (_req, res) => {
141
+ res.json({
142
+ connected: state.connected,
143
+ status: state.connected
144
+ ? (state.currentScreen === 'menu' ? 'authenticated' : 'connected')
145
+ : 'disconnected',
146
+ host: state.connected ? 'mock-ibmi' : undefined,
147
+ username: state.currentScreen === 'menu' ? (state.username || 'MOCKUSER') : undefined,
148
+ });
149
+ });
150
+ // GET /screen
151
+ router.get('/screen', (_req, res) => {
152
+ if (!state.connected) {
153
+ return res.status(503).json(null);
154
+ }
155
+ res.json(getScreenData());
156
+ });
157
+ // POST /send-text
158
+ router.post('/send-text', (req, res) => {
159
+ if (!state.connected) {
160
+ return res.status(404).json({ success: false, error: 'Not connected' });
161
+ }
162
+ const { text } = req.body || {};
163
+ if (typeof text !== 'string') {
164
+ return res.status(400).json({ success: false, error: 'text is required' });
165
+ }
166
+ // Insert text at cursor
167
+ if (state.currentScreen === 'signon') {
168
+ if (state.cursorRow === 6) {
169
+ state.username = (state.username + text).substring(0, 10);
170
+ state.cursorCol = 53 + state.username.length;
171
+ }
172
+ else if (state.cursorRow === 7) {
173
+ state.password = (state.password + text).substring(0, 10);
174
+ state.cursorCol = 53 + state.password.length;
175
+ }
176
+ }
177
+ const screen = getScreenData();
178
+ res.json({
179
+ success: true,
180
+ cursor_row: state.cursorRow,
181
+ cursor_col: state.cursorCol,
182
+ content: screen.content,
183
+ screen_signature: screen.screen_signature,
184
+ });
185
+ });
186
+ // POST /send-key
187
+ router.post('/send-key', (req, res) => {
188
+ if (!state.connected) {
189
+ return res.status(404).json({ success: false, error: 'Not connected' });
190
+ }
191
+ const { key } = req.body || {};
192
+ if (typeof key !== 'string') {
193
+ return res.status(400).json({ success: false, error: 'key is required' });
194
+ }
195
+ if (key === 'Enter') {
196
+ if (state.currentScreen === 'signon') {
197
+ // Transition to menu
198
+ state.currentScreen = 'menu';
199
+ state.cursorRow = 20;
200
+ state.cursorCol = 7;
201
+ }
202
+ }
203
+ else if (key === 'Tab') {
204
+ // Move to next field
205
+ if (state.currentScreen === 'signon') {
206
+ if (state.cursorRow < 10) {
207
+ state.cursorRow++;
208
+ state.cursorCol = 53;
209
+ }
210
+ }
211
+ }
212
+ else if (key === 'F3') {
213
+ // Go back / sign off
214
+ if (state.currentScreen === 'menu') {
215
+ state.currentScreen = 'signon';
216
+ state.cursorRow = 6;
217
+ state.cursorCol = 53;
218
+ state.username = '';
219
+ state.password = '';
220
+ }
221
+ }
222
+ const screen = getScreenData();
223
+ res.json({
224
+ success: true,
225
+ cursor_row: state.cursorRow,
226
+ cursor_col: state.cursorCol,
227
+ content: screen.content,
228
+ screen_signature: screen.screen_signature,
229
+ });
230
+ });
231
+ export default router;
@@ -0,0 +1,29 @@
1
+ import { ProtocolHandler, ScreenData, ProtocolOptions, ProtocolType } from './types.js';
2
+ import { HP6530Connection } from '../hp6530/connection.js';
3
+ import { HP6530Screen } from '../hp6530/screen.js';
4
+ import { HP6530Parser } from '../hp6530/parser.js';
5
+ import { HP6530Encoder } from '../hp6530/encoder.js';
6
+ /**
7
+ * HP 6530 protocol handler — implements the ProtocolHandler interface
8
+ * for HP NonStop (Tandem) terminal connections.
9
+ *
10
+ * HP 6530 terminals are block-mode ASCII terminals used with HP NonStop
11
+ * systems in payment processing, stock exchanges, and telecom.
12
+ */
13
+ export declare class HP6530Handler extends ProtocolHandler {
14
+ readonly protocol: ProtocolType;
15
+ readonly connection: HP6530Connection;
16
+ readonly screen: HP6530Screen;
17
+ readonly parser: HP6530Parser;
18
+ readonly encoder: HP6530Encoder;
19
+ constructor();
20
+ get isConnected(): boolean;
21
+ connect(host: string, port: number, _options?: ProtocolOptions): Promise<void>;
22
+ disconnect(): void;
23
+ getScreenData(): ScreenData;
24
+ sendText(text: string): boolean;
25
+ sendKey(keyName: string): boolean;
26
+ sendRaw(data: Buffer): void;
27
+ destroy(): void;
28
+ private onData;
29
+ }
@@ -0,0 +1,64 @@
1
+ import { ProtocolHandler } from './types.js';
2
+ import { HP6530Connection } from '../hp6530/connection.js';
3
+ import { HP6530Screen } from '../hp6530/screen.js';
4
+ import { HP6530Parser } from '../hp6530/parser.js';
5
+ import { HP6530Encoder } from '../hp6530/encoder.js';
6
+ /**
7
+ * HP 6530 protocol handler — implements the ProtocolHandler interface
8
+ * for HP NonStop (Tandem) terminal connections.
9
+ *
10
+ * HP 6530 terminals are block-mode ASCII terminals used with HP NonStop
11
+ * systems in payment processing, stock exchanges, and telecom.
12
+ */
13
+ export class HP6530Handler extends ProtocolHandler {
14
+ protocol = 'hp6530';
15
+ connection;
16
+ screen;
17
+ parser;
18
+ encoder;
19
+ constructor() {
20
+ super();
21
+ this.screen = new HP6530Screen();
22
+ this.connection = new HP6530Connection();
23
+ this.parser = new HP6530Parser(this.screen);
24
+ this.encoder = new HP6530Encoder(this.screen);
25
+ this.connection.on('data', (data) => this.onData(data));
26
+ this.connection.on('disconnected', () => this.emit('disconnected'));
27
+ this.connection.on('error', (err) => this.emit('error', err));
28
+ }
29
+ get isConnected() {
30
+ return this.connection.isConnected;
31
+ }
32
+ async connect(host, port, _options) {
33
+ await this.connection.connect(host, port);
34
+ }
35
+ disconnect() {
36
+ this.connection.disconnect();
37
+ }
38
+ getScreenData() {
39
+ return this.screen.toScreenData();
40
+ }
41
+ sendText(text) {
42
+ return this.encoder.insertText(text);
43
+ }
44
+ sendKey(keyName) {
45
+ const response = this.encoder.buildKeyResponse(keyName);
46
+ if (!response)
47
+ return false;
48
+ this.connection.sendRaw(response);
49
+ return true;
50
+ }
51
+ sendRaw(data) {
52
+ this.connection.sendRaw(data);
53
+ }
54
+ destroy() {
55
+ this.disconnect();
56
+ this.removeAllListeners();
57
+ }
58
+ onData(data) {
59
+ const modified = this.parser.parse(data);
60
+ if (modified) {
61
+ this.emit('screenChange', this.screen.toScreenData());
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,11 @@
1
+ export { ProtocolHandler, ProtocolType, ProtocolOptions, ScreenData } from './types.js';
2
+ export { TN5250Handler } from './tn5250-handler.js';
3
+ export { TN3270Handler } from './tn3270-handler.js';
4
+ export { VTHandler } from './vt-handler.js';
5
+ export { HP6530Handler } from './hp6530-handler.js';
6
+ import { ProtocolHandler, ProtocolType } from './types.js';
7
+ /**
8
+ * Create a protocol handler for the given protocol type.
9
+ * Throws if the protocol is not yet implemented.
10
+ */
11
+ export declare function createProtocolHandler(protocol?: ProtocolType): ProtocolHandler;
@@ -0,0 +1,27 @@
1
+ export { ProtocolHandler } from './types.js';
2
+ export { TN5250Handler } from './tn5250-handler.js';
3
+ export { TN3270Handler } from './tn3270-handler.js';
4
+ export { VTHandler } from './vt-handler.js';
5
+ export { HP6530Handler } from './hp6530-handler.js';
6
+ import { TN5250Handler } from './tn5250-handler.js';
7
+ import { TN3270Handler } from './tn3270-handler.js';
8
+ import { VTHandler } from './vt-handler.js';
9
+ import { HP6530Handler } from './hp6530-handler.js';
10
+ /**
11
+ * Create a protocol handler for the given protocol type.
12
+ * Throws if the protocol is not yet implemented.
13
+ */
14
+ export function createProtocolHandler(protocol = 'tn5250') {
15
+ switch (protocol) {
16
+ case 'tn5250':
17
+ return new TN5250Handler();
18
+ case 'tn3270':
19
+ return new TN3270Handler();
20
+ case 'vt':
21
+ return new VTHandler();
22
+ case 'hp6530':
23
+ return new HP6530Handler();
24
+ default:
25
+ throw new Error(`Protocol "${protocol}" is not yet implemented. Supported: tn5250, tn3270, vt, hp6530`);
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ import { ProtocolHandler, ScreenData, ProtocolOptions, ProtocolType } from './types.js';
2
+ import { TN3270Connection } from '../tn3270/connection.js';
3
+ import { ScreenBuffer3270 } from '../tn3270/screen.js';
4
+ import { TN3270Parser } from '../tn3270/parser.js';
5
+ import { TN3270Encoder } from '../tn3270/encoder.js';
6
+ /**
7
+ * TN3270 protocol handler — implements the ProtocolHandler interface
8
+ * for IBM z/OS (mainframe) 3270 terminal connections.
9
+ */
10
+ export declare class TN3270Handler extends ProtocolHandler {
11
+ readonly protocol: ProtocolType;
12
+ readonly connection: TN3270Connection;
13
+ readonly screen: ScreenBuffer3270;
14
+ readonly parser: TN3270Parser;
15
+ readonly encoder: TN3270Encoder;
16
+ constructor();
17
+ get isConnected(): boolean;
18
+ connect(host: string, port: number, _options?: ProtocolOptions): Promise<void>;
19
+ disconnect(): void;
20
+ getScreenData(): ScreenData;
21
+ sendText(text: string): boolean;
22
+ sendKey(keyName: string): boolean;
23
+ sendRaw(data: Buffer): void;
24
+ destroy(): void;
25
+ private onRecord;
26
+ }
@@ -0,0 +1,61 @@
1
+ import { ProtocolHandler } from './types.js';
2
+ import { TN3270Connection } from '../tn3270/connection.js';
3
+ import { ScreenBuffer3270 } from '../tn3270/screen.js';
4
+ import { TN3270Parser } from '../tn3270/parser.js';
5
+ import { TN3270Encoder } from '../tn3270/encoder.js';
6
+ /**
7
+ * TN3270 protocol handler — implements the ProtocolHandler interface
8
+ * for IBM z/OS (mainframe) 3270 terminal connections.
9
+ */
10
+ export class TN3270Handler extends ProtocolHandler {
11
+ protocol = 'tn3270';
12
+ connection;
13
+ screen;
14
+ parser;
15
+ encoder;
16
+ constructor() {
17
+ super();
18
+ this.screen = new ScreenBuffer3270();
19
+ this.connection = new TN3270Connection();
20
+ this.parser = new TN3270Parser(this.screen);
21
+ this.encoder = new TN3270Encoder(this.screen);
22
+ this.connection.on('data', (record) => this.onRecord(record));
23
+ this.connection.on('disconnected', () => this.emit('disconnected'));
24
+ this.connection.on('error', (err) => this.emit('error', err));
25
+ }
26
+ get isConnected() {
27
+ return this.connection.isConnected;
28
+ }
29
+ async connect(host, port, _options) {
30
+ await this.connection.connect(host, port);
31
+ }
32
+ disconnect() {
33
+ this.connection.disconnect();
34
+ }
35
+ getScreenData() {
36
+ return this.screen.toScreenData();
37
+ }
38
+ sendText(text) {
39
+ return this.encoder.insertText(text);
40
+ }
41
+ sendKey(keyName) {
42
+ const response = this.encoder.buildAidResponse(keyName);
43
+ if (!response)
44
+ return false;
45
+ this.connection.sendRaw(response);
46
+ return true;
47
+ }
48
+ sendRaw(data) {
49
+ this.connection.sendRaw(data);
50
+ }
51
+ destroy() {
52
+ this.disconnect();
53
+ this.removeAllListeners();
54
+ }
55
+ onRecord(record) {
56
+ const modified = this.parser.parseRecord(record);
57
+ if (modified) {
58
+ this.emit('screenChange', this.screen.toScreenData());
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,26 @@
1
+ import { ProtocolHandler, ScreenData, ProtocolOptions, ProtocolType } from './types.js';
2
+ import { TN5250Connection } from '../tn5250/connection.js';
3
+ import { ScreenBuffer } from '../tn5250/screen.js';
4
+ import { TN5250Parser } from '../tn5250/parser.js';
5
+ import { TN5250Encoder } from '../tn5250/encoder.js';
6
+ /**
7
+ * TN5250 protocol handler — implements the ProtocolHandler interface
8
+ * for IBM i (AS/400) TN5250 terminal connections.
9
+ */
10
+ export declare class TN5250Handler extends ProtocolHandler {
11
+ readonly protocol: ProtocolType;
12
+ readonly connection: TN5250Connection;
13
+ readonly screen: ScreenBuffer;
14
+ readonly parser: TN5250Parser;
15
+ readonly encoder: TN5250Encoder;
16
+ constructor();
17
+ get isConnected(): boolean;
18
+ connect(host: string, port: number, _options?: ProtocolOptions): Promise<void>;
19
+ disconnect(): void;
20
+ getScreenData(): ScreenData;
21
+ sendText(text: string): boolean;
22
+ sendKey(keyName: string): boolean;
23
+ sendRaw(data: Buffer): void;
24
+ destroy(): void;
25
+ private onRecord;
26
+ }
@@ -0,0 +1,62 @@
1
+ import { ProtocolHandler } from './types.js';
2
+ import { TN5250Connection } from '../tn5250/connection.js';
3
+ import { ScreenBuffer } from '../tn5250/screen.js';
4
+ import { TN5250Parser } from '../tn5250/parser.js';
5
+ import { TN5250Encoder } from '../tn5250/encoder.js';
6
+ /**
7
+ * TN5250 protocol handler — implements the ProtocolHandler interface
8
+ * for IBM i (AS/400) TN5250 terminal connections.
9
+ */
10
+ export class TN5250Handler extends ProtocolHandler {
11
+ protocol = 'tn5250';
12
+ connection;
13
+ screen;
14
+ parser;
15
+ encoder;
16
+ constructor() {
17
+ super();
18
+ this.screen = new ScreenBuffer();
19
+ this.connection = new TN5250Connection();
20
+ this.parser = new TN5250Parser(this.screen);
21
+ this.encoder = new TN5250Encoder(this.screen);
22
+ this.connection.on('data', (record) => this.onRecord(record));
23
+ this.connection.on('disconnected', () => this.emit('disconnected'));
24
+ this.connection.on('error', (err) => this.emit('error', err));
25
+ }
26
+ get isConnected() {
27
+ return this.connection.isConnected;
28
+ }
29
+ async connect(host, port, _options) {
30
+ await this.connection.connect(host, port);
31
+ }
32
+ disconnect() {
33
+ this.connection.disconnect();
34
+ }
35
+ getScreenData() {
36
+ return this.screen.toScreenData();
37
+ }
38
+ sendText(text) {
39
+ return this.encoder.insertText(text);
40
+ }
41
+ sendKey(keyName) {
42
+ const response = this.encoder.buildAidResponse(keyName);
43
+ if (!response)
44
+ return false;
45
+ this.connection.sendRaw(response);
46
+ return true;
47
+ }
48
+ sendRaw(data) {
49
+ this.connection.sendRaw(data);
50
+ }
51
+ destroy() {
52
+ this.disconnect();
53
+ this.removeAllListeners();
54
+ }
55
+ onRecord(record) {
56
+ const modified = this.parser.parseRecord(record);
57
+ if (modified) {
58
+ this.parser.calculateFieldLengths();
59
+ this.emit('screenChange', this.screen.toScreenData());
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,59 @@
1
+ import { EventEmitter } from 'events';
2
+ /**
3
+ * Screen data returned by protocol handlers.
4
+ * Protocol-agnostic representation of the terminal screen.
5
+ */
6
+ export interface ScreenData {
7
+ content: string;
8
+ cursor_row: number;
9
+ cursor_col: number;
10
+ rows: number;
11
+ cols: number;
12
+ fields: Array<{
13
+ row: number;
14
+ col: number;
15
+ length: number;
16
+ is_input: boolean;
17
+ is_protected: boolean;
18
+ is_highlighted?: boolean;
19
+ is_reverse?: boolean;
20
+ }>;
21
+ screen_signature: string;
22
+ timestamp: string;
23
+ }
24
+ export type ProtocolType = 'tn5250' | 'tn3270' | 'vt' | 'hp6530';
25
+ export interface ProtocolOptions {
26
+ /** Terminal type string for negotiation */
27
+ terminalType?: string;
28
+ /** Screen dimensions */
29
+ rows?: number;
30
+ cols?: number;
31
+ /** Protocol-specific options */
32
+ [key: string]: unknown;
33
+ }
34
+ /**
35
+ * Interface that all protocol handlers must implement.
36
+ * Each protocol (TN5250, TN3270, VT, etc.) provides its own implementation.
37
+ */
38
+ export declare abstract class ProtocolHandler extends EventEmitter {
39
+ abstract readonly protocol: ProtocolType;
40
+ /** Connect to a remote host */
41
+ abstract connect(host: string, port: number, options?: ProtocolOptions): Promise<void>;
42
+ /** Disconnect from the host */
43
+ abstract disconnect(): void;
44
+ /** Whether the connection is active */
45
+ abstract get isConnected(): boolean;
46
+ /** Get the current screen state */
47
+ abstract getScreenData(): ScreenData;
48
+ /** Send text input at the current cursor position */
49
+ abstract sendText(text: string): boolean;
50
+ /**
51
+ * Send a key action (ENTER, F1-F24, TAB, etc.).
52
+ * Returns raw bytes to send over the wire, or null if key is unknown.
53
+ */
54
+ abstract sendKey(keyName: string): boolean;
55
+ /** Send raw bytes over the connection */
56
+ abstract sendRaw(data: Buffer): void;
57
+ /** Clean up resources */
58
+ abstract destroy(): void;
59
+ }
@@ -0,0 +1,7 @@
1
+ import { EventEmitter } from 'events';
2
+ /**
3
+ * Interface that all protocol handlers must implement.
4
+ * Each protocol (TN5250, TN3270, VT, etc.) provides its own implementation.
5
+ */
6
+ export class ProtocolHandler extends EventEmitter {
7
+ }
@@ -0,0 +1,30 @@
1
+ import { ProtocolHandler, ScreenData, ProtocolOptions, ProtocolType } from './types.js';
2
+ import { VTConnection } from '../vt/connection.js';
3
+ import { VTScreenBuffer } from '../vt/screen.js';
4
+ import { VTParser } from '../vt/parser.js';
5
+ import { VTEncoder } from '../vt/encoder.js';
6
+ /**
7
+ * VT terminal protocol handler — implements the ProtocolHandler interface
8
+ * for VT100/VT220/VT320 terminal connections.
9
+ *
10
+ * VT terminals are stream-mode (character-at-a-time). Each keystroke is
11
+ * sent immediately; the host echoes characters back. Used by OpenVMS,
12
+ * Pick/MultiValue, Unix, and many other systems.
13
+ */
14
+ export declare class VTHandler extends ProtocolHandler {
15
+ readonly protocol: ProtocolType;
16
+ readonly connection: VTConnection;
17
+ readonly screen: VTScreenBuffer;
18
+ readonly parser: VTParser;
19
+ readonly encoder: VTEncoder;
20
+ constructor();
21
+ get isConnected(): boolean;
22
+ connect(host: string, port: number, _options?: ProtocolOptions): Promise<void>;
23
+ disconnect(): void;
24
+ getScreenData(): ScreenData;
25
+ sendText(text: string): boolean;
26
+ sendKey(keyName: string): boolean;
27
+ sendRaw(data: Buffer): void;
28
+ destroy(): void;
29
+ private onData;
30
+ }