Adds the desktop web assistant app (Vite + React) with OpenClaw bridge proxy and exposes it on the local network (host: 0.0.0.0, port 5174). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
3.4 KiB
JavaScript
103 lines
3.4 KiB
JavaScript
import { defineConfig } from 'vite'
|
|
import react from '@vitejs/plugin-react'
|
|
import tailwindcss from '@tailwindcss/vite'
|
|
|
|
function bridgeProxyPlugin() {
|
|
return {
|
|
name: 'bridge-proxy',
|
|
configureServer(server) {
|
|
// Proxy a request to the OpenClaw bridge
|
|
const proxyRequest = (targetPath) => async (req, res) => {
|
|
if (req.method === 'OPTIONS') {
|
|
res.writeHead(204, {
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Headers': 'Content-Type',
|
|
})
|
|
res.end()
|
|
return
|
|
}
|
|
|
|
try {
|
|
const { default: http } = await import('http')
|
|
const chunks = []
|
|
for await (const chunk of req) chunks.push(chunk)
|
|
const body = Buffer.concat(chunks)
|
|
|
|
await new Promise((resolve, reject) => {
|
|
const proxyReq = http.request(
|
|
`http://localhost:8081${targetPath}`,
|
|
{
|
|
method: req.method,
|
|
headers: {
|
|
'Content-Type': req.headers['content-type'] || 'application/json',
|
|
'Content-Length': body.length,
|
|
},
|
|
timeout: 120000,
|
|
},
|
|
(proxyRes) => {
|
|
res.writeHead(proxyRes.statusCode, {
|
|
'Content-Type': proxyRes.headers['content-type'] || 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
})
|
|
proxyRes.pipe(res)
|
|
proxyRes.on('end', resolve)
|
|
proxyRes.on('error', resolve)
|
|
}
|
|
)
|
|
proxyReq.on('error', reject)
|
|
proxyReq.on('timeout', () => {
|
|
proxyReq.destroy()
|
|
reject(new Error('timeout'))
|
|
})
|
|
proxyReq.write(body)
|
|
proxyReq.end()
|
|
})
|
|
} catch {
|
|
if (!res.headersSent) {
|
|
res.writeHead(502, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ error: 'Bridge unreachable' }))
|
|
}
|
|
}
|
|
}
|
|
|
|
server.middlewares.use('/api/agent/message', proxyRequest('/api/agent/message'))
|
|
server.middlewares.use('/api/tts', proxyRequest('/api/tts'))
|
|
server.middlewares.use('/api/stt', proxyRequest('/api/stt'))
|
|
|
|
// Health check — direct to bridge
|
|
server.middlewares.use('/api/health', async (req, res) => {
|
|
try {
|
|
const { default: http } = await import('http')
|
|
const start = Date.now()
|
|
await new Promise((resolve, reject) => {
|
|
const reqObj = http.get('http://localhost:8081/', { timeout: 5000 }, (resp) => {
|
|
resp.resume()
|
|
resolve()
|
|
})
|
|
reqObj.on('error', reject)
|
|
reqObj.on('timeout', () => { reqObj.destroy(); reject(new Error('timeout')) })
|
|
})
|
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ status: 'online', responseTime: Date.now() - start }))
|
|
} catch {
|
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ status: 'offline', responseTime: null }))
|
|
}
|
|
})
|
|
},
|
|
}
|
|
}
|
|
|
|
export default defineConfig({
|
|
plugins: [
|
|
bridgeProxyPlugin(),
|
|
tailwindcss(),
|
|
react(),
|
|
],
|
|
server: {
|
|
host: '0.0.0.0',
|
|
port: 5174,
|
|
},
|
|
})
|