claude-code streaming chat, desktop remote viewer, new-automation route, tiktok task v4, misc fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { createRouter } from '../../create-router';
|
||||
|
||||
export const desktopRouter = createRouter();
|
||||
|
||||
desktopRouter.get('/vnc-password', (ctx) => {
|
||||
const password = process.env.VNC_PASSWORD;
|
||||
if (!password) {
|
||||
return ctx.json({ error: 'VNC password not configured' }, 500);
|
||||
}
|
||||
return ctx.json({ password });
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { ServerWebSocket } from 'bun';
|
||||
import type { Socket } from 'bun';
|
||||
|
||||
type WSData = { userId: number; email: string; username: string; role: string; sandboxed: boolean; sessionId?: string };
|
||||
|
||||
type VncSession = {
|
||||
tcpSocket: Socket<{ ws: ServerWebSocket<WSData> }> | null;
|
||||
pendingMessages: Buffer[];
|
||||
};
|
||||
|
||||
const VNC_PORT = Number(process.env.VNC_PORT || 5901);
|
||||
|
||||
const sessions = new Map<ServerWebSocket<WSData>, VncSession>();
|
||||
|
||||
export const desktopWebsocket = {
|
||||
async open(ws: ServerWebSocket<WSData>) {
|
||||
if (ws.data.role !== 'Super Admin') {
|
||||
ws.close(4003, 'Forbidden');
|
||||
return;
|
||||
}
|
||||
|
||||
const session: VncSession = { tcpSocket: null, pendingMessages: [] };
|
||||
sessions.set(ws, session);
|
||||
|
||||
try {
|
||||
const tcpSocket = await Bun.connect({
|
||||
hostname: '127.0.0.1',
|
||||
port: VNC_PORT,
|
||||
socket: {
|
||||
data(_socket, data) {
|
||||
try {
|
||||
ws.sendBinary(Buffer.from(data));
|
||||
} catch {
|
||||
// ws already closed
|
||||
}
|
||||
},
|
||||
close() {
|
||||
sessions.delete(ws);
|
||||
try { ws.close(); } catch { /* ignore */ }
|
||||
},
|
||||
error(_socket, err) {
|
||||
console.error('[desktop] TCP error:', err.message);
|
||||
sessions.delete(ws);
|
||||
try { ws.close(); } catch { /* ignore */ }
|
||||
},
|
||||
connectError(_socket, err) {
|
||||
console.error('[desktop] TCP connect error:', err.message);
|
||||
sessions.delete(ws);
|
||||
try { ws.close(4004, 'VNC server unavailable'); } catch { /* ignore */ }
|
||||
},
|
||||
open(socket) {
|
||||
// Flush any pending messages
|
||||
for (const msg of session.pendingMessages) {
|
||||
socket.write(msg);
|
||||
}
|
||||
session.pendingMessages = [];
|
||||
},
|
||||
},
|
||||
data: { ws },
|
||||
});
|
||||
|
||||
session.tcpSocket = tcpSocket;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to connect to VNC server';
|
||||
console.error('[desktop] VNC connect failed:', message);
|
||||
sessions.delete(ws);
|
||||
ws.close(4004, 'VNC server unavailable');
|
||||
}
|
||||
},
|
||||
|
||||
message(ws: ServerWebSocket<WSData>, raw: string | Buffer) {
|
||||
const session = sessions.get(ws);
|
||||
if (!session) return;
|
||||
|
||||
const data = typeof raw === 'string' ? Buffer.from(raw) : Buffer.from(raw);
|
||||
|
||||
if (!session.tcpSocket) {
|
||||
session.pendingMessages.push(data);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
session.tcpSocket.write(data);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
},
|
||||
|
||||
close(ws: ServerWebSocket<WSData>) {
|
||||
const session = sessions.get(ws);
|
||||
if (session?.tcpSocket) {
|
||||
try {
|
||||
session.tcpSocket.end();
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
sessions.delete(ws);
|
||||
},
|
||||
|
||||
drain() {},
|
||||
};
|
||||
Reference in New Issue
Block a user