ai-remote 0.1.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 (114) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +7 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/protocols/index.js +35 -0
  5. package/dist/protocols/index.js.map +7 -0
  6. package/dist/protocols/rdp/buffer.js +170 -0
  7. package/dist/protocols/rdp/buffer.js.map +7 -0
  8. package/dist/protocols/rdp/caps.js +260 -0
  9. package/dist/protocols/rdp/caps.js.map +7 -0
  10. package/dist/protocols/rdp/cert.js +135 -0
  11. package/dist/protocols/rdp/cert.js.map +7 -0
  12. package/dist/protocols/rdp/client.js +1068 -0
  13. package/dist/protocols/rdp/client.js.map +7 -0
  14. package/dist/protocols/rdp/cliprdr.js +240 -0
  15. package/dist/protocols/rdp/cliprdr.js.map +7 -0
  16. package/dist/protocols/rdp/credssp.js +254 -0
  17. package/dist/protocols/rdp/credssp.js.map +7 -0
  18. package/dist/protocols/rdp/crypto.js +369 -0
  19. package/dist/protocols/rdp/crypto.js.map +7 -0
  20. package/dist/protocols/rdp/display.js +589 -0
  21. package/dist/protocols/rdp/display.js.map +7 -0
  22. package/dist/protocols/rdp/gcc.js +132 -0
  23. package/dist/protocols/rdp/gcc.js.map +7 -0
  24. package/dist/protocols/rdp/keymap.js +132 -0
  25. package/dist/protocols/rdp/keymap.js.map +7 -0
  26. package/dist/protocols/rdp/mcs.js +223 -0
  27. package/dist/protocols/rdp/mcs.js.map +7 -0
  28. package/dist/protocols/rdp/ntlm.js +305 -0
  29. package/dist/protocols/rdp/ntlm.js.map +7 -0
  30. package/dist/protocols/rdp/pdu.js +444 -0
  31. package/dist/protocols/rdp/pdu.js.map +7 -0
  32. package/dist/protocols/rdp/rail.js +383 -0
  33. package/dist/protocols/rdp/rail.js.map +7 -0
  34. package/dist/protocols/rdp/rle.js +276 -0
  35. package/dist/protocols/rdp/rle.js.map +7 -0
  36. package/dist/protocols/rdp/sec.js +156 -0
  37. package/dist/protocols/rdp/sec.js.map +7 -0
  38. package/dist/protocols/rdp/session.js +307 -0
  39. package/dist/protocols/rdp/session.js.map +7 -0
  40. package/dist/protocols/rdp/tls.js +354 -0
  41. package/dist/protocols/rdp/tls.js.map +7 -0
  42. package/dist/protocols/rdp/vchannel.js +59 -0
  43. package/dist/protocols/rdp/vchannel.js.map +7 -0
  44. package/dist/protocols/rdp/x224.js +110 -0
  45. package/dist/protocols/rdp/x224.js.map +7 -0
  46. package/dist/protocols/ssh/kex.js +177 -0
  47. package/dist/protocols/ssh/kex.js.map +7 -0
  48. package/dist/protocols/ssh/messages.js +55 -0
  49. package/dist/protocols/ssh/messages.js.map +7 -0
  50. package/dist/protocols/ssh/session.js +608 -0
  51. package/dist/protocols/ssh/session.js.map +7 -0
  52. package/dist/protocols/ssh/terminal.js +191 -0
  53. package/dist/protocols/ssh/terminal.js.map +7 -0
  54. package/dist/protocols/ssh/transport.js +611 -0
  55. package/dist/protocols/ssh/transport.js.map +7 -0
  56. package/dist/protocols/ssh/wire.js +189 -0
  57. package/dist/protocols/ssh/wire.js.map +7 -0
  58. package/dist/protocols/types.js +1 -0
  59. package/dist/protocols/types.js.map +7 -0
  60. package/dist/protocols/vnc/input.js +16 -0
  61. package/dist/protocols/vnc/input.js.map +7 -0
  62. package/dist/protocols/vnc/session.js +259 -0
  63. package/dist/protocols/vnc/session.js.map +7 -0
  64. package/dist/shared/connection.js +114 -0
  65. package/dist/shared/connection.js.map +7 -0
  66. package/dist/shared/device.js +1 -0
  67. package/dist/shared/device.js.map +7 -0
  68. package/dist/shared/hosts.js +133 -0
  69. package/dist/shared/hosts.js.map +7 -0
  70. package/dist/shared/icons.js +42 -0
  71. package/dist/shared/icons.js.map +7 -0
  72. package/dist/shared/protocol.js +40 -0
  73. package/dist/shared/protocol.js.map +7 -0
  74. package/dist/shared/signals.js +42 -0
  75. package/dist/shared/signals.js.map +7 -0
  76. package/package.json +82 -0
  77. package/src/index.ts +14 -0
  78. package/src/protocols/index.ts +72 -0
  79. package/src/protocols/rdp/buffer.ts +204 -0
  80. package/src/protocols/rdp/caps.ts +304 -0
  81. package/src/protocols/rdp/cert.ts +190 -0
  82. package/src/protocols/rdp/client.ts +1363 -0
  83. package/src/protocols/rdp/cliprdr.ts +344 -0
  84. package/src/protocols/rdp/credssp.ts +319 -0
  85. package/src/protocols/rdp/crypto.ts +416 -0
  86. package/src/protocols/rdp/display.ts +750 -0
  87. package/src/protocols/rdp/gcc.ts +162 -0
  88. package/src/protocols/rdp/keymap.ts +68 -0
  89. package/src/protocols/rdp/mcs.ts +283 -0
  90. package/src/protocols/rdp/ntlm.ts +380 -0
  91. package/src/protocols/rdp/pdu.ts +497 -0
  92. package/src/protocols/rdp/rail.ts +480 -0
  93. package/src/protocols/rdp/rle.ts +343 -0
  94. package/src/protocols/rdp/sec.ts +184 -0
  95. package/src/protocols/rdp/session.ts +393 -0
  96. package/src/protocols/rdp/tls.ts +452 -0
  97. package/src/protocols/rdp/vchannel.ts +89 -0
  98. package/src/protocols/rdp/x224.ts +139 -0
  99. package/src/protocols/ssh/kex.ts +227 -0
  100. package/src/protocols/ssh/messages.ts +54 -0
  101. package/src/protocols/ssh/session.ts +878 -0
  102. package/src/protocols/ssh/terminal.ts +242 -0
  103. package/src/protocols/ssh/transport.ts +732 -0
  104. package/src/protocols/ssh/wire.ts +220 -0
  105. package/src/protocols/types.ts +146 -0
  106. package/src/protocols/vnc/input.ts +13 -0
  107. package/src/protocols/vnc/novnc.d.ts +75 -0
  108. package/src/protocols/vnc/session.ts +367 -0
  109. package/src/shared/connection.ts +206 -0
  110. package/src/shared/device.ts +28 -0
  111. package/src/shared/hosts.ts +240 -0
  112. package/src/shared/icons.ts +49 -0
  113. package/src/shared/protocol.ts +55 -0
  114. package/src/shared/signals.ts +67 -0
