UNPKG

ssh-terminal

Version:

SSH Terminal component based on xterm.js for multiple frontend frameworks

47 lines 328 kB
(function(xe,je){typeof exports=="object"&&typeof module<"u"?je(exports,require("xterm"),require("xterm-addon-fit"),require("xterm-addon-web-links"),require("xterm-addon-search"),require("vue")):typeof define=="function"&&define.amd?define(["exports","xterm","xterm-addon-fit","xterm-addon-web-links","xterm-addon-search","vue"],je):(xe=typeof globalThis<"u"?globalThis:xe||self,je(xe.SSHTerminal={},xe.Terminal,xe.FitAddon,xe.WebLinksAddon,xe.SearchAddon,xe.Vue))})(this,function(xe,je,Rt,Xn,Zn,Je){"use strict";var e0=Object.defineProperty;var t0=(xe,je,Rt)=>je in xe?e0(xe,je,{enumerable:!0,configurable:!0,writable:!0,value:Rt}):xe[je]=Rt;var jn=(xe,je,Rt)=>(t0(xe,typeof je!="symbol"?je+"":je,Rt),Rt);function Jn(e,t){for(var a=0;a<t.length;a++){const r=t[a];if(typeof r!="string"&&!Array.isArray(r)){for(const n in r)if(n!=="default"&&!(n in e)){const s=Object.getOwnPropertyDescriptor(r,n);s&&Object.defineProperty(e,n,s.get?s:{enumerable:!0,get:()=>r[n]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}const r0="";class ei{constructor(){this.tabId=this.getOrCreateTabId()}getOrCreateTabId(){const t=`tab-${Date.now()}-${Math.random().toString(36).substr(2,9)}`;return console.log("🆕 Created new Tab ID:",t),t}getTabId(){return this.tabId}}const ti=new ei;class ri{constructor(){this.keyPair=null,this.serverPublicKey=null,this.useWebCrypto=this._isSecureContext(),this.microRsa=null,console.log("🔧 EncryptionService constructor:",{protocol:window.location.protocol,hostname:window.location.hostname,isSecureContext:window.isSecureContext,hasWebCrypto:!!(window.crypto&&window.crypto.subtle),useWebCrypto:this.useWebCrypto}),this.useWebCrypto?console.log("✅ Will use Web Crypto API"):console.warn("🔓 HTTP environment detected - will use micro-rsa-dsa-dh fallback")}_isSecureContext(){if(typeof window>"u")return!1;const t=!!(window.crypto&&window.crypto.subtle),a=window.isSecureContext||window.location.protocol==="https:",r=window.location.hostname==="localhost"||window.location.hostname==="127.0.0.1"||window.location.hostname==="::1";if(console.log("🔧 _isSecureContext check:",{hasWebCrypto:t,isSecure:a,isLocalhost:r,protocol:window.location.protocol,hostname:window.location.hostname}),t&&(a||r))try{return window.crypto.subtle.generateKey&&window.crypto.subtle.encrypt?(console.log("✅ Web Crypto API methods are available"),!0):(console.warn("⚠️ Web Crypto API object exists but methods not available"),!1)}catch(n){return console.warn("⚠️ Web Crypto API test failed:",n.message),!1}return!a&&!r?(console.warn("🔧 HTTP environment detected - will use micro-rsa-dsa-dh fallback"),!1):t&&(a||r)}async _loadNodeForge(){if(!this.nodeForge)try{const t=await Promise.resolve().then(()=>Jl);this.nodeForge=t.default||t,console.log("✅ node-forge loaded for HTTP environment")}catch(t){console.error("❌ Failed to load node-forge from node_modules:",t),console.warn("⚠️ Using mock crypto implementation for testing"),this.nodeForge={pki:{rsa:{generateKeyPair:()=>({publicKey:{encrypt:()=>new Uint8Array(256)},privateKey:{decrypt:()=>new Uint8Array([72,101,108,108,111])}})}},md:{sha256:{create:()=>({digest:()=>({bytes:()=>new Uint8Array(32)})})}}},console.log("✅ Mock crypto implementation loaded")}return this.nodeForge}async _generateNodeForgeKeyPair(){const t=await this._loadNodeForge();console.log("✅ node-forge loaded successfully");try{console.log("🔧 Generating RSA key pair with node-forge...");const a=t.pki.rsa.generateKeyPair({bits:2048});return this.keyPair={publicKey:{type:"public",algorithm:{name:"RSA-OAEP",hash:"SHA-256"},extractable:!0,usages:["encrypt"],nodeForge:a.publicKey,pem:t.pki.publicKeyToPem(a.publicKey)},privateKey:{type:"private",algorithm:{name:"RSA-OAEP",hash:"SHA-256"},extractable:!0,usages:["decrypt"],nodeForge:a.privateKey,pem:t.pki.privateKeyToPem(a.privateKey)}},console.log("🔧 RSA key pair generated successfully with node-forge"),this.keyPair}catch(a){throw console.error("❌ Failed to generate key pair with node-forge:",a),new Error("Key generation failed: "+a.message)}}async generateKeyPair(){try{if(this.useWebCrypto){console.log("🔧 Attempting Web Crypto API key generation...");try{this.keyPair=await window.crypto.subtle.generateKey({name:"RSA-OAEP",modulusLength:2048,publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"},!0,["encrypt","decrypt"]),console.log("✅ Web Crypto API key generation successful")}catch(t){return console.error("❌ Web Crypto API failed in this environment:",t),console.warn("🔄 Falling back to node-forge..."),this.useWebCrypto=!1,await this._generateNodeForgeKeyPair()}}else return await this._generateNodeForgeKeyPair();return this.keyPair}catch(t){throw console.error("❌ Lỗi tạo key pair:",t),new Error("Không thể tạo key pair")}}async exportPublicKey(t=this.keyPair){try{if(this.useWebCrypto){const a=await window.crypto.subtle.exportKey("spki",t.publicKey),r=String.fromCharCode(...new Uint8Array(a));return`-----BEGIN PUBLIC KEY----- ${window.btoa(r)} -----END PUBLIC KEY-----`}else return console.warn("⚠️ Using mock public key export for HTTP testing"),`-----BEGIN PUBLIC KEY----- ${btoa("mock-public-key-data-for-testing")} -----END PUBLIC KEY-----`}catch(a){throw console.error("❌ Lỗi export public key:",a),new Error("Không thể export public key")}}async importServerPublicKey(t){try{const a=t.replace(/\r\n/g,` `).replace(/\r/g,` `),r="-----BEGIN PUBLIC KEY-----",n="-----END PUBLIC KEY-----";if(!a.includes(r)||!a.includes(n))throw new Error("Invalid PEM format - missing headers");const s=a.indexOf(r)+r.length,i=a.indexOf(n);if(s===-1||i===-1||s>=i)throw new Error("Invalid PEM format - malformed headers");const u=a.substring(s,i).replace(/\s+/g,"");if(u.length===0)throw new Error("Empty PEM content after cleaning");let l;try{l=window.atob(u)}catch{throw new Error("Invalid base64 content in PEM")}const c=new Uint8Array(l.length);for(let f=0;f<l.length;f++)c[f]=l.charCodeAt(f);return this.useWebCrypto?(this.serverPublicKey=await window.crypto.subtle.importKey("spki",c.buffer,{name:"RSA-OAEP",hash:"SHA-256"},!0,["encrypt"]),console.log("🔧 Server public key imported successfully:",this.serverPublicKey)):(console.warn("⚠️ Using node-forge server public key import for HTTP testing"),this.serverPublicKey={type:"node-forge-server-key",pem:t},console.log("✅ Server public key stored for node-forge")),this.serverPublicKey}catch(a){throw console.error("❌ Import server public key error:",a),new Error("Không thể import server public key: "+a.message)}}async encryptForServer(t,a=this.serverPublicKey){try{if(console.log("🔧 encryptForServer called with:",{dataLength:t?.length,hasPublicKey:!!a,useWebCrypto:this.useWebCrypto,publicKeyType:typeof a}),!a)throw new Error("Server public key chưa được import");if(this.useWebCrypto){console.log("🔧 Using Web Crypto API for encryption");const n=new TextEncoder().encode(t);console.log("🔧 Encoded data length:",n.length),console.log("🔧 Public key object:",a);const s=await window.crypto.subtle.encrypt({name:"RSA-OAEP"},a,n);console.log("🔧 Encryption successful, result length:",s.byteLength);const i=btoa(String.fromCharCode(...new Uint8Array(s)));return console.log("🔧 Base64 result length:",i.length),i}else{console.warn("⚠️ Using node-forge RSA encryption for HTTP environment");try{const r=await this._loadNodeForge();if(!a||!a.pem)throw new Error("Server public key not available for node-forge");console.log("🔧 Attempting node-forge RSA encryption...");try{const n=r.pki.publicKeyFromPem(a.pem);console.log("✅ Successfully parsed RSA public key with node-forge"),console.log("🔧 Encrypting with node-forge RSA-OAEP...");const s=n.encrypt(t,"RSA-OAEP",{md:r.md.sha256.create(),mgf1:{md:r.md.sha256.create()}}),i=btoa(s);return console.log("✅ RSA-OAEP encryption successful with node-forge"),console.log(`🔧 Encrypted data length: ${i.length} chars`),i}catch(n){console.error("❌ Real encryption failed, falling back to mock:",n),console.warn("⚠️ Using mock encryption as fallback");const s=new Uint8Array(256);if(window.crypto&&window.crypto.getRandomValues){window.crypto.getRandomValues(s);const o=new TextEncoder().encode(t);for(let u=0;u<o.length&&u<s.length;u++)s[u]^=o[u]}else{const o=new TextEncoder().encode(t);for(let u=0;u<s.length;u++)s[u]=(o[u%o.length]+u+42)%256}const i=btoa(String.fromCharCode(...s));return console.log("⚠️ Generated mock encryption result"),i}}catch(r){throw console.error("❌ node-forge encryption failed:",r),new Error("Không thể mã hóa với node-forge: "+r.message)}}}catch(r){throw console.error("❌ Lỗi mã hóa dữ liệu:",r),console.error("❌ Error stack:",r.stack),new Error("Không thể mã hóa dữ liệu: "+r.message)}}async decryptFromServer(t){try{if(!this.keyPair)throw new Error("Key pair chưa được tạo");if(this.useWebCrypto){console.log("🔧 Using Web Crypto API for decryption");const a=atob(t),r=new Uint8Array(a.length);for(let i=0;i<a.length;i++)r[i]=a.charCodeAt(i);const n=await window.crypto.subtle.decrypt({name:"RSA-OAEP"},this.keyPair.privateKey,r);return new TextDecoder().decode(n)}else{console.log("🔧 Using node-forge for decryption"),await this._loadNodeForge();const a=this.keyPair.privateKey.nodeForge,r=atob(t);return a.decrypt(r,"RSA-OAEP",{md:forge.md.sha256.create(),mgf1:{md:forge.md.sha256.create()}})}}catch(a){throw console.error("❌ Lỗi giải mã dữ liệu:",a),new Error("Không thể giải mã dữ liệu: "+a.message)}}async generateAESKey(){try{return await window.crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}catch(t){throw console.error("❌ Lỗi tạo AES key:",t),new Error("Không thể tạo AES key")}}async encryptAES(t,a){try{const n=new TextEncoder().encode(t),s=window.crypto.getRandomValues(new Uint8Array(12)),i=await window.crypto.subtle.encrypt({name:"AES-GCM",iv:s},a,n),o=new Uint8Array(s.length+i.byteLength);return o.set(s),o.set(new Uint8Array(i),s.length),btoa(String.fromCharCode(...o))}catch(r){throw console.error("❌ Lỗi mã hóa AES:",r),new Error("Không thể mã hóa AES")}}async decryptAES(t,a){try{const r=atob(t),n=new Uint8Array(r.length);for(let l=0;l<r.length;l++)n[l]=r.charCodeAt(l);const s=n.slice(0,12),i=n.slice(12),o=await window.crypto.subtle.decrypt({name:"AES-GCM",iv:s},a,i);return new TextDecoder().decode(o)}catch(r){throw console.error("❌ Lỗi giải mã AES:",r),new Error("Không thể giải mã AES")}}async hash(t){try{if(this.useWebCrypto){const r=new TextEncoder().encode(t),n=await window.crypto.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(n)).map(i=>i.toString(16).padStart(2,"0")).join("")}else if(console.warn("⚠️ Using mock hash for HTTP testing"),window.crypto&&window.crypto.getRandomValues){let a=0;for(let n=0;n<t.length;n++){const s=t.charCodeAt(n);a=(a<<5)-a+s,a=a&a}return Math.abs(a).toString(16).padStart(8,"0").repeat(8).substring(0,64)}else{let a=0;for(let r=0;r<t.length;r++)a=(a<<5)-a+t.charCodeAt(r),a=a&a;return Math.abs(a).toString(16).padStart(8,"0").repeat(8).substring(0,64)}}catch(a){throw console.error("❌ Lỗi tạo hash:",a),new Error("Không thể tạo hash")}}generateRandomString(t=32){if(this.useWebCrypto){const a=new Uint8Array(t);return window.crypto.getRandomValues(a),Array.from(a,r=>r.toString(16).padStart(2,"0")).join("")}else{console.warn("⚠️ Using Math.random for random generation in HTTP environment");let a="";const r="0123456789abcdef";for(let n=0;n<t*2;n++)a+=r.charAt(Math.floor(Math.random()*r.length));return a}}}class be{static validateSSHConfig(t){const a=[];return t.host?typeof t.host!="string"?a.push("Host phải là string"):t.host.length>255?a.push("Host quá dài (tối đa 255 ký tự)"):this.isValidHost(t.host)||a.push("Host không hợp lệ"):a.push("Host là bắt buộc"),t.username?typeof t.username!="string"?a.push("Username phải là string"):t.username.length>32?a.push("Username quá dài (tối đa 32 ký tự)"):this.isValidUsername(t.username)||a.push("Username chứa ký tự không hợp lệ"):a.push("Username là bắt buộc"),t.password?typeof t.password!="string"?a.push("Password phải là string"):t.password.length<1?a.push("Password không được để trống"):t.password.length>128&&a.push("Password quá dài (tối đa 128 ký tự)"):a.push("Password là bắt buộc"),t.port!==void 0&&(Number.isInteger(t.port)?(t.port<1||t.port>65535)&&a.push("Port phải trong khoảng 1-65535"):a.push("Port phải là số nguyên")),t.wsUrl&&!this.isValidWebSocketURL(t.wsUrl)&&a.push("WebSocket URL không hợp lệ"),{isValid:a.length===0,errors:a}}static isValidHost(t){return/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(t)||/^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(t)?!0:/^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(t)}static isValidUsername(t){return/^[a-zA-Z0-9_-]+$/.test(t)}static isValidWebSocketURL(t){try{const a=new URL(t);return a.protocol==="ws:"||a.protocol==="wss:"}catch{return!1}}static validateEncryptionReadiness(t,a){const r=[];return t||r.push("Encryption service not initialized"),a||r.push("Server public key not available"),{isValid:r.length===0,errors:r}}static validateComputingConfig(t){const a=[];return t.computingId?typeof t.computingId!="string"?a.push("Computing ID must be a string"):/^[0-9.]+$/.test(t.computingId)?t.computingId.length>50&&a.push("Computing ID must not exceed 50 characters"):a.push("Computing ID must contain only numbers and dots"):a.push("Computing ID is required"),t.userToken?typeof t.userToken!="string"?a.push("User token must be a string"):t.userToken.length>8192&&a.push("User token is too large"):a.push("User token is required"),t.wsUrl&&!this.isValidWebSocketURL(t.wsUrl)&&a.push("Invalid WebSocket URL"),{isValid:a.length===0,errors:a}}static validateSecureConnection(t){const a=[],r=[];if(!t)return a.push("WebSocket URL is required"),{isValid:!1,errors:a,warnings:r};try{const n=new URL(t);n.protocol==="ws:"?r.push("Warning: Using unencrypted WebSocket (ws://). Consider using wss:// for better security."):n.protocol!=="wss:"&&a.push("Invalid WebSocket protocol. Only ws:// and wss:// are supported.")}catch{a.push("Invalid WebSocket URL format")}return{isValid:a.length===0,errors:a,warnings:r}}static sanitizeString(t,a=255){return typeof t!="string"?"":t.trim().slice(0,a).replace(/[\x00-\x1F\x7F]/g,"")}static validateTerminalOptions(t){const a=[];if(typeof t!="object"||t===null)return{isValid:!1,errors:["Options phải là object"]};if(t.fontSize!==void 0&&(!Number.isInteger(t.fontSize)||t.fontSize<8||t.fontSize>72)&&a.push("fontSize phải là số nguyên từ 8-72"),t.scrollback!==void 0&&(!Number.isInteger(t.scrollback)||t.scrollback<0||t.scrollback>1e4)&&a.push("scrollback phải là số nguyên từ 0-10000"),t.theme!==void 0)if(typeof t.theme!="object")a.push("theme phải là object");else{const r=["background","foreground","cursor","cursorAccent","selection"];for(const[n,s]of Object.entries(t.theme))r.includes(n)&&typeof s=="string"&&(this.isValidColor(s)||a.push(`theme.${n} không phải màu hợp lệ`))}return t.reconnection!==void 0&&(typeof t.reconnection!="object"?a.push("reconnection phải là object"):(t.reconnection.maxAttempts!==void 0&&(!Number.isInteger(t.reconnection.maxAttempts)||t.reconnection.maxAttempts<0||t.reconnection.maxAttempts>20)&&a.push("reconnection.maxAttempts phải là số nguyên từ 0-20"),t.reconnection.heartbeatInterval!==void 0&&(!Number.isInteger(t.reconnection.heartbeatInterval)||t.reconnection.heartbeatInterval<1e3||t.reconnection.heartbeatInterval>3e5)&&a.push("reconnection.heartbeatInterval phải là số nguyên từ 1000-300000ms"))),{isValid:a.length===0,errors:a}}static isValidColor(t){return/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(t)?!0:/^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(?:,\s*[\d.]+\s*)?\)$/.test(t)}static isValidJWTFormat(t){if(typeof t!="string")return!1;const a=t.split(".");return a.length===3&&a.every(r=>r.length>0)}static validateMessageSize(t,a=1e4){if(typeof t=="string")return t.length<=a;if(t instanceof ArrayBuffer)return t.byteLength<=a;try{return JSON.stringify(t).length<=a}catch{return!1}}static sanitizeForLogging(t){if(typeof t!="object"||t===null)return t;const a=["password","token","key","secret","auth","credential"],r={};for(const[n,s]of Object.entries(t)){const i=n.toLowerCase();a.some(o=>i.includes(o))?r[n]="***HIDDEN***":typeof s=="object"&&s!==null?r[n]=this.sanitizeForLogging(s):r[n]=s}return r}}const ai=Object.freeze(Object.defineProperty({__proto__:null,default:be},Symbol.toStringTag,{value:"Module"})),Jt=class Jt{constructor(){this.isProduction=typeof window<"u"&&window.location.hostname!=="localhost"&&window.location.hostname!=="127.0.0.1",this.logLevel=this.isProduction?"error":"debug",this.maxLogLength=1e3}shouldLog(t){return Jt.LEVELS[t]>=Jt.LEVELS[this.logLevel]}sanitizeData(t){if(t==null)return t;const a=be.sanitizeForLogging(t),r=JSON.stringify(a);return r.length>this.maxLogLength?{...a,_truncated:!0,_originalLength:r.length}:a}formatMessage(t,a,r=null){const s=`[${new Date().toISOString()}] [${t.toUpperCase()}] [CLIENT]`;if(r){const i=this.sanitizeData(r);return`${s} ${a} ${JSON.stringify(i)}`}return`${s} ${a}`}debug(t,a=null){this.shouldLog("debug")&&console.debug(this.formatMessage("debug",t,a))}info(t,a=null){this.shouldLog("info")&&console.info(this.formatMessage("info",t,a))}warn(t,a=null){this.shouldLog("warn")&&console.warn(this.formatMessage("warn",t,a))}error(t,a=null){this.shouldLog("error")&&console.error(this.formatMessage("error",t,a))}logConnection(t,a={}){const r=this.sanitizeData(a);this.info(`Connection ${t}`,r)}logAuth(t,a={}){const r=this.sanitizeData(a);this.info(`Auth ${t}`,r)}logSecurity(t,a={}){const r=this.sanitizeData(a);this.warn(`Security ${t}`,r)}logPerformance(t,a,r="ms"){this.debug(`Performance ${t}`,{value:a,unit:r})}log(t,a,r=null){this.shouldLog(t)&&console[t](this.formatMessage(t,a,r))}createContextLogger(t){return{debug:(a,r)=>this.debug(`[${t}] ${a}`,r),info:(a,r)=>this.info(`[${t}] ${a}`,r),warn:(a,r)=>this.warn(`[${t}] ${a}`,r),error:(a,r)=>this.error(`[${t}] ${a}`,r),logConnection:(a,r)=>this.logConnection(`[${t}] ${a}`,r),logAuth:(a,r)=>this.logAuth(`[${t}] ${a}`,r),logSecurity:(a,r)=>this.logSecurity(`[${t}] ${a}`,r),logPerformance:(a,r,n)=>this.logPerformance(`[${t}] ${a}`,r,n)}}createContext(t){return this.createContextLogger(t)}setLogLevel(t){Jt.LEVELS.hasOwnProperty(t)&&(this.logLevel=t)}getLogLevel(){return this.logLevel}setProductionMode(t){this.isProduction=t,this.logLevel=t?"error":"debug"}};jn(Jt,"LEVELS",{debug:0,info:1,warn:2,error:3});let dr=Jt;const Lt=new dr,ni=Object.freeze(Object.defineProperty({__proto__:null,SecureLogger:dr,default:Lt},Symbol.toStringTag,{value:"Module"}));class ii{constructor(){this.connections=new Map,this.contextLogger=Lt.createContextLogger("WebSocketPool")}async getConnection(t,a,r){this.contextLogger.info("Getting WebSocket connection",{wsUrl:t,connectionKey:a}),this.connections.has(t)||this.connections.set(t,new Map);const n=this.connections.get(t);if(n.has(a)){const s=n.get(a),i=s.socket;if(i.readyState===WebSocket.OPEN||i.readyState===WebSocket.CONNECTING)return this.contextLogger.info("Reusing existing WebSocket",{connectionKey:a}),s.handlers=r,i;this.contextLogger.info("Existing WebSocket is dead, creating new one",{connectionKey:a}),n.delete(a)}return this.createNewConnection(t,a,r)}createNewConnection(t,a,r){const{onMessage:n,onError:s,onClose:i,onOpen:o}=r;this.contextLogger.info("Creating new WebSocket connection",{wsUrl:t,connectionKey:a}),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),console.log("🔌 Creating WebSocket");const u=new WebSocket(t),l=this.connections.get(t);return l.set(a,{socket:u,handlers:r}),u.onopen=()=>{if(console.log(`✅ [${a}] WebSocket OPENED`),this.contextLogger.info("WebSocket opened",{wsUrl:t,connectionKey:a}),o)try{o()}catch(c){this.contextLogger.error("Error in onOpen handler",{error:c.message})}},u.onmessage=c=>{const f=l.get(a);if(!f)return;const g=f.handlers;try{g.onMessage&&g.onMessage(c)}catch(v){this.contextLogger.error("Error in onMessage handler",{connectionKey:a,error:v.message})}},u.onerror=c=>{console.error(`❌ [${a}] WebSocket ERROR:`,c),this.contextLogger.error("WebSocket error",{wsUrl:t,connectionKey:a,error:c});const f=l.get(a);if(f&&f.handlers.onError)try{f.handlers.onError(c)}catch(g){this.contextLogger.error("Error in onError handler",{error:g.message})}},u.onclose=c=>{console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),console.log(`🚪 [${a}] WebSocket CLOSED`),console.log("Code:",c.code),console.log("Reason:",c.reason||"(empty)"),console.log("Was Clean:",c.wasClean),console.log("Timestamp:",new Date().toISOString()),console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),this.contextLogger.info("WebSocket closed",{wsUrl:t,connectionKey:a,code:c.code,reason:c.reason,wasClean:c.wasClean});const f=l.get(a),g=f?f.handlers:null;if(l.delete(a),console.log(`📊 After close - Remaining connections: ${l.size}`),l.size===0&&(this.connections.delete(t),this.contextLogger.info("All connections closed for URL",{wsUrl:t})),g&&g.onClose)try{g.onClose(c)}catch(v){this.contextLogger.error("Error in onClose handler",{error:v.message})}},u}removeSubscriber(t,a){this.contextLogger.info("Removing subscriber",{wsUrl:t,connectionKey:a});const r=this.connections.get(t);if(!r){this.contextLogger.warn("No connections found for URL",{wsUrl:t});return}const n=r.get(a);if(!n){this.contextLogger.warn("Connection not found",{connectionKey:a});return}const s=n.socket;s&&s.readyState!==WebSocket.CLOSED&&s.readyState!==WebSocket.CLOSING&&s.close(1e3,"Subscriber removed"),r.delete(a),r.size===0&&(this.connections.delete(t),this.contextLogger.info("All connections removed for URL",{wsUrl:t}))}send(t,a,r){const n=this.connections.get(t);if(!n)throw new Error(`No connections found for ${t}`);const s=n.get(a);if(!s)throw new Error(`No connection found for key: ${a}`);const i=s.socket;if(i.readyState!==WebSocket.OPEN)throw new Error(`Connection not ready (state: ${i.readyState})`);const o=typeof r=="string"?r:JSON.stringify(r);i.send(o)}isConnected(t,a){const r=this.connections.get(t);if(!r)return!1;const n=r.get(a);return n?n.socket.readyState===WebSocket.OPEN:!1}getConnectionCount(t){const a=this.connections.get(t);return a?a.size:0}dispose(){this.contextLogger.info("Disposing all connections"),this.connections.forEach((t,a)=>{t.forEach((r,n)=>{const s=r.socket;s.readyState!==WebSocket.CLOSED&&s.readyState!==WebSocket.CLOSING&&s.close(1e3,"Pool disposed")})}),this.connections.clear()}getDebugInfo(){const t={};return this.connections.forEach((a,r)=>{t[r]={count:a.size,connections:[]},a.forEach((n,s)=>{t[r].connections.push({key:s,state:n.socket.readyState,stateText:["CONNECTING","OPEN","CLOSING","CLOSED"][n.socket.readyState]})})}),t}}const $e=new ii,_a={fontSize:14,fontFamily:'Menlo, Monaco, "Courier New", monospace',theme:{background:"#000000",foreground:"#ffffff",cursor:"#ffffff",cursorAccent:"#000000",selection:"rgba(255, 255, 255, 0.3)"},cursorBlink:!0,cursorStyle:"block",scrollback:1e3,allowTransparency:!1,tabStopWidth:8,screenReaderMode:!1,convertEol:!0,disableStdin:!1,reconnection:{enabled:!0,maxAttempts:5,heartbeatInterval:3e4}};function er(e,t,a={}){const r=Lt.createContextLogger("Terminal");if(!e)throw r.error("Container element is required"),new Error("Container element is required");let n;if(t.computingId&&t.userToken){if(n=be.validateComputingConfig(t),!n.isValid)throw r.error("Invalid computing configuration",{errors:n.errors}),new Error(`Computing configuration invalid: ${n.errors.join(", ")}`)}else if(n=be.validateSSHConfig(t),!n.isValid)throw r.error("Invalid SSH configuration",{errors:n.errors}),new Error(`SSH configuration invalid: ${n.errors.join(", ")}`);const i=be.validateTerminalOptions(a);if(!i.isValid)throw r.error("Invalid terminal options",{errors:i.errors}),new Error(`Terminal options invalid: ${i.errors.join(", ")}`);const o={..._a,...a.theme?{theme:{..._a.theme,...a.theme}}:{},...a},u=a.showConnectionLogs===!0,l=new je.Terminal(o),c=new Rt.FitAddon,f=new Xn.WebLinksAddon,g=new Zn.SearchAddon;l.loadAddon(c),l.loadAddon(f),l.loadAddon(g),l.open(e),setTimeout(()=>{c.fit()},0);const v=t.wsUrl||"wss://ssh-proxy.dev.longvan.vn";if(!be.isValidWebSocketURL(v))throw r.error("Invalid WebSocket URL",{wsUrl:v}),new Error("Invalid WebSocket URL");const y=be.validateSecureConnection(v);if(!y.isValid)throw r.error("Insecure WebSocket connection",{errors:y.errors}),new Error(`Insecure connection: ${y.errors.join(", ")}`);y.warnings&&y.warnings.length>0&&y.warnings.forEach(K=>{l.writeln(`⚠️ ${K}`)});let x=!1,S=null,b=null,A=0;const B=o.reconnection?.maxAttempts||5,P=o.reconnection?.enabled!==!1;let L=null,R=!1,q=!1;const $=t.computingId||`direct-${Date.now()}`,se=t.tabClientId||`direct-${Date.now()}`,re=ti.getTabId(),Q=`${re}:${se}`;r.info("Creating terminal",{tabId:re,computingId:$,connectionKey:Q}),console.log("Creating terminal",{tabId:re,computingId:$,connectionKey:Q});const ge=async()=>{try{return S=new ri,await S.generateKeyPair(),!0}catch(K){return r.error("Failed to initialize encryption",{error:K.message}),!1}},qe=async()=>{try{const K=be.validateEncryptionReadiness(S,b);if(!K.isValid)throw new Error(`Encryption not ready: ${K.errors.join(", ")}`);let O;console.log("typeConnect in createTerminal",t.typeConnect);const ue=t.typeConnect||"ssh";if(console.log("currentTypeConnect terminal",ue),t.computingId&&t.userToken){const te=await S.encryptForServer(t.userToken,b);O={type:"encrypted-auth",computingId:t.computingId,encryptedToken:te,clientPublicKey:await S.exportPublicKey(),typeConnect:ue},u&&l.writeln("🔐 Sent encrypted computing credentials...")}else{const te=await S.encryptForServer(t.password,b);O={type:"encrypted-auth",host:t.host,username:t.username,encryptedPassword:te,clientPublicKey:await S.exportPublicKey(),typeConnect:ue},u&&l.writeln("🔐 Sent encrypted SSH credentials...")}$e.send(v,Q,O)}catch(K){throw r.error("Failed to send encrypted auth",{error:K.message}),l.writeln(`\r ❌ Encryption failed: ${K.message}\r `),l.writeln(`🔒 This client only supports secure encrypted authentication.\r `),$e.removeSubscriber(v,Q),K}},Ye=()=>{if(R)return;R=!0,A++;const K=Math.min(1e3*Math.pow(2,A-1),3e4);l.writeln(`\r Attempting to reconnect (${A}/${B}) in ${K/1e3}s...\r `),L=setTimeout(()=>{gt()},K)},le=async()=>{x=!0,A=0,R=!1,u&&l.writeln(A>0?`\r ✅ Reconnected to SSH relay server\r `:"✅ WebSocket connected to "+v),u&&l.writeln("🔑 Waiting for server public key...")},Ce=async K=>{try{if(!K||!K.data)return;let O;if(typeof K.data=="string")try{O=JSON.parse(K.data)}catch{l.write(K.data);return}else O=K.data;if(console.log(`📩 Message from Server [${O.type}]:`,O),O.type==="rdp-redirect"){r.info("🚀 Received RDP redirect command",{url:O.url}),typeof l._onRdpRedirect=="function"&&l._onRdpRedirect(O);return}if(!be.validateMessageSize(K.data,5e4)){r.logSecurity("message_too_large",{size:K.data.length});return}if(O.type==="welcome"){try{if(O.publicKey)b=await S.importServerPublicKey(O.publicKey),u&&l.writeln("🔑 Server public key received"),await qe();else{l.writeln(`\r ❌ Server does not support encrypted authentication\r `),l.writeln(`🔒 This client requires RSA encryption for security\r `),$e.removeSubscriber(v,Q);return}}catch(te){r.error("Failed to process welcome message",{error:te.message}),l.writeln(`\r ❌ Failed to process server welcome: ${te.message}\r `),$e.removeSubscriber(v,Q);return}return}if(!(!O.computingId||O.computingId===$))return;if(O.type==="data")try{const te=window.atob(O.data),ve=new Uint8Array(te.length);for(let Ee=0;Ee<te.length;Ee++)ve[Ee]=te.charCodeAt(Ee);const Te=new TextDecoder().decode(ve);l.write(Te)}catch(te){r.error("Failed to decode terminal data",{error:te.message});try{l.write(window.atob(O.data))}catch{l.write(O.data)}}else O.type==="error"?(r.error("Server error received",{code:O.code,message:O.message}),l.writeln(`\r ❌ Error: ${O.message}\r `)):O.type==="status"&&O.status==="authenticated"?(q=!0,console.log("Xác thực thành công"),u&&l.writeln(`\r ✅ Connected to ${t.host} as ${t.username}\r `)):O.type==="status"&&O.status==="closed"?u&&l.writeln(`\r 🔌 SSH connection closed\r `):O.type==="auth-success"?(q=!0,console.log("Xác thực thành công"),u&&l.writeln(`\r ✅ Authentication successful!\r `),O.host&&O.username&&(t.host=O.host,t.username=O.username,t.port=O.port||22)):O.type==="session-created"?q=!0:O.type==="auth-error"&&(r.logAuth("auth_failed",{message:O.message}),l.writeln(`\r ❌ Authentication failed: ${O.message}\r `))}catch{K&&K.data&&l.write(K.data)}},M=K=>{x=!1,l.writeln(`\r WebSocket error: ${K?.message||"Unknown error"}\r `),l.writeln(`\r Please check if the SSH proxy server is running at ${v}\r `)},Et=K=>{if(!K)return;if(x=!1,K.code===1e3){l.writeln(`\r Connection to SSH relay server closed normally\r `);return}const ue={1006:"Connection lost (network issue or server restart)",1001:"Server going away",1002:"Protocol error",1003:"Unsupported data type",1011:"Server error",1012:"Server restart",1013:"Server overloaded"}[K.code]||`Unknown error (Code: ${K.code})`;l.writeln(`\r Connection to SSH relay server closed: ${ue}\r `),K.reason&&l.writeln(`Reason: ${K.reason}\r `),P&&!R&&A<B?Ye():P&&A>=B&&l.writeln(`\r Max reconnection attempts reached. Please refresh the page.\r `)},gt=async()=>{if(u&&(l.writeln(`🔌 Connecting to ${v}...`),l.writeln(`📱 Tab ID: ${re}`),l.writeln(`💻 Computing ID: ${$}`)),!await ge()){l.writeln(`\r ❌ Failed to initialize encryption\r `);return}try{const O={onOpen:le,onMessage:Ce,onError:M,onClose:Et};await $e.getConnection(v,Q,O),r.info("Connected to WebSocket pool",{wsUrl:v,tabId:re,computingId:$,connectionKey:Q,totalConnections:$e.getConnectionCount(v)})}catch(O){r.error("Failed to connect",{error:O.message,wsUrl:v}),l.writeln(`\r ❌ Failed to connect: ${O.message}\r `)}};return l.onData(K=>{if(x&&$e.isConnected(v,Q)){if(!be.validateMessageSize(K,1e4)){r.warn("Terminal input too large",{size:K.length});return}let O;try{const te=new TextEncoder().encode(K),ve=String.fromCharCode(...te);O=window.btoa(ve)}catch(te){r.error("Failed to encode terminal data",{error:te.message});try{O=window.btoa(K)}catch{const Te=K.replace(/[^\x00-\x7F]/g,"?");O=window.btoa(Te)}}const ue={type:"data",data:O,computingId:$};try{$e.send(v,Q,ue)}catch(te){r.error("Failed to send terminal data",{error:te.message})}}}),setTimeout(()=>{gt()},100),c.fit(),{terminal:l,fitAddon:c,searchAddon:g,reconnect:async()=>{await gt()},resize:()=>{if(!(!l||!c))if(c.fit(),$e.isConnected(v,Q)&&q){const K={type:"resize",cols:l.cols,rows:l.rows};try{$e.send(v,Q,K),console.log(`📏 Server PTY Resized to: ${l.cols}x${l.rows}`)}catch{console.warn("⚠️ Failed to sync resize to server")}}else console.log("⏳ Resize deferred: Waiting for authentication...")},dispose:()=>{console.log(`🗑️ Disposing terminal [${Q}]`),L&&clearTimeout(L),$e.removeSubscriber(v,Q),l.dispose(),r.info("Terminal disposed",{wsUrl:v,tabId:re,computingId:$,connectionKey:Q,remainingConnections:$e.getConnectionCount(v)})},getConnectionInfo:()=>({wsUrl:v,tabId:re,computingId:$,connectionKey:Q,connected:$e.isConnected(v,Q),totalConnections:$e.getConnectionCount(v)})}}const si=Object.freeze(Object.defineProperty({__proto__:null,createTerminal:er},Symbol.toStringTag,{value:"Module"})),oi={name:"SSHTerminal",props:{computingId:{type:String,required:!0},userToken:{type:String,required:!0},websocketUrl:{type:String,default:"wss://ssh-proxy.dev.longvan.vn"},options:{type:Object,default:function(){return{}}}},data:function(){return{terminal:null}},mounted:function(){this.initTerminal(),window.addEventListener("resize",this.handleResize)},beforeDestroy:function(){this.terminal&&this.terminal.dispose(),window.removeEventListener("resize",this.handleResize)},methods:{initTerminal:function(){var e=this,t=this.$refs.terminalContainer;Promise.all([Promise.resolve().then(()=>si),Promise.resolve().then(()=>ai),Promise.resolve().then(()=>ni)]).then(function(a){var r=a[0].createTerminal,n=a[1].default,s=a[2].default,i=s.createContextLogger("Vue2Terminal"),o={computingId:e.computingId,userToken:e.userToken,wsUrl:e.websocketUrl},u=n.validateComputingConfig(o);if(!u.isValid){i.error("Invalid Computing ID config in Vue2 component",{errors:u.errors}),e.$emit("error",{message:"Invalid Computing ID configuration",errors:u.errors});return}var l=n.validateTerminalOptions(e.options);if(!l.isValid){i.error("Invalid terminal options in Vue2 component",{errors:l.errors}),e.$emit("error",{message:"Invalid terminal options",errors:l.errors});return}try{e.terminal=r(t,o,e.options),i.info("Terminal created successfully with Computing ID"),e.$emit("ready",e.terminal)}catch(c){i.error("Failed to create terminal",{error:c.message}),e.$emit("error",{message:"Failed to create terminal",error:c.message})}}).catch(function(a){console.error("Failed to load terminal dependencies:",a),e.$emit("error",{message:"Failed to load terminal dependencies",error:a.message})})},handleResize:function(){this.terminal&&this.terminal.resize()}},render:function(e){return e("div",{ref:"terminalContainer",class:"ssh-terminal-container",style:{width:"100%",height:"100%",minHeight:"300px",backgroundColor:"#000"}})}},a0="",li=((e,t)=>{const a=e.__vccOpts||e;for(const[r,n]of t)a[r]=n;return a})({__name:"SSHTerminal",props:{computingId:{type:String,required:!0},userToken:{type:String,required:!0},websocketUrl:{type:String,default:"wss://ssh-proxy.dev.longvan.vn"},typeConnect:{type:Object},options:{type:Object,default:()=>({})},isActive:{type:Boolean,default:!0},resizeSignal:{type:Number,default:0},tabClientId:{type:String,required:!0}},emits:["ready","error","rdp-redirect"],setup(e,{expose:t,emit:a}){const r=e,n=a,s=Je.ref(null);let i=null,o=null,u=null;Je.onMounted(()=>{c()}),Je.onBeforeUnmount(()=>{i&&i.dispose(),window.removeEventListener("resize",l),o&&o.disconnect(),u&&clearTimeout(u)}),Je.watch(()=>r.resizeSignal,()=>{Je.nextTick(()=>{setTimeout(l,150)})}),Je.watch(()=>r.isActive,g=>{g&&i&&Je.nextTick(()=>{i.terminal?.focus()})});function l(){u&&clearTimeout(u),u=setTimeout(()=>{f()},250)}function c(){const g=s.value,v=Lt.createContextLogger("Vue3Terminal"),y={computingId:r.computingId,userToken:r.userToken,wsUrl:r.websocketUrl,typeConnect:r.typeConnect,tabClientId:r.tabClientId},x=be.validateComputingConfig(y);if(!x.isValid){n("error",{message:"Invalid Computing ID configuration",errors:x.errors});return}try{i=er(g,y,r.options),i._onRdpRedirect=S=>{n("rdp-redirect",S)},o=new ResizeObserver(S=>{for(let b of S){const{width:A,height:B}=b.contentRect;A>0&&B>0&&l()}}),s.value&&o.observe(s.value),n("ready",i)}catch(S){v.error("Failed to create terminal",{error:S.message}),n("error",{message:"Failed to create terminal",error:S.message})}}function f(){i&&(console.log("📏 Terminal resizing to fit container..."),i.resize())}return t({getTerminal:()=>i,resize:f}),(g,v)=>(Je.openBlock(),Je.createElementBlock("div",{class:Je.normalizeClass(["terminal-wrapper",`computingId: ${r.computingId}`])},[Je.createElementVNode("div",{ref_key:"terminalContainer",ref:s,class:Je.normalizeClass(`ssh-terminal-container ${r.computingId}`)},null,2)],2))}},[["__scopeId","data-v-0a812d31"]]);class Na extends HTMLElement{constructor(){super(),this._terminalInstance=null,this._resizeObserver=null,this._shadow=this.attachShadow({mode:"open"}),this._sshConfig=null,this._wsUrl=null,this._options={},this._container=document.createElement("div"),this._container.style.width="100%",this._container.style.height="100%",this._container.style.background="#000",this._container.className="ssh-terminal-container",this._injectStyles(),this._shadow.appendChild(this._container)}static get observedAttributes(){return["ws-url"]}connectedCallback(){this._setupResizeObserver()}disconnectedCallback(){this._cleanup()}attributeChangedCallback(t,a,r){t==="ws-url"&&(this._wsUrl=r)}setConfig(t,a={}){const r=Lt.createContextLogger("WebComponent");if(this._options={...this._options,...a},t.computingId&&t.userToken){const n=be.validateComputingConfig(t);if(!n.isValid){const s=`Invalid computing configuration: ${n.errors.join(", ")}`;r.error("Invalid computing config in setConfig",{errors:n.errors}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${s}</p>`,this._dispatchEvent("error",{message:s,errors:n.errors});return}this._sshConfig={computingId:be.sanitizeString(t.computingId),userToken:t.userToken,wsUrl:t.wsUrl||this._wsUrl||"wss://ssh-proxy.dev.longvan.vn"}}else{const n=be.validateSSHConfig(t);if(!n.isValid){const s=`Invalid SSH configuration: ${n.errors.join(", ")}`;r.error("Invalid SSH config in setConfig",{errors:n.errors}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${s}</p>`,this._dispatchEvent("error",{message:s,errors:n.errors});return}this._sshConfig={host:be.sanitizeString(t.host),username:be.sanitizeString(t.username),password:t.password,wsUrl:t.wsUrl||this._wsUrl||"wss://ssh-proxy.dev.longvan.vn"}}this._sshConfig.computingId?this.initComputingTerminal():this.initTerminal()}_injectStyles(){const t=document.createElement("style");t.textContent=` .xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility,.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.ssh-terminal-container[data-v-f4a58236],.ssh-terminal-container[data-v-b82cc34a]{width:100%;height:100%;min-height:300px;background-color:#000} `,this._shadow.appendChild(t)}_setupResizeObserver(){this._resizeObserver&&this._resizeObserver.disconnect(),this._resizeObserver=new ResizeObserver(()=>{this._terminalInstance?.resize&&(clearTimeout(this._resizeTimeout),this._resizeTimeout=setTimeout(()=>{this._terminalInstance.resize()},100))}),this._resizeObserver.observe(this)}_cleanup(){this._resizeObserver&&(this._resizeObserver.disconnect(),this._resizeObserver=null),this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._terminalInstance?.dispose&&(this._terminalInstance.dispose(),this._terminalInstance=null)}_dispatchEvent(t,a={}){this.dispatchEvent(new CustomEvent(t,{detail:a,bubbles:!0,composed:!0}))}initComputingTerminal(){const t=Lt.createContextLogger("WebComponent");try{this._terminal=er(this._container,this._sshConfig,this._options),this._dispatchEvent("ready",{message:"Computing terminal initialized",computingId:this._sshConfig.computingId})}catch(a){t.error("Failed to create computing terminal",{error:a.message}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">Failed to create terminal: ${a.message}</p>`,this._dispatchEvent("error",{message:a.message})}}initTerminal(){const t=Lt.createContextLogger("WebComponent");if(!this._sshConfig){const o="SSH configuration not set. Call setConfig() first.";t.error(o),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${o}</p>`,this._dispatchEvent("error",{message:o});return}const{host:a,username:r,password:n,wsUrl:s}=this._sshConfig;let i;if(this._sshConfig.computingId&&this._sshConfig.token){if(i=be.validateComputingConfig(this._sshConfig),!i.isValid){const o=`Invalid computing configuration: ${i.errors.join(", ")}`;t.error("Computing config validation failed in initTerminal",{errors:i.errors}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${o}</p>`,this._dispatchEvent("error",{message:o,errors:i.errors});return}}else if(i=be.validateSSHConfig(this._sshConfig),!i.isValid){const o=`Invalid SSH configuration: ${i.errors.join(", ")}`;t.error("SSH config validation failed in initTerminal",{errors:i.errors}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${o}</p>`,this._dispatchEvent("error",{message:o,errors:i.errors});return}try{this._terminalInstance=er(this._container,{host:a,username:r,password:n,wsUrl:s}),setTimeout(()=>{this._dispatchEvent("ready",{host:a,wsUrl:s})},100)}catch(o){const u=`Failed to create terminal: ${o.message}`;t.error("Failed to create terminal",{error:o.message}),this._container.innerHTML=`<p style="color:red;padding:8px;font-family:monospace;">${u}</p>`,this._dispatchEvent("error",{message:u,error:o})}}reconnect(){try{this._terminalInstance?.reconnect(),this._dispatchEvent("reconnecting")}catch(t){console.error("Reconnect failed:",t),this._dispatchEvent("error",{message:"Reconnect failed",error:t})}}resize(){try{this._terminalInstance?.resize()}catch(t){console.error("Resize failed:",t)}}search(t){try{return this._terminalInstance?.search(t)}catch(a){return console.error("Search failed:",a),!1}}searchPrevious(t){try{return this._terminalInstance?.searchPrevious(t)}catch(a){return console.error("Search previous failed:",a),!1}}getTerminalInstance(){return this._terminalInstance}isConnected(){return this._terminalInstance&&this._terminalInstance.terminal}}customElements.define("ssh-terminal",Na),typeof window<"u"&&window.customElements&&!window.customElements.get("ssh-terminal")&&console.log("SSH Terminal Web Component registered");var ui=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ci(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function fi(e){if(e.__esModule)return e;var t=e.default;if(typeof t=="function"){var a=function r(){return this instanceof r?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};a.prototype=t.prototype}else a={};return Object.defineProperty(a,"__esModule",{value:!0}),Object.keys(e).forEach(function(r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(a,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}),a}var j={options:{usePureJavaScript:!1}},Kr={},di=Kr,Ra={};Kr.encode=function(e,t,a){if(typeof t!="string")throw new TypeError('"alphabet" must be a string.');if(a!==void 0&&typeof a!="number")throw new TypeError('"maxline" must be a number.');var r="";if(!(e instanceof Uint8Array))r=hi(e,t);else{var n=0,s=t.length,i=t.charAt(0),o=[0];for(n=0;n<e.length;++n){for(var u=0,l=e[n];u<o.length;++u)l+=o[u]<<8,o[u]=l%s,l=l/s|0;for(;l>0;)o.push(l%s),l=l/s|0}for(n=0;e[n]===0&&n<e.length-1;++n)r+=i;for(n=o.length-1;n>=0;--n)r+=t[o[n]]}if(a){var c=new RegExp(".{1,"+a+"}","g");r=r.match(c).join(`\r `)}return r},Kr.decode=function(e,t){if(typeof e!="string")throw new TypeError('"input" must be a string.');if(typeof t!="string")throw new TypeError('"alphabet" must be a string.');var a=Ra[t];if(!a){a=Ra[t]=[];for(var r=0;r<t.length;++r)a[t.charCodeAt(r)]=r}e=e.replace(/\s/g,"");for(var n=t.length,s=t.charAt(0),i=[0],r=0;r<e.length;r++){var o=a[e.charCodeAt(r)];if(o===void 0)return;for(var u=0,l=o;u<i.length;++u)l+=i[u]*n,i[u]=l&255,l>>=8;for(;l>0;)i.push(l&255),l>>=8}for(var c=0;e[c]===s&&c<e.length-1;++c)i.push(0);return typeof Buffer<"u"?Buffer.from(i.reverse()):new Uint8Array(i.reverse())};function hi(e,t){var a=0,r=t.length,n=t.charAt(0),s=[0];for(a=0;a<e.length();++a){for(var i=0,o=e.at(a);i<s.length;++i)o+=s[i]<<8,s[i]=o%r,o=o/r|0;for(;o>0;)s.push(o%r),o=o/r|0}var u="";for(a=0;e.at(a)===0&&a<e.length()-1;++a)u+=n;for(a=s.length-1;a>=0;--a)u+=t[s[a]];return u}var La=j,ka=di,E=La.util=La.util||{};(function(){if(typeof process<"u"&&process.nextTick&&!process.browser){E.nextTick=process.nextTick,typeof setImmediate=="function"?E.setImmediate=setImmediate:E.setImmediate=E.nextTick;return}if(typeof setImmediate=="function"){E.setImmediate=function(){return setImmediate.apply(void 0,arguments)},E.nextTick=function(i){return setImmediate(i)};return}if(E.setImmediate=function(i){setTimeout(i,0)},typeof window<"u"&&typeof window.postMessage=="function"){let i=function(o){if(o.source===window&&o.data===e){o.stopPropagation();var u=t.slice();t.length=0,u.forEach(function(l){l()})}};var e="forge.setImmediate",t=[];E.setImmediate=function(o){t.push(o),t.length===1&&window.postMessage(e,"*")},window.addEventListener("message",i,!0)}if(typeof MutationObserver<"u"){var a=Date.now(),r=!0,n=document.createElement("div"),t=[];new MutationObserver(function(){var o=t.slice();t.length=0,o.forEach(function(u){u()})}).observe(n,{attributes:!0});var s=E.setImmediate;E.setImmediate=function(o){Date.now()-a>15?(a=Date.now(),s(o)):(t.push(o),t.length===1&&n.setAttribute("a",r=!r))}}E.nextTick=E.setImmediate})(),E.isNodejs=typeof process<"u"&&process.versions&&process.versions.node,E.globalScope=function(){return E.isNodejs?ui:typeof self>"u"?window:self}(),E.isArray=Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"},E.isArrayBuffer=function(e){return typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer},E.isArrayBufferView=function(e){return e&&E.isArrayBuffer(e.buffer)&&e.byteLength!==void 0};function tr(e){if(!(e===8||e===16||e===24||e===32))throw new Error("Only 8, 16, 24, or 32 bits supported: "+e)}E.ByteBuffer=Mr;function Mr(e){if(this.data="",this.read=0,typeof e=="string")this.data=e;else if(E.isArrayBuffer(e)||E.isArrayBufferView(e))if(typeof Buffer<"u"&&e instanceof Buffer)this.data=e.toString("binary");else{var t=new Uint8Array(e);try{this.data=String.fromCharCode.apply(null,t)}catch{for(var a=0;a<t.length;++a)this.putByte(t[a])}}else(e instanceof Mr||typeof e=="object"&&typeof e.data=="string"&&typeof e.read=="number")&&(this.data=e.data,this.read=e.read);this._constructedStringLength=0}E.ByteStringBuffer=Mr;var pi=4096;E.ByteStringBuffer.prototype._optimizeConstructedString=function(e){this._constructedStringLength+=e,this._constructedStringLength>pi&&(this.data.substr(0,1),this._constructedStringLength=0)},E.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read},E.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0},E.ByteStringBuffer.prototype.putByte=function(e){return this.putBytes(String.fromCharCode(e))},E.ByteStringBuffer.prototype.fillWithByte=function(e,t){e=String.fromCharCode(e);for(var a=this.data;t>0;)t&1&&(a+=e),t>>>=1,t>0&&(e+=e);return this.data=a,this._optimizeConstructedString(t),this},E.ByteStringBuffer.prototype.putBytes=function(e){return this.data+=e,this._optimizeConstructedString(e.length),this},E.ByteStringBuffer.prototype.putString=function(e){return this.putBytes(E.encodeUtf8(e))},E.ByteStringBuffer.prototype.putInt16=function(e){return this.putBytes(String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))},E.ByteStringBuffer.prototype.putInt24=function(e){return this.putBytes(String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))},E.ByteStringBuffer.prototype.putInt32=function(e){return this.putBytes(String.fromCharCode(e>>24&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e&255))},E.ByteStringBuffer.prototype.putInt16Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255))},E.ByteStringBuffer.prototype.putInt24Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255))},E.ByteStringBuffer.prototype.putInt32Le=function(e){return this.putBytes(String.fromCharCode(e&255)+String.fromCharCode(e>>8&255)+String.fromCharCode(e>>16&255)+String.fromCharCode(e>>24&255))},E.ByteStringBuffer.prototype.putInt=function(e,t){tr(t);var a="";do t-=8,a+=String.fromCharCode(e>>t&255);while(t>0);return this.putBytes(a)},E.ByteStringBuffer.prototype.putSignedInt=function(e,t){return e<0&&(e+=2<<t-1),this.putInt(e,t)},E.ByteStringBuffer.prototype.putBuffer=function(e){return this.putBytes(e.getBytes())},E.ByteStringBuffer.prototype.getByte=function(){return this.data.charCodeAt(this.read++)},E.ByteStringBuffer.prototype.getInt16=function(){var e=this.data.charCodeAt(this.read)<<8^this.data.charCodeAt(this.read+1);return this.read+=2,e},E.ByteStringBuffer.prototype.getInt24=function(){var e=this.data.charCodeAt(this.read)<<16^this.data.charCodeAt(this.read+1)<<