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.
- package/README.md +50 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +7 -0
- package/dist/protocols/index.js +35 -0
- package/dist/protocols/index.js.map +7 -0
- package/dist/protocols/rdp/buffer.js +170 -0
- package/dist/protocols/rdp/buffer.js.map +7 -0
- package/dist/protocols/rdp/caps.js +260 -0
- package/dist/protocols/rdp/caps.js.map +7 -0
- package/dist/protocols/rdp/cert.js +135 -0
- package/dist/protocols/rdp/cert.js.map +7 -0
- package/dist/protocols/rdp/client.js +1068 -0
- package/dist/protocols/rdp/client.js.map +7 -0
- package/dist/protocols/rdp/cliprdr.js +240 -0
- package/dist/protocols/rdp/cliprdr.js.map +7 -0
- package/dist/protocols/rdp/credssp.js +254 -0
- package/dist/protocols/rdp/credssp.js.map +7 -0
- package/dist/protocols/rdp/crypto.js +369 -0
- package/dist/protocols/rdp/crypto.js.map +7 -0
- package/dist/protocols/rdp/display.js +589 -0
- package/dist/protocols/rdp/display.js.map +7 -0
- package/dist/protocols/rdp/gcc.js +132 -0
- package/dist/protocols/rdp/gcc.js.map +7 -0
- package/dist/protocols/rdp/keymap.js +132 -0
- package/dist/protocols/rdp/keymap.js.map +7 -0
- package/dist/protocols/rdp/mcs.js +223 -0
- package/dist/protocols/rdp/mcs.js.map +7 -0
- package/dist/protocols/rdp/ntlm.js +305 -0
- package/dist/protocols/rdp/ntlm.js.map +7 -0
- package/dist/protocols/rdp/pdu.js +444 -0
- package/dist/protocols/rdp/pdu.js.map +7 -0
- package/dist/protocols/rdp/rail.js +383 -0
- package/dist/protocols/rdp/rail.js.map +7 -0
- package/dist/protocols/rdp/rle.js +276 -0
- package/dist/protocols/rdp/rle.js.map +7 -0
- package/dist/protocols/rdp/sec.js +156 -0
- package/dist/protocols/rdp/sec.js.map +7 -0
- package/dist/protocols/rdp/session.js +307 -0
- package/dist/protocols/rdp/session.js.map +7 -0
- package/dist/protocols/rdp/tls.js +354 -0
- package/dist/protocols/rdp/tls.js.map +7 -0
- package/dist/protocols/rdp/vchannel.js +59 -0
- package/dist/protocols/rdp/vchannel.js.map +7 -0
- package/dist/protocols/rdp/x224.js +110 -0
- package/dist/protocols/rdp/x224.js.map +7 -0
- package/dist/protocols/ssh/kex.js +177 -0
- package/dist/protocols/ssh/kex.js.map +7 -0
- package/dist/protocols/ssh/messages.js +55 -0
- package/dist/protocols/ssh/messages.js.map +7 -0
- package/dist/protocols/ssh/session.js +608 -0
- package/dist/protocols/ssh/session.js.map +7 -0
- package/dist/protocols/ssh/terminal.js +191 -0
- package/dist/protocols/ssh/terminal.js.map +7 -0
- package/dist/protocols/ssh/transport.js +611 -0
- package/dist/protocols/ssh/transport.js.map +7 -0
- package/dist/protocols/ssh/wire.js +189 -0
- package/dist/protocols/ssh/wire.js.map +7 -0
- package/dist/protocols/types.js +1 -0
- package/dist/protocols/types.js.map +7 -0
- package/dist/protocols/vnc/input.js +16 -0
- package/dist/protocols/vnc/input.js.map +7 -0
- package/dist/protocols/vnc/session.js +259 -0
- package/dist/protocols/vnc/session.js.map +7 -0
- package/dist/shared/connection.js +114 -0
- package/dist/shared/connection.js.map +7 -0
- package/dist/shared/device.js +1 -0
- package/dist/shared/device.js.map +7 -0
- package/dist/shared/hosts.js +133 -0
- package/dist/shared/hosts.js.map +7 -0
- package/dist/shared/icons.js +42 -0
- package/dist/shared/icons.js.map +7 -0
- package/dist/shared/protocol.js +40 -0
- package/dist/shared/protocol.js.map +7 -0
- package/dist/shared/signals.js +42 -0
- package/dist/shared/signals.js.map +7 -0
- package/package.json +82 -0
- package/src/index.ts +14 -0
- package/src/protocols/index.ts +72 -0
- package/src/protocols/rdp/buffer.ts +204 -0
- package/src/protocols/rdp/caps.ts +304 -0
- package/src/protocols/rdp/cert.ts +190 -0
- package/src/protocols/rdp/client.ts +1363 -0
- package/src/protocols/rdp/cliprdr.ts +344 -0
- package/src/protocols/rdp/credssp.ts +319 -0
- package/src/protocols/rdp/crypto.ts +416 -0
- package/src/protocols/rdp/display.ts +750 -0
- package/src/protocols/rdp/gcc.ts +162 -0
- package/src/protocols/rdp/keymap.ts +68 -0
- package/src/protocols/rdp/mcs.ts +283 -0
- package/src/protocols/rdp/ntlm.ts +380 -0
- package/src/protocols/rdp/pdu.ts +497 -0
- package/src/protocols/rdp/rail.ts +480 -0
- package/src/protocols/rdp/rle.ts +343 -0
- package/src/protocols/rdp/sec.ts +184 -0
- package/src/protocols/rdp/session.ts +393 -0
- package/src/protocols/rdp/tls.ts +452 -0
- package/src/protocols/rdp/vchannel.ts +89 -0
- package/src/protocols/rdp/x224.ts +139 -0
- package/src/protocols/ssh/kex.ts +227 -0
- package/src/protocols/ssh/messages.ts +54 -0
- package/src/protocols/ssh/session.ts +878 -0
- package/src/protocols/ssh/terminal.ts +242 -0
- package/src/protocols/ssh/transport.ts +732 -0
- package/src/protocols/ssh/wire.ts +220 -0
- package/src/protocols/types.ts +146 -0
- package/src/protocols/vnc/input.ts +13 -0
- package/src/protocols/vnc/novnc.d.ts +75 -0
- package/src/protocols/vnc/session.ts +367 -0
- package/src/shared/connection.ts +206 -0
- package/src/shared/device.ts +28 -0
- package/src/shared/hosts.ts +240 -0
- package/src/shared/icons.ts +49 -0
- package/src/shared/protocol.ts +55 -0
- package/src/shared/signals.ts +67 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/protocols/ssh/session.ts"],
|
|
4
|
+
"sourcesContent": ["// An SSH session: authentication, one interactive shell channel, and the\n// events a terminal needs to render it.\n//\n// The transport below this owns the cryptography. This module owns the\n// conversation: proving who the user is, asking for a pseudo-terminal, and\n// moving bytes between the shell and the screen.\n\nimport { MSG, EXTENDED_DATA_STDERR } from './messages';\nimport { SshReader, SshWriter, decodeUtf8, encodeUtf8, toBase64 } from './wire';\nimport { SshTransport } from './transport';\n\nexport const DEFAULT_PORT = 22;\n\nconst SERVICE = 'ssh-connection';\nconst TERMINAL_TYPE = 'xterm-256color';\n\n// A shell is not a bulk transfer, so a modest window costs nothing and is\n// topped up long before it runs out.\nconst INITIAL_WINDOW = 2 * 1024 * 1024;\nconst MAX_PACKET = 32 * 1024;\nconst WINDOW_REFILL_THRESHOLD = INITIAL_WINDOW / 2;\n\n/** Terminal modes: only the terminator, so the host keeps its own defaults. */\nconst PTY_MODES = new Uint8Array([0]);\n\n/**\n * How patient `runCommand` is, in three different senses.\n *\n * A single deadline cannot tell \"the shell ignored me\" apart from \"the\n * installer is three minutes into a download\", and a short one turns every\n * real piece of work -- a package install, a build, a large copy -- into a\n * false failure that also leaves the command running on the far side.\n *\n * So the wait is judged by what the shell is doing. It has START_MS to\n * acknowledge the command at all; after that it may take as long as it likes\n * as long as it keeps printing, and only silence longer than IDLE_MS, or a run\n * longer than MAX_MS, is treated as stuck.\n */\nexport const COMMAND_START_MS = 20_000;\nexport const COMMAND_IDLE_MS = 5 * 60_000;\nexport const COMMAND_MAX_MS = 60 * 60_000;\n\n/** Nothing for the terminal to draw, while the framing is being hidden. */\nconst NO_BYTES = new Uint8Array(0);\n\n/**\n * One shell word holding `value` literally.\n *\n * Single quotes suspend every kind of expansion the shell has, so the only\n * character that needs handling is the closing quote itself: the string is left\n * for a moment, an escaped quote is added, and it starts again.\n */\nfunction singleQuote(value) {\n return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\n/**\n * The shell a machine answers with, guessed from its prompt.\n *\n * Windows OpenSSH hands out cmd.exe by default, and a POSIX-framed command\n * there does not fail loudly -- it prints `'printf' is not recognized` and\n * leaves the caller waiting for markers that will never come.\n */\nfunction shellFromPrompt(text: string) {\n const tail = text.slice(-400);\n if (/(?:^|\\n)PS [A-Za-z]:\\\\[^\\n]*>\\s*$/.test(tail)) return 'powershell';\n if (/(?:^|\\n)[^\\n]*[A-Za-z]:\\\\[^\\n]*>\\s*$/.test(tail)) return 'cmd';\n return '';\n}\n\n/** A shell saying it has never heard of the command that frames the output. */\nconst FRAMING_REJECTED = /is not recognized as an internal or external command|command not found|not recognized as the name of a cmdlet|CommandNotFoundException/i;\n\n/**\n * The one line that runs `command` and marks where its output starts and ends.\n *\n * Each shell gets its own, because none of the three agree on how to print a\n * string, run a command and read back its exit status.\n */\nexport function frameCommand(family, command: string, id) {\n if (family === 'cmd') {\n // `&` chains unconditionally, and the delayed-expansion problem is dodged\n // by reading ERRORLEVEL through `call`, which re-expands its arguments.\n return `echo.&echo CCB${id}&${command}&call echo.CCE${id} %^ERRORLEVEL%\\r`;\n }\n\n if (family === 'powershell') {\n // A base64 payload keeps a trailing comment, newline or semicolon from\n // swallowing the closing marker. Dot-sourcing the resulting script block\n // also keeps Set-Location and environment changes in this same shell.\n //\n // $LASTEXITCODE is read before $?, because the two answer different\n // questions and only the first one is about the command. $? says whether\n // the dot-source raised an error record, and launching a program that goes\n // on to fail is not an error record: a failing `git`, `msiexec` or nested\n // `powershell.exe` leaves $? True and its own status in $LASTEXITCODE, so\n // asking $? first reports a failed command as exit 0. $LASTEXITCODE is\n // cleared just above, so a non-null value can only have come from a\n // program this command ran, and $? still decides for a payload that ended\n // in a cmdlet and never set it.\n const encoded = toBase64(encodeUtf8(command));\n return `Write-Output \"\"; Write-Output (\"CCB\" + \"${id}\"); `\n + `$__ccCode = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')); `\n + `$global:LASTEXITCODE = $null; . ([ScriptBlock]::Create($__ccCode)); $__ccOk = $?; `\n + `$__cc = if ($LASTEXITCODE -ne $null) { $LASTEXITCODE } elseif ($__ccOk) { 0 } else { 1 }; `\n + `Write-Output (\"CCE\" + \"${id} \" + $__cc)\\r`;\n }\n\n // `printf '%s%s'` joins the marker inside the shell; `__cc` holds the status\n // so the command's own exit code survives the second printf.\n //\n // It all goes on one line, because an interactive shell echoes each line as\n // it reads it: a second line would be echoed after the command had already\n // printed, landing inside the captured output rather than in front of the\n // opening marker where it can be discarded.\n //\n // The command travels through `eval` as a single quoted word rather than\n // being spliced between the semicolons directly. Spliced in, three ordinary\n // commands break the framing: one ending in `&` puts `&` next to `;`, which\n // is a syntax error; one ending in a newline leaves a bare `;`, likewise; and\n // one ending in `# a comment` swallows the closing marker, so the call only\n // returns when it times out. Inside `eval` the command is a string, so none\n // of it can reach the outer line. `eval` runs in this same shell, so the\n // working directory, the environment and `$?` all still behave as if the\n // user had typed it.\n return `printf '\\\\n%s%s\\\\n' 'CCB' '${id}'; eval ${singleQuote(command)}; `\n + `__cc=$?; printf '\\\\n%s%s %s\\\\n' 'CCE' '${id}' \"$__cc\"\\r`;\n}\n\n/**\n * Plain text from a shell transcript. An interactive shell dresses its output\n * with colour, cursor moves and title changes, none of which mean anything to\n * a model reading the result.\n */\nfunction stripTerminalCodes(text: string) {\n return text\n // OSC sequences (window titles, and iTerm/VS Code shell integration)\n .replace(/\\u001b\\][^\\u0007\\u001b]*(?:\\u0007|\\u001b\\\\)/g, '')\n // CSI sequences: colour, cursor movement, erase\n .replace(/\\u001b\\[[0-9;?]*[ -/]*[@-~]/g, '')\n // remaining two-character escapes\n .replace(/\\u001b[@-Z\\\\-_]/g, '')\n // carriage returns a terminal would have overwritten in place\n .replace(/\\r\\n/g, '\\n')\n .replace(/\\r/g, '');\n}\n\n/**\n * The same bytes, minus the scaffolding around a tool command.\n *\n * An interactive shell echoes whatever is typed into it, and what\n * `frameCommand` types is a `printf`-and-`eval` line -- or, in PowerShell, a\n * base64 blob. Left alone it buries the command's own output in the\n * scrollback the user is trying to read. The joined markers are safe to cut\n * on for the same reason `runCommand` finds them: the echoed line contains\n * their halves, never the joined marker itself.\n *\n * Only display is filtered. `detail.bytes` stays exactly what the host sent,\n * because that is what `runCommand` reads the result out of.\n */\nfunction filterDisplay(session, bytes: Uint8Array) {\n const filter = session.displayFilter;\n if (!filter) return bytes;\n\n filter.pending += decodeUtf8(bytes);\n let shown = '';\n\n if (filter.stage === 'before') {\n const at = filter.pending.indexOf(filter.begin);\n if (at === -1) return NO_BYTES; // still inside the echoed wrapper\n filter.pending = filter.pending.slice(at + filter.begin.length).replace(/^\\r?\\n/, '');\n filter.stage = 'inside';\n }\n\n if (filter.stage === 'inside') {\n const at = filter.pending.indexOf(filter.end);\n if (at === -1) {\n shown = filter.pending;\n filter.pending = '';\n return shown ? encodeUtf8(shown) : NO_BYTES;\n }\n shown = filter.pending.slice(0, at);\n filter.pending = filter.pending.slice(at);\n filter.stage = 'closing';\n }\n\n // The closing marker carries the exit status on its own line. Drop that\n // line and hand back everything after it: the prompt the shell draws next\n // belongs to the user again.\n const newline = filter.pending.indexOf('\\n');\n if (newline !== -1) {\n shown += filter.pending.slice(newline + 1);\n session.displayFilter = null;\n }\n return shown ? encodeUtf8(shown) : NO_BYTES;\n}\n\n/**\n * Stop filtering, showing anything held back on the way out.\n *\n * Text held before the opening marker is normally the echoed wrapper, but if\n * the command never started it is the shell explaining why -- the one case\n * where the held text is the whole answer, so it must not be swallowed.\n */\nfunction releaseDisplayFilter(session) {\n const filter = session.displayFilter;\n session.displayFilter = null;\n if (!filter || filter.stage !== 'before' || !filter.pending) return;\n session.dispatchEvent(new CustomEvent('data', {\n detail: { bytes: NO_BYTES, display: encodeUtf8(filter.pending), isStderr: false },\n }));\n}\n\nexport class SshSession extends EventTarget {\n /**\n * @param {string} url gateway WebSocket URL\n * @param {object} options\n * @param {string} options.username\n * @param {string} [options.password]\n * @param {'powershell'} [options.preferredShell]\n * @param {(info: object) => Promise<boolean>} [options.verifyHost]\n * @param {(prompt: object) => Promise<string>} [options.requestInput]\n */\n constructor(url: string, options = {}) {\n super();\n\n this.options = options;\n this.username = options.username || '';\n this.password = options.password || '';\n this.requestInput = options.requestInput || (async () => '');\n this.columns = options.columns || 80;\n this.rows = options.rows || 24;\n\n this.channelId = 0;\n this.remoteChannelId = null;\n this.remoteWindow = 0;\n this.remoteMaxPacket = MAX_PACKET;\n this.localWindow = INITIAL_WINDOW;\n this.pendingWrites = [];\n this.shellOpen = false;\n this.commandInFlight = false;\n /** Set while a tool command runs, to keep its framing off the screen. */\n this.displayFilter = null;\n /** True only after the requested interactive shell is ready for commands. */\n this.readySignaled = false;\n this.powerShellAttempted = false;\n this.powerShellTimer = null;\n /** '', 'posix', 'cmd' or 'powershell'; learned from the prompt. */\n this.shellFamily = '';\n this.promptTail = '';\n this.exitStatus = null;\n this.authMethods = [];\n this.closed = false;\n this.waiters = new Map();\n\n this.transport = new SshTransport(url, {\n verifyHost: options.verifyHost || (async () => true),\n log: (step: number, detail) => console.info(`[SSH] ${step}`, detail),\n });\n\n this.transport.addEventListener('packet', (event) => {\n this.#handlePacket(event.detail.type, event.detail.payload);\n });\n this.transport.addEventListener('greeting', (event) => {\n for (const line of event.detail.lines) this.#emitBanner(`${line}\\r\\n`);\n });\n this.transport.addEventListener('banner', (event) => this.#emitBanner(event.detail.text));\n this.transport.addEventListener('keys', (event) => {\n this.fingerprint = event.detail.fingerprint;\n this.#status('secured', `Host key ${event.detail.fingerprint} verified.`);\n this.dispatchEvent(new CustomEvent('secure', { detail: event.detail }));\n if (event.detail.firstTime) void this.#authenticate();\n });\n this.transport.addEventListener('hostdisconnect', (event) => {\n this.lastMessage = event.detail.reason;\n });\n this.transport.addEventListener('error', (event) => {\n this.lastMessage = event.detail.message;\n this.dispatchEvent(new CustomEvent('error', { detail: event.detail }));\n });\n this.transport.addEventListener('close', (event) => {\n if (this.closed) return;\n this.closed = true;\n clearTimeout(this.powerShellTimer);\n this.#abandonWaiters(event.detail.message || 'The SSH connection closed.');\n this.dispatchEvent(new CustomEvent('close', {\n detail: {\n ...event.detail,\n message: event.detail.message || this.lastMessage || '',\n exitStatus: this.exitStatus,\n },\n }));\n });\n }\n\n connect() {\n this.#status('connecting', 'Opening the SSH transport\u2026');\n this.transport.connect();\n }\n\n disconnect() {\n if (this.closed) return;\n clearTimeout(this.powerShellTimer);\n if (this.shellOpen && this.remoteChannelId !== null) {\n this.transport.send(new SshWriter(8).u8(MSG.CHANNEL_CLOSE).u32(this.remoteChannelId).take());\n }\n this.transport.disconnect('Closed by the user');\n }\n\n /** Send keystrokes, or anything else the terminal produces, to the shell. */\n write(data: Uint8Array) {\n const bytes = typeof data === 'string' ? encodeUtf8(data) : data;\n if (!bytes.length) return;\n this.pendingWrites.push(bytes);\n this.#flushWrites();\n }\n\n /**\n * Run one command in this shell and return what it printed.\n *\n * Deliberately the *same* shell the user is looking at, not a second channel:\n * a command the assistant runs inherits the working directory, the\n * environment and the login the user already has, and it appears in their\n * terminal as it happens. An agent acting on a machine invisibly is worse\n * than an agent that is slower to read.\n *\n * The output is framed by markers rather than measured, because an\n * interactive shell echoes its input and prints a prompt around it. The\n * markers are assembled by the shell from two pieces, so the echoed command\n * line -- which contains the pieces but not the joined marker -- never looks\n * like the real boundary.\n *\n * A `command` event brackets the run so the terminal can label it, and the\n * three deadlines above decide when a command is stuck rather than slow.\n */\n runCommand(command: string, {\n startMs = COMMAND_START_MS,\n idleMs = COMMAND_IDLE_MS,\n maxMs = COMMAND_MAX_MS,\n } = {}) {\n const text = String(command || '').trim();\n if (!text) return Promise.reject(new Error('No command was given.'));\n if (!this.shellOpen || this.closed) {\n return Promise.reject(new Error('The SSH shell is not open.'));\n }\n if (this.readySignaled === false) {\n return Promise.reject(new Error('The SSH shell is still starting.'));\n }\n if (this.commandInFlight) {\n return Promise.reject(new Error('Another command is still running in this shell.'));\n }\n\n const id = Array.from(crypto.getRandomValues(new Uint8Array(6)))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('');\n const begin = `CCB${id}`;\n const end = `CCE${id}`;\n // What the prompt showed, or POSIX until something says otherwise.\n const family = this.shellFamily || 'posix';\n\n this.commandInFlight = true;\n const startedAt = Date.now();\n\n // The wrapper is machinery, not something the user asked to read, so the\n // terminal is shown the command's own output instead of the line that\n // frames it. The announcement below is what marks it in the scrollback.\n this.displayFilter = { begin, end, stage: 'before', pending: '' };\n this.dispatchEvent(new CustomEvent('command', {\n detail: { phase: 'start', id, command: text },\n }));\n\n return new Promise((resolve, reject) => {\n let transcript = '';\n let started = false;\n let startTimer = null;\n let idleTimer = null;\n let maxTimer = null;\n\n const finish = (settle) => {\n clearTimeout(startTimer);\n clearTimeout(idleTimer);\n clearTimeout(maxTimer);\n this.removeEventListener('data', onData);\n this.removeEventListener('close', onClose);\n this.commandInFlight = false;\n releaseDisplayFilter(this);\n settle();\n };\n\n /** End the call, and tell the terminal how it went. */\n const done = (result) => finish(() => {\n this.dispatchEvent(new CustomEvent('command', {\n detail: {\n phase: 'end',\n id,\n command: text,\n exitStatus: result.exitStatus,\n timedOut: result.timedOut,\n started: result.started !== false,\n reason: result.reason,\n durationMs: result.durationMs,\n },\n }));\n resolve(result);\n });\n\n const fail = (error) => finish(() => {\n this.dispatchEvent(new CustomEvent('command', {\n detail: {\n phase: 'end',\n id,\n command: text,\n exitStatus: null,\n timedOut: false,\n started,\n durationMs: Date.now() - startedAt,\n error: error.message,\n },\n }));\n reject(error);\n });\n\n /**\n * What the command has printed so far. Without the opening marker the\n * shell never started it, so the transcript is its echo of the wrapper\n * -- noise, not output.\n */\n const partialOutput = () => {\n const at = transcript.indexOf(begin);\n return at === -1\n ? ''\n : stripTerminalCodes(transcript.slice(at + begin.length)).replace(/^\\r?\\n/, '').replace(/\\s+$/, '');\n };\n\n /** Interrupt whatever is still running, so the shell is usable again. */\n const giveUp = (reason) => {\n this.write('\\u0003');\n done({\n output: partialOutput(),\n exitStatus: null,\n timedOut: true,\n started,\n reason,\n durationMs: Date.now() - startedAt,\n });\n };\n\n const onData = (event) => {\n transcript += decodeUtf8(event.detail.bytes);\n\n if (!started && transcript.indexOf(begin) !== -1) {\n started = true;\n clearTimeout(startTimer);\n startTimer = null;\n }\n // A command that is still printing is still working, however long it\n // has been at it, so every chunk buys another idle window.\n if (started) {\n clearTimeout(idleTimer);\n idleTimer = setTimeout(() => giveUp('idle'), idleMs);\n }\n\n const endAt = transcript.indexOf(`${end} `);\n if (endAt === -1) {\n // The shell has rejected the wrapper itself, so the closing marker is\n // never coming. Say so now instead of holding the caller until the\n // deadline with a transcript of the wrapper it could not read.\n if (!started && FRAMING_REJECTED.test(stripTerminalCodes(transcript))) {\n const guessed = shellFromPrompt(stripTerminalCodes(transcript)) || 'cmd';\n const wrongGuess = family !== guessed;\n // Learn from it, so the next command is framed the shell's way.\n this.shellFamily = guessed;\n fail(new Error(\n wrongGuess\n ? `This shell is ${guessed === 'powershell' ? 'PowerShell' : 'Windows cmd'}, not a POSIX shell, `\n + 'and it rejected the wrapper used to capture output. The shell has been noted; '\n + 'run the command again and it will be framed correctly.'\n : `The shell rejected the wrapper used to capture output: ${\n stripTerminalCodes(transcript).split('\\n').filter(Boolean).pop() || 'no detail'\n }`\n ));\n }\n return;\n }\n\n const tail = transcript.slice(endAt);\n const status = parseInt(tail.slice(end.length + 1).trim(), 10);\n // Everything the shell printed between the two markers, with the\n // marker lines themselves and any terminal control codes removed.\n const beginAt = transcript.indexOf(begin);\n const body = beginAt === -1\n ? transcript.slice(0, endAt)\n : transcript.slice(beginAt + begin.length, endAt);\n\n done({\n output: stripTerminalCodes(body).replace(/^\\r?\\n/, '').replace(/\\s+$/, ''),\n exitStatus: Number.isInteger(status) ? status : null,\n timedOut: false,\n started: true,\n durationMs: Date.now() - startedAt,\n });\n };\n\n const onClose = () => fail(new Error('The SSH session closed while the command was running.'));\n\n this.addEventListener('data', onData);\n this.addEventListener('close', onClose);\n\n // Three deadlines, none of which is \"how long may this command take\".\n startTimer = setTimeout(() => giveUp('start'), startMs);\n idleTimer = setTimeout(() => giveUp('idle'), idleMs);\n maxTimer = setTimeout(() => giveUp('max'), maxMs);\n\n this.write(frameCommand(family, text, id));\n });\n }\n\n /** Tell the shell its window changed, so full-screen programs redraw. */\n resize(columns: number, rows: number) {\n const nextColumns = Math.max(1, Math.floor(columns));\n const nextRows = Math.max(1, Math.floor(rows));\n if (nextColumns === this.columns && nextRows === this.rows) return;\n\n this.columns = nextColumns;\n this.rows = nextRows;\n if (!this.shellOpen || this.remoteChannelId === null) return;\n\n this.transport.send(new SshWriter(64)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('window-change').boolean(false)\n .u32(nextColumns).u32(nextRows).u32(0).u32(0)\n .take());\n }\n\n // --- plumbing ------------------------------------------------------------\n\n #status(phase, message: string) {\n this.dispatchEvent(new CustomEvent('status', { detail: { phase, message } }));\n }\n\n #emitBanner(text: string) {\n if (text) this.dispatchEvent(new CustomEvent('banner', { detail: { text } }));\n }\n\n /** Wait for one of a few message types: the handshakes read linearly that way. */\n #expect(...types) {\n return new Promise((resolve, reject) => {\n for (const type of types) this.waiters.set(type, { resolve, reject });\n });\n }\n\n #settle(type, payload: Uint8Array) {\n const waiter = this.waiters.get(type);\n if (!waiter) return false;\n this.waiters.clear();\n waiter.resolve({ type, payload });\n return true;\n }\n\n /** A closed transport must not leave a handshake waiting forever. */\n #abandonWaiters(message: string) {\n if (this.waiters.size === 0) return;\n const pending = [...new Set(this.waiters.values())];\n this.waiters.clear();\n for (const waiter of pending) waiter.reject(new Error(message));\n }\n\n #handlePacket(type, payload: Uint8Array) {\n if (this.#settle(type, payload)) return;\n\n switch (type) {\n case MSG.USERAUTH_BANNER:\n this.#emitBanner(new SshReader(payload, 1).string().replaceAll('\\n', '\\r\\n'));\n return;\n case MSG.CHANNEL_DATA: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n this.#consume(reader.stringBytes());\n return;\n }\n case MSG.CHANNEL_EXTENDED_DATA: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n const dataType = reader.u32();\n const bytes = reader.stringBytes();\n this.#consume(bytes, dataType === EXTENDED_DATA_STDERR);\n return;\n }\n case MSG.CHANNEL_WINDOW_ADJUST: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n this.remoteWindow += reader.u32();\n this.#flushWrites();\n return;\n }\n case MSG.CHANNEL_REQUEST: {\n const reader = new SshReader(payload, 1);\n reader.u32();\n const request = reader.string();\n const wantReply = reader.boolean();\n if (request === 'exit-status') this.exitStatus = reader.u32();\n if (request === 'exit-signal') this.exitSignal = reader.string();\n if (wantReply && this.remoteChannelId !== null) {\n this.transport.send(new SshWriter(8).u8(MSG.CHANNEL_SUCCESS).u32(this.remoteChannelId).take());\n }\n return;\n }\n case MSG.CHANNEL_EOF:\n return;\n case MSG.CHANNEL_CLOSE:\n this.shellOpen = false;\n this.#status('closed', 'The remote shell ended.');\n this.transport.disconnect('Shell closed');\n return;\n default:\n return;\n }\n }\n\n /**\n * Watch the prompt for which shell this machine answers with, so the first\n * command is framed correctly rather than discovering it by failing.\n */\n #noteShellFamily(bytes: Uint8Array) {\n this.promptTail = (this.promptTail + stripTerminalCodes(decodeUtf8(bytes))).slice(-400);\n const family = shellFromPrompt(this.promptTail);\n if (!family) return;\n\n if (family === 'cmd' && !this.powerShellAttempted) {\n this.#startPowerShell();\n return;\n }\n\n // While PowerShell is starting, the old cmd prompt may still be the last\n // prompt in the transcript. Wait for PowerShell's own prompt rather than\n // briefly advertising cmd as ready and racing an AI tool call into it.\n if (family === 'cmd' && this.powerShellAttempted && !this.readySignaled) return;\n\n this.shellFamily = family;\n if (family === 'powershell' && !this.readySignaled) {\n this.#markReady('Connected \u00B7 PowerShell');\n }\n }\n\n /** Replace Windows OpenSSH's usual cmd.exe with an interactive PowerShell. */\n #startPowerShell() {\n if (this.powerShellAttempted || !this.shellOpen) return;\n this.powerShellAttempted = true;\n this.readySignaled = false;\n this.shellFamily = '';\n this.#status('opening', 'Starting PowerShell\u2026');\n this.write('powershell.exe -NoLogo\\r');\n\n // A customized prompt may not begin with \"PS\". Keep the terminal usable\n // in that case; the command was already sent and PowerShell is still the\n // overwhelmingly likely interpreter after this short settling period.\n this.powerShellTimer = setTimeout(() => {\n if (this.readySignaled || this.closed) return;\n this.shellFamily = 'powershell';\n this.#markReady('Connected \u00B7 PowerShell');\n }, 2_000);\n }\n\n #markReady(message = 'Connected.') {\n clearTimeout(this.powerShellTimer);\n this.powerShellTimer = null;\n this.readySignaled = true;\n this.#status('ready', message);\n this.dispatchEvent(new CustomEvent('ready', {\n detail: { fingerprint: this.fingerprint, shellFamily: this.shellFamily || 'posix' },\n }));\n }\n\n /** Shell output, with the receive window topped up before it runs dry. */\n #consume(bytes: Uint8Array, isStderr = false) {\n if (!bytes.length) return;\n this.#noteShellFamily(bytes);\n this.dispatchEvent(new CustomEvent('data', {\n detail: { bytes, display: filterDisplay(this, bytes), isStderr },\n }));\n\n this.localWindow -= bytes.length;\n if (this.localWindow > WINDOW_REFILL_THRESHOLD || this.remoteChannelId === null) return;\n\n const increment = INITIAL_WINDOW - this.localWindow;\n this.localWindow = INITIAL_WINDOW;\n this.transport.send(new SshWriter(16)\n .u8(MSG.CHANNEL_WINDOW_ADJUST).u32(this.remoteChannelId).u32(increment).take());\n }\n\n #flushWrites() {\n if (!this.shellOpen || this.remoteChannelId === null) return;\n\n while (this.pendingWrites.length > 0) {\n const next = this.pendingWrites[0];\n const room = Math.min(this.remoteWindow, this.remoteMaxPacket - 64);\n if (room <= 0) return;\n\n const chunk = next.length <= room ? next : next.subarray(0, room);\n if (chunk.length === next.length) this.pendingWrites.shift();\n else this.pendingWrites[0] = next.subarray(chunk.length);\n\n this.remoteWindow -= chunk.length;\n this.transport.send(new SshWriter(chunk.length + 16)\n .u8(MSG.CHANNEL_DATA).u32(this.remoteChannelId).string(chunk).take());\n }\n }\n\n // --- authentication ------------------------------------------------------\n\n async #authenticate() {\n try {\n this.#status('authenticating', `Authenticating as ${this.username}\u2026`);\n this.transport.send(new SshWriter(32).u8(MSG.SERVICE_REQUEST).string('ssh-userauth').take());\n await this.#expect(MSG.SERVICE_ACCEPT);\n\n // \"none\" is how a client learns which methods a host will accept, and a\n // host with no authentication at all answers it with success.\n if (await this.#tryNone()) return void await this.#openShell();\n\n if (this.authMethods.includes('password') && this.password) {\n if (await this.#tryPassword(this.password)) return void await this.#openShell();\n }\n\n if (this.authMethods.includes('keyboard-interactive')) {\n if (await this.#tryKeyboardInteractive()) return void await this.#openShell();\n\n // The stored password was not what the host wanted -- a one-time code,\n // or simply the wrong one. Ask, the way the command-line client does.\n if (this.authMethods.includes('keyboard-interactive')) {\n if (await this.#tryKeyboardInteractive({ usePassword: false })) {\n return void await this.#openShell();\n }\n }\n }\n\n // A host that only advertises password auth still needs an answer when\n // the panel was opened without one.\n if (this.authMethods.includes('password')) {\n const typed = await this.requestInput({\n prompt: `${this.username}@${this.options.hostLabel || 'host'}'s password: `,\n echo: false,\n });\n if (typed && await this.#tryPassword(typed)) return void await this.#openShell();\n }\n\n throw new Error(this.authMethods.length\n ? `SSH: authentication failed. The host accepts ${this.authMethods.join(', ')}.`\n : 'SSH: the host rejected the connection before any authentication method was offered.');\n } catch (error) {\n if (!this.closed) this.transport.fail(error);\n }\n }\n\n async #tryNone() {\n this.transport.send(new SshWriter(64)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('none').take());\n return this.#authResult();\n }\n\n async #tryPassword(password: string) {\n this.transport.send(new SshWriter(128)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('password')\n .boolean(false).string(password)\n .take());\n return this.#authResult();\n }\n\n /**\n * keyboard-interactive is how most Linux hosts actually ask for a password,\n * and the only way a one-time code can be entered. Prompts are answered from\n * the password when one is obviously being asked for, and by the user\n * otherwise.\n */\n async #tryKeyboardInteractive({ usePassword = true } = {}) {\n this.transport.send(new SshWriter(96)\n .u8(MSG.USERAUTH_REQUEST).string(this.username).string(SERVICE).string('keyboard-interactive')\n .string('').string('')\n .take());\n\n let usedPassword = !usePassword;\n while (true) {\n const { type, payload } = await this.#expect(\n MSG.USERAUTH_SUCCESS, MSG.USERAUTH_FAILURE, MSG.USERAUTH_INFO_REQUEST\n );\n if (type === MSG.USERAUTH_SUCCESS) return true;\n if (type === MSG.USERAUTH_FAILURE) {\n this.#readFailure(payload);\n return false;\n }\n\n const reader = new SshReader(payload, 1);\n const name = reader.string();\n const instruction = reader.string();\n reader.string(); // language\n const promptCount = reader.u32();\n\n if (name) this.#emitBanner(`${name.replaceAll('\\n', '\\r\\n')}\\r\\n`);\n if (instruction) this.#emitBanner(`${instruction.replaceAll('\\n', '\\r\\n')}\\r\\n`);\n\n const answers = [];\n for (let index = 0; index < promptCount; index++) {\n const prompt = reader.string();\n const echo = reader.boolean();\n const looksLikePassword = !echo && /pass(word|phrase)/i.test(prompt);\n\n if (looksLikePassword && this.password && !usedPassword) {\n usedPassword = true;\n answers.push(this.password);\n continue;\n }\n answers.push(await this.requestInput({ prompt, echo }));\n }\n\n const response = new SshWriter(256).u8(MSG.USERAUTH_INFO_RESPONSE).u32(answers.length);\n for (const answer of answers) response.string(answer);\n this.transport.send(response.take());\n }\n }\n\n async #authResult() {\n const { type, payload } = await this.#expect(MSG.USERAUTH_SUCCESS, MSG.USERAUTH_FAILURE);\n if (type === MSG.USERAUTH_SUCCESS) return true;\n this.#readFailure(payload);\n return false;\n }\n\n #readFailure(payload: Uint8Array) {\n const reader = new SshReader(payload, 1);\n this.authMethods = reader.nameList();\n const partial = reader.boolean();\n if (partial) this.#emitBanner('\\r\\nOne factor accepted; the host wants another.\\r\\n');\n }\n\n // --- the shell -----------------------------------------------------------\n\n async #openShell() {\n this.#status('opening', 'Starting the remote shell\u2026');\n\n this.transport.send(new SshWriter(64)\n .u8(MSG.CHANNEL_OPEN).string('session').u32(this.channelId).u32(INITIAL_WINDOW).u32(MAX_PACKET)\n .take());\n\n const opened = await this.#expect(MSG.CHANNEL_OPEN_CONFIRMATION, MSG.CHANNEL_OPEN_FAILURE);\n if (opened.type === MSG.CHANNEL_OPEN_FAILURE) {\n const reader = new SshReader(opened.payload, 1);\n reader.u32();\n reader.u32();\n throw new Error(`SSH: the host refused to open a session channel (${reader.string()}).`);\n }\n\n const reader = new SshReader(opened.payload, 1);\n reader.u32(); // our channel id, echoed back\n this.remoteChannelId = reader.u32();\n this.remoteWindow = reader.u32();\n this.remoteMaxPacket = Math.max(1024, Math.min(reader.u32(), MAX_PACKET));\n\n this.transport.send(new SshWriter(128)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('pty-req').boolean(true)\n .string(TERMINAL_TYPE).u32(this.columns).u32(this.rows).u32(0).u32(0).string(PTY_MODES)\n .take());\n const pty = await this.#expect(MSG.CHANNEL_SUCCESS, MSG.CHANNEL_FAILURE);\n if (pty.type === MSG.CHANNEL_FAILURE) {\n throw new Error('SSH: the host refused a pseudo-terminal, so there is no interactive shell to show.');\n }\n\n this.transport.send(new SshWriter(32)\n .u8(MSG.CHANNEL_REQUEST).u32(this.remoteChannelId).string('shell').boolean(true).take());\n const shell = await this.#expect(MSG.CHANNEL_SUCCESS, MSG.CHANNEL_FAILURE);\n if (shell.type === MSG.CHANNEL_FAILURE) {\n throw new Error('SSH: the host refused to start a shell for this account.');\n }\n\n this.shellOpen = true;\n this.#flushWrites();\n if (this.options.preferredShell === 'powershell') this.#startPowerShell();\n else this.#markReady();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["reader"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Terminal } from "@xterm/xterm";
|
|
2
|
+
import { FitAddon } from "@xterm/addon-fit";
|
|
3
|
+
import xtermStyles from "@xterm/xterm/css/xterm.css";
|
|
4
|
+
const STYLE_ELEMENT_ID = "xterm-styles";
|
|
5
|
+
const MIN_COLUMNS = 80;
|
|
6
|
+
const BASE_FONT_SIZE = 13;
|
|
7
|
+
const MIN_FONT_SIZE = 7;
|
|
8
|
+
function installStyles() {
|
|
9
|
+
if (document.getElementById(STYLE_ELEMENT_ID)) return;
|
|
10
|
+
const style = document.createElement("style");
|
|
11
|
+
style.id = STYLE_ELEMENT_ID;
|
|
12
|
+
style.textContent = xtermStyles;
|
|
13
|
+
document.head.appendChild(style);
|
|
14
|
+
}
|
|
15
|
+
const THEME = {
|
|
16
|
+
background: "#0b0d12",
|
|
17
|
+
foreground: "#dfe4ec",
|
|
18
|
+
cursor: "#f38020",
|
|
19
|
+
cursorAccent: "#0b0d12",
|
|
20
|
+
selectionBackground: "rgba(243, 128, 32, 0.32)",
|
|
21
|
+
black: "#1b1f27",
|
|
22
|
+
red: "#ff6b6b",
|
|
23
|
+
green: "#4ade80",
|
|
24
|
+
yellow: "#fbbf24",
|
|
25
|
+
blue: "#60a5fa",
|
|
26
|
+
magenta: "#c084fc",
|
|
27
|
+
cyan: "#34d3ee",
|
|
28
|
+
white: "#cbd3e1",
|
|
29
|
+
brightBlack: "#63708a",
|
|
30
|
+
brightRed: "#ff8787",
|
|
31
|
+
brightGreen: "#86efac",
|
|
32
|
+
brightYellow: "#fcd34d",
|
|
33
|
+
brightBlue: "#93c5fd",
|
|
34
|
+
brightMagenta: "#d8b4fe",
|
|
35
|
+
brightCyan: "#67e8f9",
|
|
36
|
+
brightWhite: "#f5f7fa"
|
|
37
|
+
};
|
|
38
|
+
class SshTerminal {
|
|
39
|
+
constructor(container) {
|
|
40
|
+
installStyles();
|
|
41
|
+
this.container = container;
|
|
42
|
+
this.terminal = new Terminal({
|
|
43
|
+
allowProposedApi: true,
|
|
44
|
+
convertEol: false,
|
|
45
|
+
cursorBlink: true,
|
|
46
|
+
cursorStyle: "bar",
|
|
47
|
+
drawBoldTextInBrightColors: true,
|
|
48
|
+
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
|
|
49
|
+
fontSize: BASE_FONT_SIZE,
|
|
50
|
+
lineHeight: 1.25,
|
|
51
|
+
letterSpacing: 0,
|
|
52
|
+
macOptionIsMeta: true,
|
|
53
|
+
minimumContrastRatio: 1.5,
|
|
54
|
+
scrollback: 1e4,
|
|
55
|
+
theme: THEME
|
|
56
|
+
});
|
|
57
|
+
this.fitAddon = new FitAddon();
|
|
58
|
+
this.terminal.loadAddon(this.fitAddon);
|
|
59
|
+
this.terminal.open(container);
|
|
60
|
+
this.fit();
|
|
61
|
+
this.observer = new ResizeObserver(() => this.fit());
|
|
62
|
+
this.observer.observe(container);
|
|
63
|
+
this.dataHandler = null;
|
|
64
|
+
this.terminal.onData((data) => this.dataHandler?.(data));
|
|
65
|
+
}
|
|
66
|
+
get columns() {
|
|
67
|
+
return this.terminal.cols;
|
|
68
|
+
}
|
|
69
|
+
get rows() {
|
|
70
|
+
return this.terminal.rows;
|
|
71
|
+
}
|
|
72
|
+
onData(handler) {
|
|
73
|
+
this.dataHandler = handler;
|
|
74
|
+
}
|
|
75
|
+
onResize(handler) {
|
|
76
|
+
this.terminal.onResize(({ cols, rows }) => handler(cols, rows));
|
|
77
|
+
}
|
|
78
|
+
write(data) {
|
|
79
|
+
this.terminal.write(data);
|
|
80
|
+
}
|
|
81
|
+
/** Gateway-side notices, kept visually apart from what the host prints. */
|
|
82
|
+
notice(text, tone = "info") {
|
|
83
|
+
const color = { info: "\x1B[38;5;245m", good: "\x1B[38;5;114m", bad: "\x1B[38;5;203m" }[tone] || "";
|
|
84
|
+
this.terminal.write(`${color}${text}\x1B[0m\r
|
|
85
|
+
`);
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.terminal.clear();
|
|
89
|
+
}
|
|
90
|
+
focus() {
|
|
91
|
+
this.terminal.focus();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Size the terminal to the panel, choosing the font so MIN_COLUMNS fit.
|
|
95
|
+
*
|
|
96
|
+
* The columns matter more than the type size here: the commands people run in
|
|
97
|
+
* a side panel -- `ls -l`, `top`, anything in PowerShell that prints a table
|
|
98
|
+
* -- are laid out for 80 columns, and a narrower grid wraps every row into an
|
|
99
|
+
* unreadable stack.
|
|
100
|
+
*
|
|
101
|
+
* `proposeDimensions()` reports the grid the current font size yields, and a
|
|
102
|
+
* monospace cell's width scales with its font size, so `fontSize * cols /
|
|
103
|
+
* MIN_COLUMNS` lands close to the size that gives MIN_COLUMNS. It is measured
|
|
104
|
+
* rather than assumed because the cell-width-to-font-size ratio belongs to
|
|
105
|
+
* whichever font the platform picked. A second pass settles the rounding.
|
|
106
|
+
* Changing the font resizes the element, which the ResizeObserver sees, so
|
|
107
|
+
* the guard keeps that from recursing.
|
|
108
|
+
*/
|
|
109
|
+
fit() {
|
|
110
|
+
if (this.fitting) return;
|
|
111
|
+
if (this.container.clientWidth <= 0 || this.container.clientHeight <= 0) return;
|
|
112
|
+
this.fitting = true;
|
|
113
|
+
try {
|
|
114
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
115
|
+
const proposed = this.fitAddon.proposeDimensions();
|
|
116
|
+
if (!proposed || !(proposed.cols > 0)) break;
|
|
117
|
+
const current = this.terminal.options.fontSize || BASE_FONT_SIZE;
|
|
118
|
+
const wanted = Math.min(
|
|
119
|
+
BASE_FONT_SIZE,
|
|
120
|
+
Math.max(MIN_FONT_SIZE, Math.floor(current * proposed.cols / MIN_COLUMNS))
|
|
121
|
+
);
|
|
122
|
+
if (wanted === current) break;
|
|
123
|
+
this.terminal.options.fontSize = wanted;
|
|
124
|
+
}
|
|
125
|
+
this.fitAddon.fit();
|
|
126
|
+
const short = this.terminal.cols < MIN_COLUMNS;
|
|
127
|
+
this.container.classList.toggle("is-narrow", short);
|
|
128
|
+
if (short) this.terminal.resize(MIN_COLUMNS, this.terminal.rows);
|
|
129
|
+
} catch {
|
|
130
|
+
} finally {
|
|
131
|
+
this.fitting = false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** The grid the host is being told about, for a status line or a log. */
|
|
135
|
+
get geometry() {
|
|
136
|
+
return {
|
|
137
|
+
columns: this.terminal.cols,
|
|
138
|
+
rows: this.terminal.rows,
|
|
139
|
+
fontSize: this.terminal.options.fontSize
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
get selection() {
|
|
143
|
+
return this.terminal.getSelection();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Read one line from the user, for a password or a one-time code the host
|
|
147
|
+
* asked for during authentication. The shell is not open yet, so the
|
|
148
|
+
* terminal is free to be a prompt.
|
|
149
|
+
*/
|
|
150
|
+
readLine({ prompt = "", echo = false } = {}) {
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
const previous = this.dataHandler;
|
|
153
|
+
let answer = "";
|
|
154
|
+
this.terminal.write(prompt.replaceAll("\n", "\r\n"));
|
|
155
|
+
this.dataHandler = (data) => {
|
|
156
|
+
for (const character of data) {
|
|
157
|
+
const code = character.charCodeAt(0);
|
|
158
|
+
if (code === 13 || code === 10) {
|
|
159
|
+
this.terminal.write("\r\n");
|
|
160
|
+
this.dataHandler = previous;
|
|
161
|
+
resolve(answer);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (code === 127 || code === 8) {
|
|
165
|
+
if (answer.length === 0) continue;
|
|
166
|
+
answer = answer.slice(0, -1);
|
|
167
|
+
if (echo) this.terminal.write("\b \b");
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (code === 3) {
|
|
171
|
+
this.terminal.write("^C\r\n");
|
|
172
|
+
this.dataHandler = previous;
|
|
173
|
+
resolve("");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (code < 32) continue;
|
|
177
|
+
answer += character;
|
|
178
|
+
if (echo) this.terminal.write(character);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
destroy() {
|
|
184
|
+
this.observer?.disconnect();
|
|
185
|
+
this.terminal.dispose();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export {
|
|
189
|
+
SshTerminal
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/protocols/ssh/terminal.ts"],
|
|
4
|
+
"sourcesContent": ["// The terminal surface: xterm.js, themed to match the gateway, wired to an\n// SSH session's bytes in and keystrokes out.\n\nimport { Terminal } from '@xterm/xterm';\nimport { FitAddon } from '@xterm/addon-fit';\nimport xtermStyles from '@xterm/xterm/css/xterm.css';\n\nconst STYLE_ELEMENT_ID = 'xterm-styles';\n\n/**\n * The width almost every command's output is laid out for.\n *\n * A terminal narrower than this does not merely look cramped, it garbles: `ls\n * -l`, `Get-ChildItem`, `ps` and `top` all place columns by character position,\n * so the last column wraps one letter per line and the table stops being a\n * table. Fitting the font to the panel rather than the panel to the font is\n * what keeps that from happening in a docked panel or on a phone.\n */\nconst MIN_COLUMNS = 80;\n\n/** Comfortable on a wide panel, and the size a roomy one keeps. */\nconst BASE_FONT_SIZE = 13;\n\n/**\n * The floor. On a small mobile screen this yields 80 columns without wrapping\n * or cutting off table outputs.\n */\nconst MIN_FONT_SIZE = 7;\n\n/** xterm ships its own stylesheet; the bundle carries it instead of a CDN. */\nfunction installStyles() {\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ELEMENT_ID;\n style.textContent = xtermStyles;\n document.head.appendChild(style);\n}\n\n/** The gateway's palette, so the terminal belongs to the same application. */\nconst THEME = {\n background: '#0b0d12',\n foreground: '#dfe4ec',\n cursor: '#f38020',\n cursorAccent: '#0b0d12',\n selectionBackground: 'rgba(243, 128, 32, 0.32)',\n black: '#1b1f27',\n red: '#ff6b6b',\n green: '#4ade80',\n yellow: '#fbbf24',\n blue: '#60a5fa',\n magenta: '#c084fc',\n cyan: '#34d3ee',\n white: '#cbd3e1',\n brightBlack: '#63708a',\n brightRed: '#ff8787',\n brightGreen: '#86efac',\n brightYellow: '#fcd34d',\n brightBlue: '#93c5fd',\n brightMagenta: '#d8b4fe',\n brightCyan: '#67e8f9',\n brightWhite: '#f5f7fa',\n};\n\nexport class SshTerminal {\n constructor(container) {\n installStyles();\n\n this.container = container;\n this.terminal = new Terminal({\n allowProposedApi: true,\n convertEol: false,\n cursorBlink: true,\n cursorStyle: 'bar',\n drawBoldTextInBrightColors: true,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: BASE_FONT_SIZE,\n lineHeight: 1.25,\n letterSpacing: 0,\n macOptionIsMeta: true,\n minimumContrastRatio: 1.5,\n scrollback: 10_000,\n theme: THEME,\n });\n\n this.fitAddon = new FitAddon();\n this.terminal.loadAddon(this.fitAddon);\n this.terminal.open(container);\n this.fit();\n\n // The panel is resizable and the browser window is not the only thing that\n // changes its size, so follow the element rather than the window.\n this.observer = new ResizeObserver(() => this.fit());\n this.observer.observe(container);\n\n this.dataHandler = null;\n this.terminal.onData((data: Uint8Array) => this.dataHandler?.(data));\n }\n\n get columns() {\n return this.terminal.cols;\n }\n\n get rows() {\n return this.terminal.rows;\n }\n\n onData(handler) {\n this.dataHandler = handler;\n }\n\n onResize(handler) {\n this.terminal.onResize(({ cols, rows }) => handler(cols, rows));\n }\n\n write(data: Uint8Array) {\n this.terminal.write(data);\n }\n\n /** Gateway-side notices, kept visually apart from what the host prints. */\n notice(text: string, tone = 'info') {\n const color = { info: '\\x1b[38;5;245m', good: '\\x1b[38;5;114m', bad: '\\x1b[38;5;203m' }[tone] || '';\n this.terminal.write(`${color}${text}\\x1b[0m\\r\\n`);\n }\n\n clear() {\n this.terminal.clear();\n }\n\n focus() {\n this.terminal.focus();\n }\n\n /**\n * Size the terminal to the panel, choosing the font so MIN_COLUMNS fit.\n *\n * The columns matter more than the type size here: the commands people run in\n * a side panel -- `ls -l`, `top`, anything in PowerShell that prints a table\n * -- are laid out for 80 columns, and a narrower grid wraps every row into an\n * unreadable stack.\n *\n * `proposeDimensions()` reports the grid the current font size yields, and a\n * monospace cell's width scales with its font size, so `fontSize * cols /\n * MIN_COLUMNS` lands close to the size that gives MIN_COLUMNS. It is measured\n * rather than assumed because the cell-width-to-font-size ratio belongs to\n * whichever font the platform picked. A second pass settles the rounding.\n * Changing the font resizes the element, which the ResizeObserver sees, so\n * the guard keeps that from recursing.\n */\n fit() {\n if (this.fitting) return;\n if (this.container.clientWidth <= 0 || this.container.clientHeight <= 0) return;\n\n this.fitting = true;\n try {\n for (let pass = 0; pass < 2; pass++) {\n const proposed = this.fitAddon.proposeDimensions();\n if (!proposed || !(proposed.cols > 0)) break;\n\n const current = this.terminal.options.fontSize || BASE_FONT_SIZE;\n const wanted = Math.min(\n BASE_FONT_SIZE,\n Math.max(MIN_FONT_SIZE, Math.floor((current * proposed.cols) / MIN_COLUMNS))\n );\n if (wanted === current) break;\n this.terminal.options.fontSize = wanted;\n }\n\n this.fitAddon.fit();\n\n // A panel narrow enough to be short of MIN_COLUMNS even at the smallest\n // type keeps the columns and scrolls sideways instead, because wrapped\n // output is harder to read than output that has to be scrolled to.\n const short = this.terminal.cols < MIN_COLUMNS;\n this.container.classList.toggle('is-narrow', short);\n if (short) this.terminal.resize(MIN_COLUMNS, this.terminal.rows);\n } catch {\n // A hidden panel has no size to fit to; the next resize will catch it.\n } finally {\n this.fitting = false;\n }\n }\n\n /** The grid the host is being told about, for a status line or a log. */\n get geometry() {\n return {\n columns: this.terminal.cols,\n rows: this.terminal.rows,\n fontSize: this.terminal.options.fontSize,\n };\n }\n\n get selection() {\n return this.terminal.getSelection();\n }\n\n /**\n * Read one line from the user, for a password or a one-time code the host\n * asked for during authentication. The shell is not open yet, so the\n * terminal is free to be a prompt.\n */\n readLine({ prompt = '', echo = false } = {}) {\n return new Promise((resolve) => {\n const previous = this.dataHandler;\n let answer = '';\n\n this.terminal.write(prompt.replaceAll('\\n', '\\r\\n'));\n this.dataHandler = (data: Uint8Array) => {\n for (const character of data) {\n const code = character.charCodeAt(0);\n\n if (code === 0x0d || code === 0x0a) { // Enter, or a pasted line break\n this.terminal.write('\\r\\n');\n this.dataHandler = previous;\n resolve(answer);\n return;\n }\n if (code === 0x7f || code === 0x08) { // Backspace\n if (answer.length === 0) continue;\n answer = answer.slice(0, -1);\n if (echo) this.terminal.write('\\b \\b');\n continue;\n }\n if (code === 0x03) { // Ctrl-C\n this.terminal.write('^C\\r\\n');\n this.dataHandler = previous;\n resolve('');\n return;\n }\n if (code < 0x20) continue;\n\n answer += character;\n if (echo) this.terminal.write(character);\n }\n };\n });\n }\n\n destroy() {\n this.observer?.disconnect();\n this.terminal.dispose();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|