// Open a real terminal on the machine through the UI websocket and run a command, like the web page does. import crypto from 'node:crypto'; const [pw, secret] = process.argv.slice(2); function totp(secret){const a='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';let b='';for(const c of secret)b+=a.indexOf(c).toString(2).padStart(5,'0');const k=Buffer.from(b.match(/.{8}/g).map(x=>parseInt(x,2)));const t=Buffer.alloc(8);t.writeBigUInt64BE(BigInt(Math.floor(Date.now()/30000)));const h=crypto.createHmac('sha1',k).update(t).digest();const o=h[19]&15;return String((h.readUInt32BE(o)&0x7fffffff)%1000000).padStart(6,'0')} const r = await fetch('https://control.wxch.org/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pw, code: totp(secret) }) }); console.log('login', r.status); const cookie = r.headers.get('set-cookie').split(';')[0]; const ws = new WebSocket('wss://control.wxch.org/ws/ui', { headers: { Cookie: cookie } }); let out = '', agents = null, ended = false; ws.onmessage = ev => { const m = JSON.parse(ev.data); if (m.t === 'agents') agents = m.list; if (m.t === 'out') out += Buffer.from(m.d, 'base64').toString(); if (m.t === 'exit') ended = true; }; await new Promise(res => ws.onopen = res); await new Promise(res => setTimeout(res, 1500)); console.log('agents:', agents); ws.send(JSON.stringify({ t: 'open', sid: 't1', agent: 'pgx', cols: 100, rows: 30 })); await new Promise(res => setTimeout(res, 3000)); console.log('prompt received:', JSON.stringify(out.slice(-120))); ws.send(JSON.stringify({ t: 'in', sid: 't1', d: Buffer.from('echo TERMINAL-OK; hostname\n').toString('base64') })); await new Promise(res => setTimeout(res, 2000)); console.log('after command:', JSON.stringify(out.slice(-160)), '| ended:', ended); ws.send(JSON.stringify({ t: 'close', sid: 't1' })); ws.close();