Files
openclaw/integrations/paradiz-web/paradiz-web-agent-server.mjs

131 lines
5.1 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
import http from 'node:http';
import { execFile } from 'node:child_process';
import { readFileSync, existsSync } from 'node:fs';
const ENV_PATH = process.env.PARADIZWEB_ENV_PATH || '/home/openclaw/.openclaw/agents/paradizweb/agent/.env';
function loadEnv(path) {
const env = {};
if (!existsSync(path)) return env;
const raw = readFileSync(path, 'utf8');
for (const line of raw.split(/\r?\n/)) {
if (!line || line.trim().startsWith('#') || !line.includes('=')) continue;
const idx = line.indexOf('=');
const key = line.slice(0, idx).trim();
const val = line.slice(idx + 1).trim();
env[key] = val;
}
return env;
}
const env = { ...loadEnv(ENV_PATH), ...process.env };
const PORT = Number(env.PARADIZWEB_UPSTREAM_PORT || 8787);
const HOST = env.PARADIZWEB_UPSTREAM_HOST || '127.0.0.1';
const TOKEN = env.PARADIZWEB_API_TOKEN || '';
const ALLOWED_ORIGIN = env.PARADIZWEB_ALLOWED_ORIGIN || 'https://vparadize.ru';
const AGENT_ID = env.PARADIZWEB_AGENT_ID || 'paradizweb';
if (!TOKEN) {
console.error('PARADIZWEB_API_TOKEN is empty. Set it in .env');
process.exit(1);
}
function sanitizeForWebsite(text) {
const lines = String(text || '').split(/\r?\n/);
const cleaned = lines.filter((line) => {
const t = line.trim();
if (!t) return true;
if (t.startsWith('🔵')) return false;
if (t.startsWith('Лимиты:')) return false;
if (/^Аккаунт:/i.test(t)) return false;
if (/^Модель:/i.test(t)) return false;
if (/^Codex-аккаунт:/i.test(t)) return false;
return true;
}).join('\n').trim();
return cleaned;
}
function json(res, code, payload) {
res.writeHead(code, {
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Origin': ALLOWED_ORIGIN,
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Methods': 'POST, OPTIONS'
});
res.end(JSON.stringify(payload));
}
const OPENCLAW_BIN = env.OPENCLAW_BIN || '/home/openclaw/.npm-global/bin/openclaw';
function runAgent(userText) {
return new Promise((resolve, reject) => {
const guardrail = [
'Ты агент paradizweb.',
'Работай только по теме бронирования/подбора проживания для Парадиз.',
'Используй только логику скилла paradiz.',
'Не выполняй задачи вне бронирования и не меняй файлы.',
'Если данных не хватает для брони — задай уточняющие вопросы.'
].join(' ');
const message = `${guardrail}\n\nОтвечай клиенту полезно и конкретно. Не используй NO_REPLY для веб-чата. Никогда не выводи служебные техстроки (аккаунт, модель, лимиты, codex-аккаунт).\n\nВопрос клиента: ${userText}`;
execFile(
OPENCLAW_BIN,
['agent', '--agent', AGENT_ID, '--message', message, '--json'],
{ timeout: 120000, maxBuffer: 1024 * 1024 },
(err, stdout, stderr) => {
if (err) {
return reject(new Error(stderr || err.message));
}
try {
const parsed = JSON.parse(stdout);
const text = (parsed?.result?.payloads?.[0]?.text || '').trim();
if (!text || text === 'NO_REPLY') {
return resolve('Здравствуйте! Я на связи 😊 Напишите, пожалуйста, даты заезда/выезда и состав гостей — сразу подберу варианты и посчитаю стоимость.');
}
const sanitized = sanitizeForWebsite(text);
if (!sanitized) {
return resolve('Здравствуйте! Напишите, пожалуйста, даты и количество гостей — сразу подготовлю варианты.');
}
resolve(sanitized);
} catch (e) {
reject(new Error(`Bad JSON from openclaw agent: ${e.message}`));
}
}
);
});
}
const server = http.createServer(async (req, res) => {
if (req.method === 'OPTIONS') return json(res, 200, { ok: true });
if (req.method !== 'POST' || req.url !== '/chat') {
return json(res, 404, { ok: false, error: 'not_found' });
}
const auth = req.headers.authorization || '';
if (auth !== `Bearer ${TOKEN}`) {
return json(res, 401, { ok: false, error: 'unauthorized' });
}
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', async () => {
try {
const parsed = JSON.parse(body || '{}');
const question = String(parsed.question || '').trim();
if (!question) return json(res, 400, { ok: false, error: 'question_required' });
const answer = await runAgent(question);
return json(res, 200, { ok: true, answer });
} catch (e) {
return json(res, 500, { ok: false, error: e.message });
}
});
});
server.listen(PORT, HOST, () => {
console.log(`paradiz-web-agent listening on http://${HOST}:${PORT}`);
});