@@ -0,0 +1,242 @@
1
+ // The terminal surface: xterm.js, themed to match the gateway, wired to an
2
+ // SSH session's bytes in and keystrokes out.
3
+
4
+ import { Terminal } from '@xterm/xterm';
5
+ import { FitAddon } from '@xterm/addon-fit';
6
+ import xtermStyles from '@xterm/xterm/css/xterm.css';
7
+
8
+ const STYLE_ELEMENT_ID = 'xterm-styles';
9
+
10
+ /**
11
+ * The width almost every command's output is laid out for.
12
+ *
13
+ * A terminal narrower than this does not merely look cramped, it garbles: `ls
14
+ * -l`, `Get-ChildItem`, `ps` and `top` all place columns by character position,
15
+ * so the last column wraps one letter per line and the table stops being a
16
+ * table. Fitting the font to the panel rather than the panel to the font is
17
+ * what keeps that from happening in a docked panel or on a phone.
18
+ */
19
+ const MIN_COLUMNS = 80;
20
+
21
+ /** Comfortable on a wide panel, and the size a roomy one keeps. */
22
+ const BASE_FONT_SIZE = 13;
23
+
24
+ /**
25
+ * The floor. On a small mobile screen this yields 80 columns without wrapping
26
+ * or cutting off table outputs.
27
+ */
28
+ const MIN_FONT_SIZE = 7;
29
+
30
+ /** xterm ships its own stylesheet; the bundle carries it instead of a CDN. */
31
+ function installStyles() {
32
+ if (document.getElementById(STYLE_ELEMENT_ID)) return;
33
+ const style = document.createElement('style');
34
+ style.id = STYLE_ELEMENT_ID;
35
+ style.textContent = xtermStyles;
36
+ document.head.appendChild(style);
37
+ }
38
+
39
+ /** The gateway's palette, so the terminal belongs to the same application. */
40
+ const THEME = {
41
+ background: '#0b0d12',
42
+ foreground: '#dfe4ec',
43
+ cursor: '#f38020',
44
+ cursorAccent: '#0b0d12',
45
+ selectionBackground: 'rgba(243, 128, 32, 0.32)',
46
+ black: '#1b1f27',
47
+ red: '#ff6b6b',
48
+ green: '#4ade80',
49
+ yellow: '#fbbf24',
50
+ blue: '#60a5fa',
51
+ magenta: '#c084fc',
52
+ cyan: '#34d3ee',
53
+ white: '#cbd3e1',
54
+ brightBlack: '#63708a',
55
+ brightRed: '#ff8787',
56
+ brightGreen: '#86efac',
57
+ brightYellow: '#fcd34d',
58
+ brightBlue: '#93c5fd',
59
+ brightMagenta: '#d8b4fe',
60
+ brightCyan: '#67e8f9',
61
+ brightWhite: '#f5f7fa',
62
+ };
63
+
64
+ export class SshTerminal {
65
+ constructor(container) {
66
+ installStyles();
67
+
68
+ this.container = container;
69
+ this.terminal = new Terminal({
70
+ allowProposedApi: true,
71
+ convertEol: false,
72
+ cursorBlink: true,
73
+ cursorStyle: 'bar',
74
+ drawBoldTextInBrightColors: true,
75
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
76
+ fontSize: BASE_FONT_SIZE,
77
+ lineHeight: 1.25,
78
+ letterSpacing: 0,
79
+ macOptionIsMeta: true,
80
+ minimumContrastRatio: 1.5,
81
+ scrollback: 10_000,
82
+ theme: THEME,
83
+ });
84
+
85
+ this.fitAddon = new FitAddon();
86
+ this.terminal.loadAddon(this.fitAddon);
87
+ this.terminal.open(container);
88
+ this.fit();
89
+
90
+ // The panel is resizable and the browser window is not the only thing that
91
+ // changes its size, so follow the element rather than the window.
92
+ this.observer = new ResizeObserver(() => this.fit());
93
+ this.observer.observe(container);
94
+
95
+ this.dataHandler = null;
96
+ this.terminal.onData((data: Uint8Array) => this.dataHandler?.(data));
97
+ }
98
+
99
+ get columns() {
100
+ return this.terminal.cols;
101
+ }
102
+
103
+ get rows() {
104
+ return this.terminal.rows;
105
+ }
106
+
107
+ onData(handler) {
108
+ this.dataHandler = handler;
109
+ }
110
+
111
+ onResize(handler) {
112
+ this.terminal.onResize(({ cols, rows }) => handler(cols, rows));
113
+ }
114
+
115
+ write(data: Uint8Array) {
116
+ this.terminal.write(data);
117
+ }
118
+
119
+ /** Gateway-side notices, kept visually apart from what the host prints. */
120
+ notice(text: string, tone = 'info') {
121
+ const color = { info: '\x1b[38;5;245m', good: '\x1b[38;5;114m', bad: '\x1b[38;5;203m' }[tone] || '';
122
+ this.terminal.write(`${color}${text}\x1b[0m\r\n`);
123
+ }
124
+
125
+ clear() {
126
+ this.terminal.clear();
127
+ }
128
+
129
+ focus() {
130
+ this.terminal.focus();
131
+ }
132
+
133
+ /**
134
+ * Size the terminal to the panel, choosing the font so MIN_COLUMNS fit.
135
+ *
136
+ * The columns matter more than the type size here: the commands people run in
137
+ * a side panel -- `ls -l`, `top`, anything in PowerShell that prints a table
138
+ * -- are laid out for 80 columns, and a narrower grid wraps every row into an
139
+ * unreadable stack.
140
+ *
141
+ * `proposeDimensions()` reports the grid the current font size yields, and a
142
+ * monospace cell's width scales with its font size, so `fontSize * cols /
143
+ * MIN_COLUMNS` lands close to the size that gives MIN_COLUMNS. It is measured
144
+ * rather than assumed because the cell-width-to-font-size ratio belongs to
145
+ * whichever font the platform picked. A second pass settles the rounding.
146
+ * Changing the font resizes the element, which the ResizeObserver sees, so
147
+ * the guard keeps that from recursing.
148
+ */
149
+ fit() {
150
+ if (this.fitting) return;
151
+ if (this.container.clientWidth <= 0 || this.container.clientHeight <= 0) return;
152
+
153
+ this.fitting = true;
154
+ try {
155
+ for (let pass = 0; pass < 2; pass++) {
156
+ const proposed = this.fitAddon.proposeDimensions();
157
+ if (!proposed || !(proposed.cols > 0)) break;
158
+
159
+ const current = this.terminal.options.fontSize || BASE_FONT_SIZE;
160
+ const wanted = Math.min(
161
+ BASE_FONT_SIZE,
162
+ Math.max(MIN_FONT_SIZE, Math.floor((current * proposed.cols) / MIN_COLUMNS))
163
+ );
164
+ if (wanted === current) break;
165
+ this.terminal.options.fontSize = wanted;
166
+ }
167
+
168
+ this.fitAddon.fit();
169
+
170
+ // A panel narrow enough to be short of MIN_COLUMNS even at the smallest
171
+ // type keeps the columns and scrolls sideways instead, because wrapped
172
+ // output is harder to read than output that has to be scrolled to.
173
+ const short = this.terminal.cols < MIN_COLUMNS;
174
+ this.container.classList.toggle('is-narrow', short);
175
+ if (short) this.terminal.resize(MIN_COLUMNS, this.terminal.rows);
176
+ } catch {
177
+ // A hidden panel has no size to fit to; the next resize will catch it.
178
+ } finally {
179
+ this.fitting = false;
180
+ }
181
+ }
182
+
183
+ /** The grid the host is being told about, for a status line or a log. */
184
+ get geometry() {
185
+ return {
186
+ columns: this.terminal.cols,
187
+ rows: this.terminal.rows,
188
+ fontSize: this.terminal.options.fontSize,
189
+ };
190
+ }
191
+
192
+ get selection() {
193
+ return this.terminal.getSelection();
194
+ }
195
+
196
+ /**
197
+ * Read one line from the user, for a password or a one-time code the host
198
+ * asked for during authentication. The shell is not open yet, so the
199
+ * terminal is free to be a prompt.
200
+ */
201
+ readLine({ prompt = '', echo = false } = {}) {
202
+ return new Promise((resolve) => {
203
+ const previous = this.dataHandler;
204
+ let answer = '';
205
+
206
+ this.terminal.write(prompt.replaceAll('\n', '\r\n'));
207
+ this.dataHandler = (data: Uint8Array) => {
208
+ for (const character of data) {
209
+ const code = character.charCodeAt(0);
210
+
211
+ if (code === 0x0d || code === 0x0a) { // Enter, or a pasted line break
212
+ this.terminal.write('\r\n');
213
+ this.dataHandler = previous;
214
+ resolve(answer);
215
+ return;
216
+ }
217
+ if (code === 0x7f || code === 0x08) { // Backspace
218
+ if (answer.length === 0) continue;
219
+ answer = answer.slice(0, -1);
220
+ if (echo) this.terminal.write('\b \b');
221
+ continue;
222
+ }
223
+ if (code === 0x03) { // Ctrl-C
224
+ this.terminal.write('^C\r\n');
225
+ this.dataHandler = previous;
226
+ resolve('');
227
+ return;
228
+ }
229
+ if (code < 0x20) continue;
230
+
231
+ answer += character;
232
+ if (echo) this.terminal.write(character);
233
+ }
234
+ };
235
+ });
236
+ }
237
+
238
+ destroy() {
239
+ this.observer?.disconnect();
240
+ this.terminal.dispose();
241
+ }
242
+ }