simbiot-harness/harness/roy.html

83 lines
3.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simbiot · Roy — Claude Code (ttyd)</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.min.css">
<style>
html,body{margin:0;height:100%;background:#161b22;color:#e6edf3;font-family:system-ui,sans-serif}
#bar{display:flex;align-items:center;gap:10px;padding:8px 12px;background:#0d1117;border-bottom:1px solid #30363d}
#bar b{font-size:14px}
#st{margin-left:auto;font-size:12px;color:#8b949e}
#st.ok{color:#3fb950}#st.err{color:#f85149}
#term{padding:6px;height:calc(100% - 42px);box-sizing:border-box}
</style>
</head>
<body>
<div id="bar"><b>Roy · 178.104.52.234 · Claude Code (ttyd)</b>
<span id="st">подключение…</span></div>
<div id="term"></div>
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<script>
(async () => {
const term = new Terminal({cursorBlink:true, fontSize:14, theme:{background:'#161b22'}});
const fit = new FitAddon.FitAddon();
term.loadAddon(fit);
term.open(document.getElementById('term'));
fit.fit();
addEventListener('resize', () => fit.fit());
// токен — как у дашборда: из cookie или query этой страницы
let token = '';
const q = new URLSearchParams(location.search).get('token');
if (q) { token = q; document.cookie = 'simbiot_token=' + encodeURIComponent(q); }
else { const m = document.cookie.match(/(?:^|;\s*)simbiot_token=([^;]+)/); if (m) token = decodeURIComponent(m[1]); }
const wsUrl = (location.protocol==='https:'?'wss://':'ws://') + location.host
+ '/roy/ws' + (token ? '?token=' + encodeURIComponent(token) : '');
const st = document.getElementById('st');
// ttyd-протокол: первый JSON-фрейм {AuthToken:'', Columns:, Rows:}
const ws = new WebSocket(wsUrl);
ws.binaryType = 'arraybuffer';
let initSent = false;
ws.onopen = () => {
ws.send(JSON.stringify({AuthToken:'', Columns:term.cols, Rows:term.rows}));
initSent = true;
st.textContent = 'онлайн'; st.className = 'ok';
term.focus();
};
ws.onmessage = ev => {
const d = new Uint8Array(ev.data instanceof ArrayBuffer ? ev.data : new TextEncoder().encode(ev.data));
if (!d.length) return;
if (d[0] === 0x00) { // OUTPUT: печатаем как есть
term.write(new Uint8Array(d.buffer, 1));
} else if (d[0] === 0x01) { // PING
const p = new Uint8Array(2); p[0] = 0x02; ws.send(p);
}
};
ws.onclose = () => {
st.textContent = initSent ? 'связь потеряна — обновите страницу' : 'нет доступа (токен?)';
st.className = 'err';
};
term.onData(data => {
// INPUT: 0x00 + utf-8
const p = new Uint8Array(1 + data.length);
p[0] = 0x00;
for (let i = 0; i < data.length; i++) p[1+i] = data.charCodeAt(i) & 0xFF;
ws.send(p);
});
// resize: WINDOW_RESIZE 0x02 + json {Columns,Rows}
term.onResize(({cols, rows}) => {
if (ws.readyState !== 1) return;
const j = JSON.stringify({Columns:cols, Rows:rows});
const p = new Uint8Array(1 + j.length);
p[0] = 0x02;
for (let i = 0; i < j.length; i++) p[1+i] = j.charCodeAt(i);
ws.send(p);
});
})();
</script>
</body>
</html>