feat(app): enhance theme support and improve PWA integration
- Updated the app to support light and dark themes with appropriate CSS classes. - Enhanced PWA configuration with manifest details and caching strategies. - Improved the chat UI with dynamic theme adjustments for various components. - Added new meta tags for better mobile web app experience. - Refactored environment variables to include new Anthropic token. - Updated package dependencies for better compatibility and performance. Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { spawn } from 'child_process'
|
||||
import { createServer } from 'http'
|
||||
import { resolve } from 'path'
|
||||
|
||||
const PORT = 3141
|
||||
const CLAUDE_BIN = resolve(process.env.HOME ?? '', '.local/bin/claude')
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204, {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
})
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
if (req.method !== 'POST' || req.url !== '/v1/messages') {
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' })
|
||||
res.end(JSON.stringify({ error: 'Not found' }))
|
||||
return
|
||||
}
|
||||
|
||||
let body = ''
|
||||
req.on('data', (chunk) => { body += chunk })
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { model, messages, system } = JSON.parse(body)
|
||||
|
||||
const modelFlag = model?.includes('opus') ? 'opus'
|
||||
: model?.includes('haiku') ? 'haiku'
|
||||
: 'sonnet'
|
||||
|
||||
const history = (messages ?? []) as { role: string; content: string }[]
|
||||
const userMessages = history.filter((m) => m.role === 'user')
|
||||
const lastUserMsg = userMessages[userMessages.length - 1]?.content ?? ''
|
||||
|
||||
const contextParts: string[] = []
|
||||
if (system) contextParts.push(system)
|
||||
const prior = history.slice(0, -1)
|
||||
if (prior.length > 0) {
|
||||
contextParts.push(
|
||||
'Conversation so far:\n' +
|
||||
prior.map((m) => `${m.role}: ${m.content}`).join('\n')
|
||||
)
|
||||
}
|
||||
|
||||
const systemPrompt = contextParts.length > 0 ? contextParts.join('\n\n') : undefined
|
||||
|
||||
const args = ['-p', '--model', modelFlag]
|
||||
if (systemPrompt) args.push('--system-prompt', systemPrompt)
|
||||
args.push('--', lastUserMsg)
|
||||
|
||||
console.log(`[proxy] → claude -p --model ${modelFlag} "${lastUserMsg.slice(0, 60)}..."`)
|
||||
|
||||
const proc = spawn(CLAUDE_BIN, args, {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
env: { ...process.env, NO_COLOR: '1', TERM: 'dumb' },
|
||||
detached: false,
|
||||
})
|
||||
|
||||
proc.stdin.end('')
|
||||
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'X-Accel-Buffering': 'no',
|
||||
})
|
||||
|
||||
let fullOutput = ''
|
||||
let clientDisconnected = false
|
||||
|
||||
proc.stdout.on('data', (chunk: Buffer) => {
|
||||
const text = chunk.toString()
|
||||
fullOutput += text
|
||||
console.log(`[proxy] stdout +${text.length}b total=${fullOutput.length}b`)
|
||||
|
||||
if (!clientDisconnected) {
|
||||
const sseData = {
|
||||
type: 'content_block_delta',
|
||||
delta: { type: 'text_delta', text },
|
||||
}
|
||||
res.write(`data: ${JSON.stringify(sseData)}\n\n`)
|
||||
}
|
||||
})
|
||||
|
||||
proc.stderr.on('data', (chunk: Buffer) => {
|
||||
const msg = chunk.toString().trim()
|
||||
if (msg) console.error('[proxy] stderr:', msg)
|
||||
})
|
||||
|
||||
proc.on('error', (err) => {
|
||||
console.error('[proxy] spawn error:', err)
|
||||
if (!clientDisconnected) {
|
||||
const errData = {
|
||||
type: 'error',
|
||||
error: { message: `Spawn error: ${err.message}` },
|
||||
}
|
||||
res.write(`data: ${JSON.stringify(errData)}\n\n`)
|
||||
res.write('data: [DONE]\n\n')
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
|
||||
proc.on('close', (code, signal) => {
|
||||
console.log(`[proxy] ← exit code=${code} signal=${signal} output=${fullOutput.length}b`)
|
||||
if (!clientDisconnected) {
|
||||
res.write('data: [DONE]\n\n')
|
||||
res.end()
|
||||
}
|
||||
})
|
||||
|
||||
res.on('close', () => {
|
||||
clientDisconnected = true
|
||||
if (proc.exitCode === null && !proc.killed) {
|
||||
console.log('[proxy] Client disconnected, killing process')
|
||||
proc.kill('SIGTERM')
|
||||
}
|
||||
})
|
||||
|
||||
} catch (err) {
|
||||
console.error('[proxy] Parse error:', err)
|
||||
res.writeHead(400, {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
})
|
||||
res.end(JSON.stringify({ error: String(err) }))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`\n Claude proxy → http://localhost:${PORT}`)
|
||||
console.log(` Binary: ${CLAUDE_BIN}`)
|
||||
console.log(` Using your Max subscription\n`)
|
||||
})
|
||||
Reference in New Issue
Block a user