bdy 1.7.46-dev

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 (96) hide show
  1. package/.eslintrc.yml +23 -0
  2. package/LICENSE +21 -0
  3. package/bin/cli.js +5 -0
  4. package/dockerfile +15 -0
  5. package/link.sh +3 -0
  6. package/package.json +39 -0
  7. package/src/agent/linux.js +127 -0
  8. package/src/agent/manager.js +404 -0
  9. package/src/agent/osx.js +150 -0
  10. package/src/agent/socket/tunnel.js +232 -0
  11. package/src/agent/socket.js +260 -0
  12. package/src/agent/system.js +205 -0
  13. package/src/agent/wait.js +20 -0
  14. package/src/agent/windows.js +168 -0
  15. package/src/agent.js +248 -0
  16. package/src/api/agent.js +95 -0
  17. package/src/api/buddy.js +131 -0
  18. package/src/api/socket.js +142 -0
  19. package/src/cfg.js +228 -0
  20. package/src/command/agent/disable.js +37 -0
  21. package/src/command/agent/enable.js +117 -0
  22. package/src/command/agent/restart.js +28 -0
  23. package/src/command/agent/run.js +16 -0
  24. package/src/command/agent/start.js +28 -0
  25. package/src/command/agent/status.js +45 -0
  26. package/src/command/agent/stop.js +28 -0
  27. package/src/command/agent/tunnel/http.js +47 -0
  28. package/src/command/agent/tunnel/list.js +27 -0
  29. package/src/command/agent/tunnel/start.js +38 -0
  30. package/src/command/agent/tunnel/status.js +32 -0
  31. package/src/command/agent/tunnel/stop.js +30 -0
  32. package/src/command/agent/tunnel/tcp.js +47 -0
  33. package/src/command/agent/tunnel/tls.js +47 -0
  34. package/src/command/agent/tunnel.js +21 -0
  35. package/src/command/agent/update.js +43 -0
  36. package/src/command/agent/version.js +23 -0
  37. package/src/command/agent.js +27 -0
  38. package/src/command/config/add/http.js +33 -0
  39. package/src/command/config/add/tcp.js +33 -0
  40. package/src/command/config/add/tls.js +33 -0
  41. package/src/command/config/add.js +13 -0
  42. package/src/command/config/get/region.js +13 -0
  43. package/src/command/config/get/timeout.js +13 -0
  44. package/src/command/config/get/token.js +13 -0
  45. package/src/command/config/get/tunnel.js +21 -0
  46. package/src/command/config/get/tunnels.js +13 -0
  47. package/src/command/config/get/whitelist.js +13 -0
  48. package/src/command/config/get.js +19 -0
  49. package/src/command/config/remove/tunnel.js +22 -0
  50. package/src/command/config/remove.js +9 -0
  51. package/src/command/config/set/region.js +19 -0
  52. package/src/command/config/set/timeout.js +22 -0
  53. package/src/command/config/set/token.js +18 -0
  54. package/src/command/config/set/whitelist.js +19 -0
  55. package/src/command/config/set.js +15 -0
  56. package/src/command/config.js +15 -0
  57. package/src/command/http.js +34 -0
  58. package/src/command/pre.js +47 -0
  59. package/src/command/start.js +31 -0
  60. package/src/command/tcp.js +32 -0
  61. package/src/command/tls.js +32 -0
  62. package/src/command/version.js +10 -0
  63. package/src/format.js +171 -0
  64. package/src/index.js +32 -0
  65. package/src/input.js +283 -0
  66. package/src/logger.js +87 -0
  67. package/src/output/interactive/tunnel.js +871 -0
  68. package/src/output/noninteractive/agent/tunnels.js +32 -0
  69. package/src/output/noninteractive/config/tunnel.js +52 -0
  70. package/src/output/noninteractive/config/tunnels.js +19 -0
  71. package/src/output/noninteractive/tunnel.js +79 -0
  72. package/src/output.js +136 -0
  73. package/src/server/cert.js +51 -0
  74. package/src/server/http1.js +79 -0
  75. package/src/server/http2.js +79 -0
  76. package/src/server/sftp.js +474 -0
  77. package/src/server/ssh.js +107 -0
  78. package/src/server/tls.js +41 -0
  79. package/src/ssh/client.js +196 -0
  80. package/src/texts.js +447 -0
  81. package/src/tunnel/agent.js +100 -0
  82. package/src/tunnel/compression.js +32 -0
  83. package/src/tunnel/dns.js +55 -0
  84. package/src/tunnel/html/404.html +129 -0
  85. package/src/tunnel/html/503.html +136 -0
  86. package/src/tunnel/html.js +32 -0
  87. package/src/tunnel/http/log.js +204 -0
  88. package/src/tunnel/http/serve.js +127 -0
  89. package/src/tunnel/http/stream.js +46 -0
  90. package/src/tunnel/http.js +406 -0
  91. package/src/tunnel/identification.js +95 -0
  92. package/src/tunnel/latency.js +63 -0
  93. package/src/tunnel/tcp.js +71 -0
  94. package/src/tunnel.js +696 -0
  95. package/src/utils.js +496 -0
  96. package/unlink.sh +3 -0
