import * as plugins from './plugins.js'; import type { IWrappedKvmCommand, TKvmShellHint } from './smartkvm.interfaces.js'; export interface IParseWrappedKvmCommandOutputOptions { commandId: string; startMarker: string; endMarkerPrefix: string; rawText: string; } export interface IParseWrappedKvmCommandOutputResult { completed: boolean; exitCode?: number; combinedText: string; } export const createWrappedKvmCommand = ( command: string, shellHint: TKvmShellHint ): IWrappedKvmCommand => { const commandId = plugins.crypto.randomUUID().replace(/-/g, ''); const startMarker = `SMARTKVM_START_${commandId}`; const endMarkerPrefix = `SMARTKVM_END_${commandId}_`; let textToType: string; switch (shellHint) { case 'powershell': textToType = `$__smartkvm_id = "${commandId}"; Write-Output "${startMarker}"; $__smartkvm_status = 0; try { ${command}; if ($null -ne $LASTEXITCODE) { $__smartkvm_status = $LASTEXITCODE } } catch { Write-Output $_; $__smartkvm_status = 1 }; Write-Output "${endMarkerPrefix}$__smartkvm_status"`; break; case 'cmd': textToType = `echo ${startMarker} & ${command} & echo ${endMarkerPrefix}%ERRORLEVEL%`; break; case 'bash': case 'zsh': case 'sh': case 'unknown': default: textToType = `printf '\n${startMarker}\n'; ${command}; __smartkvm_status=$?; printf '\n${endMarkerPrefix}%s\n' "$__smartkvm_status"`; break; } return { commandId, shellHint, command, textToType, startMarker, endMarkerPrefix, }; }; export const parseWrappedKvmCommandOutput = ( options: IParseWrappedKvmCommandOutputOptions ): IParseWrappedKvmCommandOutputResult => { const normalizedRawText = options.rawText.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); const startIndex = normalizedRawText.indexOf(options.startMarker); if (startIndex === -1) { return { completed: false, combinedText: normalizedRawText, }; } const contentStartIndex = startIndex + options.startMarker.length; const endIndex = normalizedRawText.indexOf(options.endMarkerPrefix, contentStartIndex); if (endIndex === -1) { return { completed: false, combinedText: normalizedRawText, }; } const endMarkerValueStartIndex = endIndex + options.endMarkerPrefix.length; const endMarkerLine = normalizedRawText.slice(endMarkerValueStartIndex).split('\n')[0].trim(); const exitCodeMatch = endMarkerLine.match(/^-?\d+/); const exitCode = exitCodeMatch ? Number.parseInt(exitCodeMatch[0], 10) : undefined; return { completed: true, exitCode, combinedText: normalizedRawText.slice(contentStartIndex, endIndex).trim(), }; };