344 lines
8.7 KiB
TypeScript
344 lines
8.7 KiB
TypeScript
/**
|
|
* routes.ts - OpenClaw Routes
|
|
*
|
|
* Endpoints para:
|
|
* - Detecção manual de padrões
|
|
* - Listagem de padrões
|
|
* - Confirmação de sugestões
|
|
* - Histórico de sugestões
|
|
*
|
|
* Fase 4: OpenClaw Embutido
|
|
*/
|
|
|
|
import { Router, Request, Response } from "express";
|
|
import { getOpenClawEngine } from "./OpenClawEngine";
|
|
import { getPatternDetector } from "./PatternDetector";
|
|
|
|
const router = Router();
|
|
const engine = getOpenClawEngine();
|
|
const detector = getPatternDetector();
|
|
|
|
/**
|
|
* POST /api/openclaw/detect-patterns
|
|
*
|
|
* Trigger manual de detecção de padrões
|
|
* Útil para testes e verificação sob demanda
|
|
*/
|
|
router.post("/detect-patterns", async (req: Request, res: Response) => {
|
|
try {
|
|
const { user_id, tenant_id, action_type } = req.body;
|
|
|
|
// Validar entrada
|
|
if (!user_id || !tenant_id || !action_type) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Missing required fields: user_id, tenant_id, action_type",
|
|
});
|
|
}
|
|
|
|
// Detectar padrão
|
|
const pattern = await engine.detectPattern(user_id, tenant_id, action_type);
|
|
|
|
if (!pattern) {
|
|
return res.status(200).json({
|
|
success: true,
|
|
pattern: null,
|
|
message: "Nenhum padrão detectado (insuficiente ocorrências ou confiança baixa)",
|
|
});
|
|
}
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
pattern,
|
|
message: `Padrão detectado com confiança ${(pattern.confidence * 100).toFixed(1)}%`,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em detect-patterns:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/openclaw/patterns
|
|
*
|
|
* Listar padrões detectados para um usuário
|
|
* Query params:
|
|
* - user_id (required)
|
|
* - tenant_id (required)
|
|
* - action_type (optional) - filtrar por tipo de ação
|
|
*/
|
|
router.get("/patterns", async (req: Request, res: Response) => {
|
|
try {
|
|
const { user_id, tenant_id, action_type } = req.query;
|
|
|
|
if (!user_id || !tenant_id) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Missing required query params: user_id, tenant_id",
|
|
});
|
|
}
|
|
|
|
// Buscar padrões do usuário
|
|
const patterns = await detector.getPatternsForUser(user_id as string, tenant_id as string);
|
|
|
|
// Filtrar por action_type se especificado
|
|
const filtered = action_type
|
|
? patterns.filter((p) => p.action_type === action_type)
|
|
: patterns;
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
patterns: filtered,
|
|
count: filtered.length,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em GET patterns:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/openclaw/confirm-skill
|
|
*
|
|
* Usuário confirma uma sugestão de skill
|
|
*
|
|
* Body:
|
|
* {
|
|
* suggestion_id: string,
|
|
* user_id: string,
|
|
* tenant_id: string
|
|
* }
|
|
*/
|
|
router.post("/confirm-skill", async (req: Request, res: Response) => {
|
|
try {
|
|
const { suggestion_id, user_id, tenant_id } = req.body;
|
|
|
|
if (!suggestion_id || !user_id || !tenant_id) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Missing required fields: suggestion_id, user_id, tenant_id",
|
|
});
|
|
}
|
|
|
|
console.log(`[OpenClaw Routes] Confirmação recebida para sugestão: ${suggestion_id}`);
|
|
|
|
// Emitir evento de confirmação do usuário
|
|
engine.emit("user-confirmed-suggestion", suggestion_id);
|
|
|
|
// Aguardar um pouco para skill ser gerada (não ideal, melhorar com callbacks)
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "Sugestão confirmada! Gerando skill...",
|
|
suggestion_id,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em confirm-skill:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/openclaw/suggestions
|
|
*
|
|
* Listar sugestões pendentes
|
|
* Query params:
|
|
* - user_id (optional) - filtrar por usuário
|
|
* - status (optional) - 'pending', 'accepted', 'rejected'
|
|
*/
|
|
router.get("/suggestions", async (req: Request, res: Response) => {
|
|
try {
|
|
const { user_id, status } = req.query;
|
|
|
|
// Buscar sugestões pendentes
|
|
const suggestions = engine.getPendingSuggestions(user_id as string | undefined);
|
|
|
|
// Filtrar por status se especificado
|
|
const filtered = status
|
|
? suggestions.filter((s) => s.status === status)
|
|
: suggestions;
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
suggestions: filtered,
|
|
count: filtered.length,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em GET suggestions:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/openclaw/config
|
|
*
|
|
* Obter configuração atual do OpenClaw
|
|
* (Para admin panel ou debugging)
|
|
*/
|
|
router.get("/config", async (req: Request, res: Response) => {
|
|
try {
|
|
const config = engine.getConfig();
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
config,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em GET config:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/openclaw/config
|
|
*
|
|
* Atualizar configuração do OpenClaw
|
|
* (Apenas para admins)
|
|
*
|
|
* Body: Partial<OpenClawEngineConfig>
|
|
*/
|
|
router.post("/config", async (req: Request, res: Response) => {
|
|
try {
|
|
// TODO: Validar permissões de admin
|
|
|
|
const config = req.body;
|
|
|
|
engine.setConfig(config);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "Configuração atualizada",
|
|
config: engine.getConfig(),
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em POST config:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/openclaw/check-now
|
|
*
|
|
* Verificar padrões agora (não aguardar scheduler)
|
|
* Query param: tenant_id (required)
|
|
*/
|
|
router.post("/check-now", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenant_id } = req.body;
|
|
|
|
if (!tenant_id) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Missing required field: tenant_id",
|
|
});
|
|
}
|
|
|
|
console.log(`[OpenClaw Routes] Verificação manual disparada para tenant: ${tenant_id}`);
|
|
|
|
// Disparar verificação (não aguardar)
|
|
engine.checkForPatternsScheduled(tenant_id).catch((error) => {
|
|
console.error("[OpenClaw Routes] Erro na verificação manual:", error);
|
|
});
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "Verificação de padrões iniciada",
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em check-now:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/openclaw/start-scheduler
|
|
*
|
|
* Iniciar scheduler de verificação periódica
|
|
* Query param: tenant_id (required)
|
|
*/
|
|
router.post("/start-scheduler", async (req: Request, res: Response) => {
|
|
try {
|
|
const { tenant_id } = req.body;
|
|
|
|
if (!tenant_id) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
error: "Missing required field: tenant_id",
|
|
});
|
|
}
|
|
|
|
engine.startScheduledChecks(tenant_id);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: `Scheduler iniciado para tenant: ${tenant_id}`,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em start-scheduler:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/openclaw/stop-scheduler
|
|
*
|
|
* Parar scheduler de verificação periódica
|
|
*/
|
|
router.post("/stop-scheduler", async (req: Request, res: Response) => {
|
|
try {
|
|
engine.stopScheduledChecks();
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "Scheduler interrompido",
|
|
});
|
|
} catch (error: any) {
|
|
console.error("[OpenClaw Routes] Erro em stop-scheduler:", error);
|
|
return res.status(500).json({
|
|
success: false,
|
|
error: error.message || "Internal server error",
|
|
});
|
|
}
|
|
});
|
|
|
|
/**
|
|
* GET /api/openclaw/health
|
|
*
|
|
* Health check do OpenClaw
|
|
*/
|
|
router.get("/health", (req: Request, res: Response) => {
|
|
return res.status(200).json({
|
|
success: true,
|
|
status: "healthy",
|
|
module: "openclaw",
|
|
timestamp: new Date(),
|
|
});
|
|
});
|
|
|
|
export default router;
|