@@ -0,0 +1,871 @@
1
+ const {
2
+ getVersion,
3
+ TUNNEL_HTTP,
4
+ TUNNEL_OPEN,
5
+ TUNNEL_TLS
6
+ } = require('../../utils.js');
7
+ const TerminalKit = require('terminal-kit/lib/termkit-no-lazy-require');
8
+ const { isBinaryFileSync } = require('isbinaryfile');
9
+ const Format = require('../../format');
10
+ const { TUNNEL_EVENT_STOPPED } = require('../../utils');
11
+
12
+ const COLOR_CYAN = 6;
13
+ const COLOR_LIGHT_GRAY = 7;
14
+ const COLOR_RED = 9;
15
+ const COLOR_GREEN = 10;
16
+ const COLOR_WHITE = 15;
17
+
18
+ const ROW_LENGTH = 200;
19
+ const COLUMN_LENGTH = 30;
20
+ const REQUESTS_MAX = 10;
21
+ const REQUESTS_MAX_LENGTH = 80;
22
+ const REQUEST_INFO_MAX_LENGTH = 100;
23
+ const REQUEST_INFO_START_Y = 3;
24
+
25
+ const fillString = (str, len) => {
26
+ if (str.length > len) return str.substring(0, len);
27
+ const need = len - str.length;
28
+ for (let i = 0; i < need; i += 1) {
29
+ str += ' ';
30
+ }
31
+ return str;
32
+ };
33
+
34
+ const formatBytes = (len) => {
35
+ if (len < 1024) return `${len}B`;
36
+ len = Math.round(len / 1024);
37
+ if (len < 1024) return `${len}KB`;
38
+ len = Math.round(len / 1024);
39
+ if (len < 1024) return `${len}MB`;
40
+ len = Math.round(len / 1024);
41
+ return `${len}GB`;
42
+ };
43
+
44
+ class OutputInteractiveTunnel {
45
+ constructor(terminal, tunnel) {
46
+ this.terminal = terminal;
47
+ this.tunnel = tunnel;
48
+ this.viewPort = null;
49
+ this.sprites = {};
50
+ this.selectedRequest = null;
51
+ }
52
+
53
+ retry() {
54
+ if (this.selectedRequest && !this.selectedRequest.requestBody.tooLarge) {
55
+ this.tunnel.retryHttpLogRequest(this.selectedRequest);
56
+ }
57
+ }
58
+
59
+ draw() {
60
+ let y = 0;
61
+ y += this.updateSpriteHeader(y);
62
+ y += this.updateSpriteStatus(y);
63
+ y += this.updateSpriteDns(y);
64
+ y += this.updateSpriteType(y);
65
+ y += this.updateSpriteRegion(y);
66
+ y += this.updateSpriteTarget(y);
67
+ y += this.updateSpriteIdentify(y);
68
+ y += this.updateSpriteTerminate(y);
69
+ y += this.updateSpriteEntry(y);
70
+ y += this.updateSpriteLatency(y);
71
+ y += this.updateSpriteConnections(y);
72
+ y += this.updateSpriteRequests(y);
73
+ y += this.updateSpriteRequestInfo(y);
74
+ this.updateSpriteRequestScrollable();
75
+ if (y < this.terminal.height) {
76
+ this.viewPort.fill({
77
+ char: ' ',
78
+ region: {
79
+ x: 0,
80
+ y,
81
+ width: this.terminal.width,
82
+ height: this.terminal.height - y
83
+ }
84
+ });
85
+ }
86
+ this.viewPort.draw();
87
+ }
88
+
89
+ createSpriteConnections() {
90
+ let init = fillString('', ROW_LENGTH);
91
+ init += '\n';
92
+ init += fillString('Connections', ROW_LENGTH);
93
+ init += '\n';
94
+ init += fillString('', ROW_LENGTH);
95
+ init += '\n';
96
+ init += fillString('', ROW_LENGTH);
97
+ this.sprites.connections = TerminalKit.ScreenBuffer.createFromString({
98
+ attr: {
99
+ color: COLOR_LIGHT_GRAY
100
+ }
101
+ }, init);
102
+ this.sprites.connections.put({
103
+ x: 0,
104
+ y: 2,
105
+ attr: {
106
+ color: COLOR_LIGHT_GRAY,
107
+ }
108
+ }, fillString(' Current:', ROW_LENGTH));
109
+ this.sprites.connections.put({
110
+ x: 0,
111
+ y: 3,
112
+ attr: {
113
+ color: COLOR_LIGHT_GRAY,
114
+ }
115
+ }, fillString(' Total:', ROW_LENGTH));
116
+ }
117
+
118
+ createSpriteRequestInfo() {
119
+ this.sprites.requestInfo = new TerminalKit.ScreenBuffer({
120
+ x: 0,
121
+ y: 0,
122
+ width: ROW_LENGTH,
123
+ height: this.terminal.height
124
+ });
125
+ this.sprites.requestInfoScroll = new TerminalKit.ScreenBuffer({
126
+ x: 0,
127
+ y: REQUEST_INFO_START_Y,
128
+ width: REQUEST_INFO_MAX_LENGTH,
129
+ height: this.terminal.height
130
+ });
131
+ }
132
+
133
+ createSpriteRequests() {
134
+ let header = fillString('', ROW_LENGTH);
135
+ header += '\n';
136
+ header += fillString('Requests (use arrows ↑↓ to inspect & `enter` to retry)', ROW_LENGTH);
137
+ header += '\n';
138
+ header += fillString('-------------------------------------------------------------------------------------------------------', ROW_LENGTH);
139
+ this.sprites.requestsHeader = TerminalKit.ScreenBuffer.createFromString({
140
+ attr: {
141
+ color: COLOR_LIGHT_GRAY
142
+ }
143
+ }, header);
144
+ this.sprites.requests = new TerminalKit.ScreenBuffer({
145
+ x: 10,
146
+ y: 0,
147
+ width: ROW_LENGTH,
148
+ height: REQUESTS_MAX
149
+ });
150
+ this.sprites.requestsSelected = new TerminalKit.ScreenBuffer({
151
+ x: 0,
152
+ y: 0,
153
+ width: 10,
154
+ height: REQUESTS_MAX
155
+ });
156
+ this.sprites.requestsSelectedLast = new TerminalKit.ScreenBuffer({
157
+ x: 0,
158
+ y: 0,
159
+ width: ROW_LENGTH,
160
+ height: 1
161
+ });
162
+ }
163
+
164
+ logRequestToText(r) {
165
+ let msg = `${r.method} ${r.url}`;
166
+ if (msg.length > REQUESTS_MAX_LENGTH) msg = msg.substring(0, REQUESTS_MAX_LENGTH);
167
+ else msg = fillString(msg, REQUESTS_MAX_LENGTH);
168
+ if (r.status === 'NEW') {
169
+ msg += ' ^KNEW^';
170
+ } else if (r.status >= 500) {
171
+ msg += ` ^R${r.status}^`;
172
+ } else if (r.status >= 400) {
173
+ msg += ` ^Y${r.status}^`;
174
+ } else if (r.status >= 300) {
175
+ msg += ` ^B${r.status}^`;
176
+ } else {
177
+ msg += ` ${r.status}`;
178
+ }
179
+ if (r.finished) msg += ` in ^K${r.time}ms`;
180
+ return msg;
181
+ }
182
+
183
+ getPathAndQuery(url) {
184
+ let query = {};
185
+ let path;
186
+ try {
187
+ const u = new URL(url, 'https://test.com');
188
+ path = u.pathname;
189
+ u.searchParams.forEach((val, name) => {
190
+ query[name] = val;
191
+ });
192
+ } catch {
193
+ path = url;
194
+ }
195
+ return {
196
+ path,
197
+ query
198
+ };
199
+ }
200
+
201
+ updateSpriteRequestScrollable() {
202
+ if (this.selectedRequest) {
203
+ this.sprites.requestInfoScroll.y = this.sprites.requestInfoScrollY;
204
+ this.sprites.requestInfoScroll.fill({
205
+ char: ' ',
206
+ x: 0,
207
+ y: 0,
208
+ width: REQUEST_INFO_MAX_LENGTH,
209
+ height: this.terminal.height
210
+ });
211
+ const {
212
+ query,
213
+ path
214
+ } = this.getPathAndQuery(this.selectedRequest.url);
215
+ let msg = '^KStatus:^ ';
216
+ msg += `^W${this.selectedRequest.status}^\n`;
217
+ msg += '\n^KMethod:^ ';
218
+ msg += `^WHTTP ${this.selectedRequest.httpVersion} ${this.selectedRequest.method}^\n`;
219
+ msg += '\n^KPath:^ ';
220
+ msg += `^W${path}^\n`;
221
+ const queryKeys = Object.keys(query);
222
+ if (queryKeys.length > 0) {
223
+ msg += '\n^WQuery:^\n';
224
+ queryKeys.forEach((name) => {
225
+ msg += `^K${name}:^ ^W${query[name]}^\n`;
226
+ });
227
+ }
228
+ if (this.selectedRequest.requestBody.realLength > 0) {
229
+ msg += '\n^WBody:^\n';
230
+ if (this.selectedRequest.requestBody.tooLarge || this.selectedRequest.requestBody.realLength > 20 * REQUEST_INFO_MAX_LENGTH) {
231
+ msg += `^K<Too large (${formatBytes(this.selectedRequest.requestBody.realLength)})>^\n`;
232
+ } else {
233
+ const isBinary = isBinaryFileSync(this.selectedRequest.requestBody.data);
234
+ if (isBinary) {
235
+ msg += '^K<Binary>^\n';
236
+ } else {
237
+ msg += `^K${this.selectedRequest.requestBody.data.toString('utf8')}^\n`;
238
+ }
239
+ }
240
+ }
241
+ msg += '\n^WHeaders:^\n';
242
+ const headers = this.selectedRequest.headers || {};
243
+ Object.keys(headers).forEach((name) => {
244
+ msg += `^K${name}:^ ^W${headers[name]}^\n`;
245
+ });
246
+ if (this.selectedRequest.finished) {
247
+ if (this.selectedRequest.responseBody.realLength > 0) {
248
+ msg += '\n^WResponse body:^\n';
249
+ if (this.selectedRequest.responseBody.tooLarge || this.selectedRequest.responseBody.realLength > 20 * REQUEST_INFO_MAX_LENGTH) {
250
+ msg += `^K<Too large (${formatBytes(this.selectedRequest.responseBody.realLength)})>^\n`;
251
+ } else {
252
+ const isBinary = isBinaryFileSync(this.selectedRequest.responseBody.data);
253
+ if (isBinary) {
254
+ msg += '^K<Binary>^\n';
255
+ } else {
256
+ msg += `^K${this.selectedRequest.responseBody.data.toString('utf8')}^\n`;
257
+ }
258
+ }
259
+ }
260
+ msg += '\n^WResponse headers:^\n';
261
+ const headers = this.selectedRequest.responseHeaders || {};
262
+ Object.keys(headers).forEach((name) => {
263
+ msg += `^K${name}:^ ^W${headers[name]}^\n`;
264
+ });
265
+ }
266
+ this.sprites.requestInfoScroll.put({
267
+ x: 0,
268
+ y: 0,
269
+ newLine: true,
270
+ markup: true,
271
+ wrap: true
272
+ }, msg);
273
+ }
274
+ }
275
+
276
+ updateSpriteRequestInfo(y) {
277
+ if (this.tunnel.log) {
278
+ this.sprites.requestInfo.fill({
279
+ char: ' ',
280
+ region: {
281
+ x: 0,
282
+ y: 0,
283
+ width: ROW_LENGTH,
284
+ height: this.terminal.height
285
+ }
286
+ });
287
+ if (this.selectedRequest) {
288
+ this.sprites.requestInfoScroll.draw({
289
+ dst: this.sprites.requestInfo,
290
+ });
291
+ this.sprites.requestInfo.put({
292
+ attr: {
293
+ color: COLOR_LIGHT_GRAY
294
+ },
295
+ x: 0,
296
+ y: 0,
297
+ }, fillString('', ROW_LENGTH));
298
+ this.sprites.requestInfo.put({
299
+ attr: {
300
+ color: COLOR_LIGHT_GRAY
301
+ },
302
+ x: 0,
303
+ y: 1,
304
+ }, fillString('Request details (use `space` to scroll)', ROW_LENGTH));
305
+ this.sprites.requestInfo.put({
306
+ attr: {
307
+ color: COLOR_LIGHT_GRAY
308
+ },
309
+ x: 0,
310
+ y: 2,
311
+ }, fillString('-------------------------------------------------------------------------------------------------------', ROW_LENGTH));
312
+ }
313
+ this.sprites.requestInfo.draw({
314
+ dst: this.viewPort,
315
+ y
316
+ });
317
+ return this.sprites.requestInfo.height;
318
+ }
319
+ return 0;
320
+ }
321
+
322
+ updateSpriteRequests(y) {
323
+ if (this.tunnel.log && this.tunnel.httpLog) {
324
+ const requests = this.tunnel.httpLog.requests;
325
+ const max = requests.length > REQUESTS_MAX ? REQUESTS_MAX : requests.length;
326
+ let col = 0;
327
+ let selectedY = -1;
328
+ for (let i = 0; i < max; i += 1) {
329
+ const r = requests[i];
330
+ if (this.selectedRequest === r) {
331
+ selectedY = i;
332
+ }
333
+ this.sprites.requests.put({
334
+ markup: true,
335
+ x: 0,
336
+ y: col
337
+ }, fillString(this.logRequestToText(r), ROW_LENGTH - 10));
338
+ col += 1;
339
+ }
340
+ if (col < REQUESTS_MAX) {
341
+ this.sprites.requests.fill({
342
+ char: ' ',
343
+ region: {
344
+ x: 0,
345
+ y: col,
346
+ width: ROW_LENGTH,
347
+ height: REQUESTS_MAX - col
348
+ }
349
+ });
350
+ }
351
+ this.sprites.requestsSelected.fill({
352
+ char: ' ',
353
+ region: {
354
+ x: 0,
355
+ y: 0,
356
+ width: 10,
357
+ height: REQUESTS_MAX
358
+ }
359
+ });
360
+ this.sprites.requestsSelectedLast.fill({
361
+ char: ' ',
362
+ region: {
363
+ x: 0,
364
+ y: 0,
365
+ width: ROW_LENGTH,
366
+ height: 1
367
+ }
368
+ });
369
+ if (this.selectedRequest) {
370
+ let last;
371
+ let seltxt;
372
+ let selcol;
373
+ if (this.selectedRequest.requestBody.tooLarge) {
374
+ last = '^R[ LARGE ]^ ';
375
+ seltxt = '[ LARGE ]';
376
+ selcol = COLOR_RED;
377
+ } else {
378
+ last = '^c[ RETRY ]^ ';
379
+ seltxt = '[ RETRY ]';
380
+ selcol = COLOR_CYAN;
381
+ }
382
+ if (selectedY >= 0) {
383
+ this.sprites.requestsSelected.put({
384
+ attr: {
385
+ color: selcol
386
+ },
387
+ x: 0,
388
+ y: selectedY,
389
+ }, fillString(seltxt, 10));
390
+ } else {
391
+ last += this.logRequestToText(this.selectedRequest);
392
+ this.sprites.requestsSelectedLast.put({
393
+ x: 0,
394
+ y: 0,
395
+ markup: true
396
+ }, fillString(last, ROW_LENGTH));
397
+ }
398
+ }
399
+ this.sprites.requestsHeader.draw({
400
+ dst: this.viewPort,
401
+ y
402
+ });
403
+ let h = this.sprites.requestsHeader.height;
404
+ this.sprites.requests.draw({
405
+ dst: this.viewPort,
406
+ y: y + h
407
+ });
408
+ this.sprites.requestsSelected.draw({
409
+ dst: this.viewPort,
410
+ y: y + h
411
+ });
412
+ h += this.sprites.requests.height;
413
+ this.sprites.requestsSelectedLast.draw({
414
+ dst: this.viewPort,
415
+ y: y + h
416
+ });
417
+ h += this.sprites.requestsSelectedLast.height;
418
+ return h;
419
+ }
420
+ return 0;
421
+ }
422
+
423
+ moveUpDown(up = true) {
424
+ if (this.tunnel.log && this.tunnel.httpLog && this.tunnel.httpLog.requests.length > 0) {
425
+ const r = this.tunnel.httpLog.requests;
426
+ const max = r.length > REQUESTS_MAX ? REQUESTS_MAX : r.length;
427
+ if (!this.selectedRequest) this.selectedRequest = !up ? r[0] : r[max - 1];
428
+ else {
429
+ let idx = -1;
430
+ for (let i = 0; i < max; i += 1) {
431
+ if (r[i].id === this.selectedRequest.id) {
432
+ idx = !up ? i + 1 : i - 1;
433
+ break;
434
+ }
435
+ }
436
+ if (r[idx] && idx < max) this.selectedRequest = r[idx];
437
+ else if (up) this.selectedRequest = r[max - 1];
438
+ else this.selectedRequest = null;
439
+ }
440
+ this.sprites.requestInfoScrollY = REQUEST_INFO_START_Y;
441
+ this.updateSpriteRequestScrollable();
442
+ }
443
+ }
444
+
445
+ moveUp() {
446
+ this.moveUpDown(true);
447
+ }
448
+
449
+ moveDown() {
450
+ this.moveUpDown(false);
451
+ }
452
+
453
+ space() {
454
+ if (this.selectedRequest && this.sprites.requestInfoScroll) {
455
+ this.sprites.requestInfoScrollY -= 1;
456
+ }
457
+ }
458
+
459
+ createSpriteLatency() {
460
+ let init = fillString('', ROW_LENGTH);
461
+ init += '\n';
462
+ init += fillString('Latencies', ROW_LENGTH);
463
+ init += '\n';
464
+ init += fillString('', ROW_LENGTH);
465
+ init += '\n';
466
+ init += fillString('', ROW_LENGTH);
467
+ this.sprites.latency = TerminalKit.ScreenBuffer.createFromString({
468
+ attr: {
469
+ color: COLOR_LIGHT_GRAY
470
+ }
471
+ }, init);
472
+ this.sprites.latency.put({
473
+ x: 0,
474
+ y: 2,
475
+ attr: {
476
+ color: COLOR_LIGHT_GRAY,
477
+ }
478
+ }, fillString(' Region:', ROW_LENGTH));
479
+ this.sprites.latency.put({
480
+ x: 0,
481
+ y: 3,
482
+ attr: {
483
+ color: COLOR_LIGHT_GRAY,
484
+ }
485
+ }, fillString(' Target:', ROW_LENGTH));
486
+ }
487
+
488
+ createSpriteEntry() {
489
+ this.sprites.entry = TerminalKit.ScreenBuffer.createFromString({
490
+ attr: {
491
+ color: COLOR_LIGHT_GRAY
492
+ }
493
+ }, fillString('Entry:', ROW_LENGTH));
494
+ }
495
+
496
+ createSpriteTerminate() {
497
+ if (this.tunnel.type === TUNNEL_TLS) {
498
+ this.sprites.terminate = TerminalKit.ScreenBuffer.createFromString({
499
+ attr: {
500
+ color: COLOR_LIGHT_GRAY
501
+ }
502
+ }, fillString('Terminate:', ROW_LENGTH));
503
+ }
504
+ }
505
+
506
+ createSpriteTarget() {
507
+ this.sprites.target = TerminalKit.ScreenBuffer.createFromString({
508
+ attr: {
509
+ color: COLOR_LIGHT_GRAY
510
+ }
511
+ }, fillString('', ROW_LENGTH));
512
+ }
513
+
514
+ createSpriteHeader() {
515
+ this.sprites.header = TerminalKit.ScreenBuffer.createFromString({
516
+ attr: {
517
+ color: COLOR_WHITE
518
+ }
519
+ }, `${fillString(`Buddy Tunnel ${getVersion()}`, ROW_LENGTH)}\n${fillString('', ROW_LENGTH)}`);
520
+ }
521
+
522
+ createSpriteStatus() {
523
+ this.sprites.status = TerminalKit.ScreenBuffer.createFromString({
524
+ attr: {
525
+ color: COLOR_LIGHT_GRAY
526
+ }
527
+ }, fillString('Status:', ROW_LENGTH));
528
+ }
529
+
530
+ createSpriteType() {
531
+ this.sprites.type = TerminalKit.ScreenBuffer.createFromString({
532
+ attr: {
533
+ color: COLOR_LIGHT_GRAY
534
+ }
535
+ }, fillString('Type:', ROW_LENGTH));
536
+ }
537
+
538
+ createSpriteDns() {
539
+ this.sprites.dns = TerminalKit.ScreenBuffer.createFromString({
540
+ attr: {
541
+ color: COLOR_LIGHT_GRAY
542
+ }
543
+ }, fillString('DNS:', ROW_LENGTH));
544
+ }
545
+
546
+ createSpriteRegion() {
547
+ this.sprites.region = TerminalKit.ScreenBuffer.createFromString({
548
+ attr: {
549
+ color: COLOR_LIGHT_GRAY
550
+ }
551
+ }, fillString('Region:', ROW_LENGTH));
552
+ }
553
+
554
+ createSpriteIdentify() {
555
+ this.sprites.identify = TerminalKit.ScreenBuffer.createFromString({
556
+ attr: {
557
+ color: COLOR_LIGHT_GRAY
558
+ }
559
+ }, fillString('HTTP:', ROW_LENGTH));
560
+ }
561
+
562
+ updateSpriteDns(y) {
563
+ let text;
564
+ let color;
565
+ const {
566
+ checking,
567
+ valid
568
+ } = this.tunnel.dns;
569
+ if (valid) {
570
+ text = 'OK';
571
+ color = COLOR_GREEN;
572
+ } else if (checking) {
573
+ text = 'Checking...';
574
+ color = COLOR_WHITE;
575
+ } else {
576
+ text = 'Wrong. Clear DNS local cache';
577
+ color = COLOR_RED;
578
+ }
579
+ this.sprites.dns.put({
580
+ x: COLUMN_LENGTH,
581
+ y: 0,
582
+ attr: {
583
+ color
584
+ }
585
+ }, fillString(text, ROW_LENGTH - COLUMN_LENGTH));
586
+ this.sprites.dns.draw({
587
+ dst: this.viewPort,
588
+ y
589
+ });
590
+ return this.sprites.dns.height;
591
+ }
592
+
593
+ updateSpriteHeader(y) {
594
+ this.sprites.header.draw({
595
+ dst: this.viewPort,
596
+ y
597
+ });
598
+ return this.sprites.header.height;
599
+ }
600
+
601
+ updateSpriteStatus(y) {
602
+ let text;
603
+ let color;
604
+ if (this.tunnel.status === TUNNEL_OPEN) {
605
+ text = 'OPEN ';
606
+ color = COLOR_GREEN;
607
+ } else {
608
+ text = 'CLOSED';
609
+ color = COLOR_RED;
610
+ }
611
+ this.sprites.status.put({
612
+ x: COLUMN_LENGTH,
613
+ y: 0,
614
+ attr: {
615
+ color
616
+ }
617
+ }, fillString(text, ROW_LENGTH - COLUMN_LENGTH));
618
+ this.sprites.status.draw({
619
+ dst: this.viewPort,
620
+ y
621
+ });
622
+ return this.sprites.status.height;
623
+ }
624
+
625
+ updateSpriteType(y) {
626
+ this.sprites.type.put({
627
+ x: COLUMN_LENGTH,
628
+ y: 0,
629
+ attr: {
630
+ color: COLOR_WHITE
631
+ }
632
+ }, fillString(Format.type(this.tunnel.type), ROW_LENGTH - COLUMN_LENGTH));
633
+ this.sprites.type.draw({
634
+ dst: this.viewPort,
635
+ y
636
+ });
637
+ return this.sprites.type.height;
638
+ }
639
+
640
+ updateSpriteIdentify(y) {
641
+ if (this.tunnel.type === TUNNEL_HTTP) {
642
+ this.sprites.identify.put({
643
+ x: COLUMN_LENGTH,
644
+ y: 0,
645
+ attr: {
646
+ color: COLOR_WHITE
647
+ }
648
+ }, fillString(Format.identify(this.tunnel.identify.type), ROW_LENGTH - COLUMN_LENGTH));
649
+ this.sprites.identify.draw({
650
+ dst: this.viewPort,
651
+ y
652
+ });
653
+ return this.sprites.identify.height;
654
+ }
655
+ return 0;
656
+ }
657
+
658
+ updateSpriteTarget(y) {
659
+ let titleText;
660
+ let valueText;
661
+ if (this.tunnel.serve) {
662
+ titleText = 'Serve:';
663
+ valueText = Format.serve(this.tunnel.serve);
664
+ } else {
665
+ titleText = 'Target:';
666
+ valueText = Format.target(this.tunnel.type, this.tunnel.target);
667
+ }
668
+ this.sprites.target.put({
669
+ x: 0,
670
+ y: 0,
671
+ attr: {
672
+ color: COLOR_LIGHT_GRAY
673
+ }
674
+ }, fillString(titleText, ROW_LENGTH));
675
+ this.sprites.target.put({
676
+ x: COLUMN_LENGTH,
677
+ y: 0,
678
+ attr: {
679
+ color: COLOR_WHITE
680
+ }
681
+ }, fillString(valueText, ROW_LENGTH - COLUMN_LENGTH));
682
+ this.sprites.target.draw({
683
+ dst: this.viewPort,
684
+ y
685
+ });
686
+ return this.sprites.target.height;
687
+ }
688
+
689
+ updateSpriteRegion(y) {
690
+ this.sprites.region.put({
691
+ x: COLUMN_LENGTH,
692
+ y: 0,
693
+ attr: {
694
+ color: COLOR_WHITE
695
+ }
696
+ }, fillString(Format.region(this.tunnel.region), ROW_LENGTH - COLUMN_LENGTH));
697
+ this.sprites.region.draw({
698
+ dst: this.viewPort,
699
+ y
700
+ });
701
+ return this.sprites.region.height;
702
+ }
703
+
704
+ updateSpriteTerminate(y) {
705
+ if (this.tunnel.type === TUNNEL_TLS) {
706
+ this.sprites.terminate.put({
707
+ x: COLUMN_LENGTH,
708
+ y: 0,
709
+ attr: {
710
+ color: COLOR_WHITE
711
+ }
712
+ }, fillString(Format.terminate(this.tunnel.terminate), ROW_LENGTH - COLUMN_LENGTH));
713
+ this.sprites.terminate.draw({
714
+ dst: this.viewPort,
715
+ y
716
+ });
717
+ return this.sprites.terminate.height;
718
+ }
719
+ return 0;
720
+ }
721
+
722
+ updateSpriteEntry(y) {
723
+ this.sprites.entry.put({
724
+ x: COLUMN_LENGTH,
725
+ y: 0,
726
+ attr: {
727
+ color: COLOR_CYAN,
728
+ bold: true
729
+ }
730
+ }, fillString(Format.entry(this.tunnel), ROW_LENGTH - COLUMN_LENGTH));
731
+ this.sprites.entry.draw({
732
+ dst: this.viewPort,
733
+ y
734
+ });
735
+ return this.sprites.entry.height;
736
+ }
737
+
738
+ updateSpriteConnections(y) {
739
+ const count = Object.keys(this.tunnel.connections).length;
740
+ this.sprites.connections.put({
741
+ x: COLUMN_LENGTH,
742
+ y: 2,
743
+ attr: {
744
+ color: COLOR_WHITE,
745
+ }
746
+ }, fillString(String(count), ROW_LENGTH - COLUMN_LENGTH));
747
+ this.sprites.connections.put({
748
+ x: COLUMN_LENGTH,
749
+ y: 3,
750
+ attr: {
751
+ color: COLOR_WHITE,
752
+ }
753
+ }, fillString(String(this.tunnel.totalConnections), ROW_LENGTH - COLUMN_LENGTH));
754
+ this.sprites.connections.draw({
755
+ dst: this.viewPort,
756
+ y
757
+ });
758
+ return this.sprites.connections.height;
759
+ }
760
+
761
+ updateSpriteLatency(y) {
762
+ let regionLatency = -1;
763
+ let targetLatency = -1;
764
+ if (this.tunnel.regionLatency) regionLatency = this.tunnel.regionLatency.latency;
765
+ if (this.tunnel.serve) targetLatency = 0;
766
+ else if (this.tunnel.targetLatency) targetLatency = this.tunnel.targetLatency.latency;
767
+ let region;
768
+ let regionColor;
769
+ let target;
770
+ let targetColor;
771
+ if (regionLatency < 0) {
772
+ region = 'Unreachable';
773
+ regionColor = COLOR_RED;
774
+ } else {
775
+ region = Format.latency(regionLatency);
776
+ regionColor = COLOR_WHITE;
777
+ }
778
+ if (targetLatency < 0) {
779
+ target = 'Unreachable';
780
+ targetColor = COLOR_RED;
781
+ } else {
782
+ target = Format.latency(targetLatency);
783
+ targetColor = COLOR_WHITE;
784
+ }
785
+ this.sprites.latency.put({
786
+ x: COLUMN_LENGTH,
787
+ y: 2,
788
+ attr: {
789
+ color: regionColor,
790
+ }
791
+ }, fillString(region, ROW_LENGTH - COLUMN_LENGTH));
792
+ this.sprites.latency.put({
793
+ x: COLUMN_LENGTH,
794
+ y: 3,
795
+ attr: {
796
+ color: targetColor,
797
+ }
798
+ }, fillString(target, ROW_LENGTH - COLUMN_LENGTH));
799
+ this.sprites.latency.draw({
800
+ dst: this.viewPort,
801
+ y
802
+ });
803
+ return this.sprites.latency.height;
804
+ }
805
+
806
+ init() {
807
+ this.viewPort = new TerminalKit.ScreenBuffer({
808
+ dst: this.terminal,
809
+ width: Math.min(this.terminal.width),
810
+ height: Math.min(this.terminal.height),
811
+ x: 1,
812
+ y: 1
813
+ });
814
+ this.createSpriteHeader();
815
+ this.createSpriteStatus();
816
+ this.createSpriteType();
817
+ this.createSpriteDns();
818
+ this.createSpriteRegion();
819
+ this.createSpriteTarget();
820
+ this.createSpriteIdentify();
821
+ this.createSpriteTerminate();
822
+ this.createSpriteEntry();
823
+ this.createSpriteLatency();
824
+ this.createSpriteConnections();
825
+ this.createSpriteRequests();
826
+ this.createSpriteRequestInfo();
827
+ this.terminal.grabInput();
828
+ this.terminal.hideCursor();
829
+ this.terminal.fullscreen(true);
830
+ this.terminal.on('key', (name) => {
831
+ if (name === 'CTRL_C') {
832
+ this.terminate();
833
+ } else if (name === 'ENTER') {
834
+ this.retry();
835
+ } else if (name === 'DOWN') {
836
+ this.moveDown();
837
+ } else if (name === 'UP') {
838
+ this.moveUp();
839
+ } else if (name === ' ') {
840
+ this.space();
841
+ }
842
+ });
843
+ this.terminal.on('resize', () => {
844
+ this.draw();
845
+ });
846
+ this.tunnel.on(TUNNEL_EVENT_STOPPED, () => {
847
+ this.terminate();
848
+ });
849
+ }
850
+
851
+ terminate() {
852
+ this.terminal.fullscreen(false);
853
+ this.terminal.hideCursor(false);
854
+ this.terminal.grabInput(false);
855
+ process.exit();
856
+ }
857
+
858
+ animate() {
859
+ this.draw();
860
+ setTimeout(() => {
861
+ this.animate();
862
+ }, 50);
863
+ }
864
+
865
+ start() {
866
+ this.init();
867
+ this.animate();
868
+ }
869
+ }
870
+
871
+ module.exports = OutputInteractiveTunnel;