- Implement local backend AI provider with Ollama integration - Add Brave Search API integration for real-time search suggestions - Add image generation manager with multiple AI providers - Create hybrid provider system with local/cloud fallback - Add comprehensive settings UI with provider management - Implement Gutenberg sidebar with writing assistance controls - Add SEO schema generation for AI-generated content - Multiple provider support: OpenRouter, local backend, Codex
123 lines
3.8 KiB
JavaScript
123 lines
3.8 KiB
JavaScript
const express = require('express');
|
|
const { spawn } = require('child_process');
|
|
const app = express();
|
|
|
|
app.use(express.json());
|
|
|
|
// Health check endpoint
|
|
app.get('/ping', (req, res) => {
|
|
res.send('pong');
|
|
});
|
|
|
|
// Main inference endpoint (OpenAI-compatible format)
|
|
app.post('/v1/messages', async (req, res) => {
|
|
const { messages } = req.body;
|
|
|
|
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
|
return res.status(400).json({
|
|
error: {
|
|
message: 'Invalid request: messages array required'
|
|
}
|
|
});
|
|
}
|
|
|
|
// Extract the last user message as the prompt
|
|
const lastMessage = messages[messages.length - 1];
|
|
const prompt = lastMessage.content;
|
|
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
console.log('Request from:', req.ip);
|
|
console.log('Prompt length:', prompt.length, 'chars');
|
|
console.log('Prompt preview:', prompt.substring(0, 150) + '...');
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
|
|
// Spawn Claude CLI process
|
|
const claude = spawn('claude', [], {
|
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
});
|
|
|
|
let output = '';
|
|
let errorOutput = '';
|
|
|
|
claude.stdout.on('data', (data) => {
|
|
output += data.toString();
|
|
process.stdout.write('.');
|
|
});
|
|
|
|
claude.stderr.on('data', (data) => {
|
|
errorOutput += data.toString();
|
|
console.error('Claude stderr:', data.toString());
|
|
});
|
|
|
|
claude.on('close', (code) => {
|
|
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
console.log('Claude exit code:', code);
|
|
console.log('Response length:', output.length, 'chars');
|
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
|
|
if (code !== 0 || !output.trim()) {
|
|
return res.status(500).json({
|
|
error: {
|
|
message: 'Claude CLI error',
|
|
details: errorOutput || 'No response from Claude'
|
|
}
|
|
});
|
|
}
|
|
|
|
// Return OpenAI-compatible response format
|
|
res.json({
|
|
id: 'local-' + Date.now(),
|
|
object: 'chat.completion',
|
|
created: Math.floor(Date.now() / 1000),
|
|
model: 'claude-local',
|
|
choices: [{
|
|
index: 0,
|
|
message: {
|
|
role: 'assistant',
|
|
content: output.trim()
|
|
},
|
|
finish_reason: 'stop'
|
|
}],
|
|
usage: {
|
|
prompt_tokens: 0,
|
|
completion_tokens: 0,
|
|
total_tokens: 0
|
|
}
|
|
});
|
|
});
|
|
|
|
claude.on('error', (err) => {
|
|
console.error('Failed to spawn Claude CLI:', err);
|
|
res.status(500).json({
|
|
error: {
|
|
message: 'Failed to spawn Claude CLI',
|
|
details: err.message
|
|
}
|
|
});
|
|
});
|
|
|
|
// Send prompt to Claude after brief pause
|
|
setTimeout(() => {
|
|
claude.stdin.write(prompt + '\n');
|
|
claude.stdin.end();
|
|
}, 100);
|
|
});
|
|
|
|
const PORT = process.env.PORT || 8080;
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|
console.log('═══════════════════════════════════════════════════');
|
|
console.log('🚀 Agentic Writer Local Backend Started!');
|
|
console.log('═══════════════════════════════════════════════════');
|
|
console.log(`Local: http://localhost:${PORT}`);
|
|
console.log(`Network: http://YOUR-IP:${PORT}`);
|
|
console.log('');
|
|
console.log('Plugin Configuration:');
|
|
console.log(` Base URL: http://YOUR-IP:${PORT}`);
|
|
console.log(` API Key: dummy`);
|
|
console.log(` Model: claude-local`);
|
|
console.log('');
|
|
console.log('Health check: GET /ping');
|
|
console.log('Inference: POST /v1/messages');
|
|
console.log('═══════════════════════════════════════════════════');
|
|
});
|