BMHQ Upgrade: Add Auto-Run, Execution Logs, Change Log, Brainown panels
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const STORAGE_DIR = path.join(process.cwd(), "data", "agent-outputs");
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(STORAGE_DIR)) {
|
||||
fs.mkdirSync(STORAGE_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function getAgentDir(agent: string) {
|
||||
const dir = path.join(STORAGE_DIR, agent);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const agent = searchParams.get("agent");
|
||||
const date = searchParams.get("date");
|
||||
|
||||
if (!agent) {
|
||||
// Return list of all agent directories
|
||||
ensureDir();
|
||||
const agents = fs.readdirSync(STORAGE_DIR).filter(f =>
|
||||
fs.statSync(path.join(STORAGE_DIR, f)).isDirectory()
|
||||
);
|
||||
return NextResponse.json(agents);
|
||||
}
|
||||
|
||||
const agentDir = getAgentDir(agent);
|
||||
|
||||
if (date) {
|
||||
// Return specific file
|
||||
const file = path.join(agentDir, `${date}.md`);
|
||||
if (fs.existsSync(file)) {
|
||||
const content = fs.readFileSync(file, "utf-8");
|
||||
return NextResponse.json({ agent, date, content });
|
||||
}
|
||||
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Return list of files for agent
|
||||
const files = fs.readdirSync(agentDir)
|
||||
.filter(f => f.endsWith('.md'))
|
||||
.map(f => f.replace('.md', ''))
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
return NextResponse.json(files);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { action, agent, date, content } = body;
|
||||
|
||||
if (action === "save") {
|
||||
const agentDir = getAgentDir(agent);
|
||||
const file = path.join(agentDir, `${date}.md`);
|
||||
fs.writeFileSync(file, content);
|
||||
return NextResponse.json({ success: true, file });
|
||||
}
|
||||
|
||||
if (action === "delete") {
|
||||
const agentDir = getAgentDir(agent);
|
||||
const file = path.join(agentDir, `${date}.md`);
|
||||
if (fs.existsSync(file)) {
|
||||
fs.unlinkSync(file);
|
||||
}
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const STORAGE_DIR = path.join(process.cwd(), "data");
|
||||
const CHANGELOG_FILE = path.join(STORAGE_DIR, "changelog.json");
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(STORAGE_DIR)) {
|
||||
fs.mkdirSync(STORAGE_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function readJSON(file: string, defaultValue: any = []) {
|
||||
ensureDir();
|
||||
if (!fs.existsSync(file)) {
|
||||
fs.writeFileSync(file, JSON.stringify(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJSON(file: string, data: any) {
|
||||
ensureDir();
|
||||
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const agent = searchParams.get("agent");
|
||||
const type = searchParams.get("type");
|
||||
const limit = parseInt(searchParams.get("limit") || "50");
|
||||
|
||||
let changelog = readJSON(CHANGELOG_FILE, []);
|
||||
|
||||
if (agent) {
|
||||
changelog = changelog.filter((c: any) => c.agent === agent);
|
||||
}
|
||||
|
||||
if (type) {
|
||||
changelog = changelog.filter((c: any) => c.type === type);
|
||||
}
|
||||
|
||||
changelog = changelog.slice(0, limit);
|
||||
|
||||
return NextResponse.json(changelog);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { action, entry, entryId } = body;
|
||||
|
||||
const changelog = readJSON(CHANGELOG_FILE, []);
|
||||
|
||||
if (action === "add") {
|
||||
const newEntry = {
|
||||
id: `changelog-${Date.now()}`,
|
||||
date: new Date().toISOString(),
|
||||
...entry
|
||||
};
|
||||
changelog.unshift(newEntry);
|
||||
writeJSON(CHANGELOG_FILE, changelog);
|
||||
return NextResponse.json({ success: true, changelog });
|
||||
}
|
||||
|
||||
if (action === "delete") {
|
||||
const filtered = changelog.filter((c: any) => c.id !== entryId);
|
||||
writeJSON(CHANGELOG_FILE, filtered);
|
||||
return NextResponse.json({ success: true, changelog: filtered });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const STORAGE_DIR = path.join(process.cwd(), "data");
|
||||
const LOGS_FILE = path.join(STORAGE_DIR, "execution-logs.json");
|
||||
const OUTPUTS_FILE = path.join(STORAGE_DIR, "agent-outputs.json");
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(STORAGE_DIR)) {
|
||||
fs.mkdirSync(STORAGE_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function readJSON(file: string, defaultValue: any = []) {
|
||||
ensureDir();
|
||||
if (!fs.existsSync(file)) {
|
||||
fs.writeFileSync(file, JSON.stringify(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJSON(file: string, data: any) {
|
||||
ensureDir();
|
||||
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const agent = searchParams.get("agent");
|
||||
const templateId = searchParams.get("templateId");
|
||||
const limit = parseInt(searchParams.get("limit") || "50");
|
||||
|
||||
let logs = readJSON(LOGS_FILE, []);
|
||||
|
||||
// Filter by agent
|
||||
if (agent) {
|
||||
logs = logs.filter((l: any) => l.agent === agent);
|
||||
}
|
||||
|
||||
// Filter by template
|
||||
if (templateId) {
|
||||
logs = logs.filter((l: any) => l.templateId === templateId);
|
||||
}
|
||||
|
||||
// Limit
|
||||
logs = logs.slice(0, limit);
|
||||
|
||||
return NextResponse.json(logs);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { action, logId, output, status, agent } = body;
|
||||
|
||||
const logs = readJSON(LOGS_FILE, []);
|
||||
|
||||
if (action === "update") {
|
||||
const idx = logs.findIndex((l: any) => l.id === logId);
|
||||
if (idx >= 0) {
|
||||
if (status) logs[idx].status = status;
|
||||
if (output) logs[idx].output = output;
|
||||
if (status === "completed" || status === "failed") {
|
||||
logs[idx].completedAt = new Date().toISOString();
|
||||
}
|
||||
writeJSON(LOGS_FILE, logs);
|
||||
return NextResponse.json({ success: true, log: logs[idx] });
|
||||
}
|
||||
}
|
||||
|
||||
if (action === "clear") {
|
||||
// Clear old logs (keep last 100)
|
||||
const filtered = logs.slice(0, 100);
|
||||
writeJSON(LOGS_FILE, filtered);
|
||||
return NextResponse.json({ success: true, logs: filtered });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const STORAGE_DIR = path.join(process.cwd(), "data");
|
||||
const TEMPLATES_FILE = path.join(STORAGE_DIR, "recurring-templates.json");
|
||||
const LOGS_FILE = path.join(STORAGE_DIR, "execution-logs.json");
|
||||
const CHANGELOG_FILE = path.join(STORAGE_DIR, "changelog.json");
|
||||
const OUTPUTS_FILE = path.join(STORAGE_DIR, "agent-outputs.json");
|
||||
|
||||
function ensureDir() {
|
||||
if (!fs.existsSync(STORAGE_DIR)) {
|
||||
fs.mkdirSync(STORAGE_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function readJSON(file: string, defaultValue: any = []) {
|
||||
ensureDir();
|
||||
if (!fs.existsSync(file)) {
|
||||
fs.writeFileSync(file, JSON.stringify(defaultValue));
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
||||
} catch {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
function writeJSON(file: string, data: any) {
|
||||
ensureDir();
|
||||
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
// Templates CRUD
|
||||
export async function GET() {
|
||||
const templates = readJSON(TEMPLATES_FILE, []);
|
||||
return NextResponse.json(templates);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { action, template, templateId, enabled } = body;
|
||||
|
||||
const templates = readJSON(TEMPLATES_FILE, []);
|
||||
|
||||
if (action === "toggle") {
|
||||
const idx = templates.findIndex((t: any) => t.id === templateId);
|
||||
if (idx >= 0) {
|
||||
templates[idx].enabled = enabled;
|
||||
writeJSON(TEMPLATES_FILE, templates);
|
||||
return NextResponse.json({ success: true, templates });
|
||||
}
|
||||
}
|
||||
|
||||
if (action === "save") {
|
||||
const idx = templates.findIndex((t: any) => t.id === template.id);
|
||||
if (idx >= 0) {
|
||||
templates[idx] = { ...templates[idx], ...template };
|
||||
} else {
|
||||
templates.push({ ...template, runCount: 0, enabled: false });
|
||||
}
|
||||
writeJSON(TEMPLATES_FILE, templates);
|
||||
return NextResponse.json({ success: true, templates });
|
||||
}
|
||||
|
||||
if (action === "delete") {
|
||||
const filtered = templates.filter((t: any) => t.id !== templateId);
|
||||
writeJSON(TEMPLATES_FILE, filtered);
|
||||
return NextResponse.json({ success: true, templates: filtered });
|
||||
}
|
||||
|
||||
if (action === "run-now") {
|
||||
// Trigger immediate execution
|
||||
const idx = templates.findIndex((t: any) => t.id === templateId);
|
||||
if (idx >= 0) {
|
||||
const template = templates[idx];
|
||||
|
||||
// Create execution log
|
||||
const logs = readJSON(LOGS_FILE, []);
|
||||
const logId = `log-${Date.now()}`;
|
||||
const newLog = {
|
||||
id: logId,
|
||||
templateId: template.id,
|
||||
agent: template.agent,
|
||||
taskTitle: template.taskTemplate.title,
|
||||
startedAt: new Date().toISOString(),
|
||||
status: "running",
|
||||
output: "Agent executing..."
|
||||
};
|
||||
logs.unshift(newLog);
|
||||
writeJSON(LOGS_FILE, logs);
|
||||
|
||||
// Update template
|
||||
templates[idx].lastRun = new Date().toISOString();
|
||||
templates[idx].runCount = (templates[idx].runCount || 0) + 1;
|
||||
writeJSON(TEMPLATES_FILE, templates);
|
||||
|
||||
// TODO: Actually dispatch to OpenClaw agent here
|
||||
// For now, simulate completion after delay
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
templates,
|
||||
executionLog: newLog
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
||||
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user