From 72b32281febca2f5c268947ea00ea22a75a97712 Mon Sep 17 00:00:00 2001 From: Deploy Bot Date: Thu, 1 Jan 2026 03:00:57 +0000 Subject: [PATCH 1/2] feat: Add Brain Server proxy for centralized knowledge base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add brain-proxy.ts: HTTP proxy to Brain Server (49.13.158.176:5001) - Add brain/client.ts: BrainClient HTTP adapter - Update index.ts: Conditional proxy mode via BRAIN_SERVER_URL env - Update ecosystem.config.cjs: Add BRAIN_SERVER_URL config - Add start-backend.sh: Wrapper script with env vars for PM2 Architecture: - Cloud-Agents (178.156.178.70:3001) proxies /api/brain/* to Brain-Server - Brain-Server (49.13.158.176:5001) stores all knowledge base data centrally - Fallback to local mode if BRAIN_SERVER_URL not set 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ecosystem.config.cjs | 7 +- src/api/brain-proxy.ts | 294 +++++++++++++++++++++++++++++++++++++++++ src/brain/client.ts | 152 +++++++++++++++++++++ src/brain/index.ts | 15 +++ src/index.ts | 12 +- start-backend.sh | 6 + 6 files changed, 482 insertions(+), 4 deletions(-) create mode 100644 src/api/brain-proxy.ts create mode 100644 src/brain/client.ts create mode 100755 start-backend.sh diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index 527bae7..fd4b85a 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -1,3 +1,5 @@ +require('dotenv').config(); + module.exports = { apps: [{ name: 'cloud-agents-backend', @@ -5,9 +7,10 @@ module.exports = { args: 'tsx src/index.ts', cwd: '/root/cloud-agents', env: { - PORT: 3000, + PORT: 3001, NODE_ENV: 'production', - SENTRY_DSN: 'https://66a26f4df181c1c92a9b4178fd8e4913@o4510621142024192.ingest.de.sentry.io/4510627168649296' + BRAIN_SERVER_URL: 'http://49.13.158.176:5001', + ...process.env } }] }; diff --git a/src/api/brain-proxy.ts b/src/api/brain-proxy.ts new file mode 100644 index 0000000..8237c08 --- /dev/null +++ b/src/api/brain-proxy.ts @@ -0,0 +1,294 @@ +/** + * Brain API Proxy + * + * Proxies all brain requests to the central Brain-Server (49.13.158.176:5001) + * Maps between Cloud-Agents brain API and The-Brain memory API + */ + +import { Router } from "express"; +import { requireAuth } from "../auth/middleware.js"; + +const BRAIN_SERVER_URL = process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; + +interface BrainProxyOptions { + fallbackToLocal?: boolean; +} + +/** + * Creates Brain API Proxy router + */ +export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router { + const router = Router(); + router.use(requireAuth); + + // Helper to proxy requests + async function proxyToBrain( + endpoint: string, + method: string, + userId: string, + body?: any + ): Promise { + const url = `${BRAIN_SERVER_URL}${endpoint}`; + const headers: Record = { + "Content-Type": "application/json", + "x-user-id": userId, + }; + + const response = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ error: response.statusText })); + throw new Error(error.error || "Brain server error"); + } + + return response.json(); + } + + // ===== INGEST ENDPOINTS ===== + // Maps to /api/memory/store with type="document" + + router.post("/ingest/text", async (req, res) => { + try { + const userId = (req as any).userId; + const { title, content, metadata } = req.body; + + if (!title || !content) { + return res.status(400).json({ success: false, error: "title and content required" }); + } + + const result = await proxyToBrain("/api/memory/store", "POST", userId, { + type: "document", + content: `# ${title}\n\n${content}`, + tags: metadata?.tags || ["ingested", "text"], + }); + + res.status(201).json({ + success: true, + doc: { + id: result.id, + userId, + title, + sourceType: "text", + status: "ready", + createdAt: new Date().toISOString(), + }, + }); + } catch (error: any) { + console.error("Brain proxy ingest/text failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + router.post("/ingest/url", async (req, res) => { + try { + const userId = (req as any).userId; + const { title, url, content, metadata } = req.body; + + if (!title || !url || !content) { + return res.status(400).json({ success: false, error: "title, url and content required" }); + } + + const result = await proxyToBrain("/api/memory/store", "POST", userId, { + type: "document", + content: `# ${title}\n\nSource: ${url}\n\n${content}`, + tags: metadata?.tags || ["ingested", "url"], + }); + + res.status(201).json({ + success: true, + doc: { + id: result.id, + userId, + title, + url, + sourceType: "url", + status: "ready", + createdAt: new Date().toISOString(), + }, + }); + } catch (error: any) { + console.error("Brain proxy ingest/url failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + router.post("/ingest/file", async (req, res) => { + try { + const userId = (req as any).userId; + const { title, filePath, fileName, fileType, content, metadata } = req.body; + + if (!title || !content) { + return res.status(400).json({ success: false, error: "title and content required" }); + } + + const result = await proxyToBrain("/api/memory/store", "POST", userId, { + type: "document", + content: `# ${title}\n\nFile: ${fileName || filePath}\nType: ${fileType}\n\n${content}`, + tags: metadata?.tags || ["ingested", "file", fileType || "unknown"], + }); + + res.status(201).json({ + success: true, + doc: { + id: result.id, + userId, + title, + filePath, + fileName, + fileType, + sourceType: "file", + status: "ready", + createdAt: new Date().toISOString(), + }, + }); + } catch (error: any) { + console.error("Brain proxy ingest/file failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // ===== SEARCH ENDPOINTS ===== + + router.post("/search", async (req, res) => { + try { + const userId = (req as any).userId; + const { query, limit = 10, mode = "keyword" } = req.body; + + if (!query) { + return res.status(400).json({ success: false, error: "query required" }); + } + + const result = await proxyToBrain("/api/memory/search", "POST", userId, { + query, + limit, + }); + + // Transform memory results to brain search results + const results = (result.results || []).map((r: any) => ({ + docId: r.id, + chunkId: r.id, + content: r.content, + score: 1.0, + title: r.content.split("\n")[0].replace(/^#\s*/, "") || "Untitled", + snippet: r.content.substring(0, 200), + })); + + res.json({ success: true, results, count: results.length, mode }); + } catch (error: any) { + console.error("Brain proxy search failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + router.get("/search", async (req, res) => { + try { + const userId = (req as any).userId; + const query = req.query.q as string; + const limit = parseInt(req.query.limit as string) || 10; + + if (!query) { + return res.status(400).json({ success: false, error: "q parameter required" }); + } + + const result = await proxyToBrain("/api/memory/search", "POST", userId, { + query, + limit, + }); + + const results = (result.results || []).map((r: any) => ({ + docId: r.id, + chunkId: r.id, + content: r.content, + score: 1.0, + title: r.content.split("\n")[0].replace(/^#\s*/, "") || "Untitled", + snippet: r.content.substring(0, 200), + })); + + res.json({ success: true, results, count: results.length, mode: "keyword" }); + } catch (error: any) { + console.error("Brain proxy search failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // ===== DOCUMENT MANAGEMENT ===== + + router.get("/docs", async (req, res) => { + try { + const userId = (req as any).userId; + const limit = parseInt(req.query.limit as string) || 50; + + const result = await proxyToBrain(`/api/memory/recent?limit=${limit}`, "GET", userId); + + // Transform memory entries to docs + const docs = (result.results || []).map((r: any) => ({ + id: r.id, + userId, + title: r.content.split("\n")[0].replace(/^#\s*/, "") || "Untitled", + sourceType: r.type === "document" ? "text" : r.type, + status: "ready", + createdAt: r.createdAt, + })); + + res.json({ success: true, docs, count: docs.length }); + } catch (error: any) { + console.error("Brain proxy docs list failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // ===== STATS ===== + + router.get("/stats", async (req, res) => { + try { + const userId = (req as any).userId; + + // Get recent documents to calculate stats + const result = await proxyToBrain("/api/memory/recent?limit=50", "GET", userId); + const docs = result.results || []; + + res.json({ + success: true, + enabled: true, + proxyMode: true, + brainServer: BRAIN_SERVER_URL, + stats: { + totalDocs: docs.length, + totalChunks: docs.length, // Each memory entry is one "chunk" + searchCount: 0, + lastSearchAt: null, + }, + }); + } catch (error: any) { + console.error("Brain proxy stats failed:", error.message); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // Health check for proxy + router.get("/proxy/health", async (_req, res) => { + try { + const response = await fetch(`${BRAIN_SERVER_URL}/health`); + const health = await response.json(); + res.json({ + success: true, + proxyMode: true, + brainServer: BRAIN_SERVER_URL, + brainHealth: health, + }); + } catch (error: any) { + res.status(503).json({ + success: false, + proxyMode: true, + brainServer: BRAIN_SERVER_URL, + error: error.message, + }); + } + }); + + return router; +} diff --git a/src/brain/client.ts b/src/brain/client.ts new file mode 100644 index 0000000..091916c --- /dev/null +++ b/src/brain/client.ts @@ -0,0 +1,152 @@ +/** + * BrainClient - HTTP Client for Brain Server + * + * Calls the centralized Brain Server at 49.13.158.176:5001 + * instead of using local database operations. + */ + +const BRAIN_SERVER_URL = process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; + +export interface MemoryEntry { + id: string; + type: string; + content: string; + tags: string[]; + createdAt: string; +} + +export interface StoreOptions { + userId?: string; + projectId?: string; + type: string; + content: string; + tags?: string[]; +} + +export interface SearchOptions { + userId?: string; + projectId?: string; + query: string; + limit?: number; +} + +export class BrainClient { + private baseUrl: string; + private defaultHeaders: Record; + + constructor(options?: { baseUrl?: string; userId?: string; projectId?: string }) { + this.baseUrl = options?.baseUrl || BRAIN_SERVER_URL; + this.defaultHeaders = { + "Content-Type": "application/json", + }; + if (options?.userId) { + this.defaultHeaders["x-user-id"] = options.userId; + } + if (options?.projectId) { + this.defaultHeaders["x-project-id"] = options.projectId; + } + } + + async health(): Promise<{ status: string; service: string; version: string }> { + const res = await fetch(`${this.baseUrl}/health`); + if (!res.ok) throw new Error(`Brain health check failed: ${res.status}`); + return res.json(); + } + + async storeMemory(options: StoreOptions): Promise<{ id: string }> { + const headers = { ...this.defaultHeaders }; + if (options.userId) headers["x-user-id"] = options.userId; + if (options.projectId) headers["x-project-id"] = options.projectId; + + const res = await fetch(`${this.baseUrl}/api/memory/store`, { + method: "POST", + headers, + body: JSON.stringify({ + type: options.type, + content: options.content, + tags: options.tags || [], + }), + }); + + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || "Failed to store memory"); + } + return res.json(); + } + + async searchMemory(options: SearchOptions): Promise<{ results: MemoryEntry[] }> { + const headers = { ...this.defaultHeaders }; + if (options.userId) headers["x-user-id"] = options.userId; + if (options.projectId) headers["x-project-id"] = options.projectId; + + const res = await fetch(`${this.baseUrl}/api/memory/search`, { + method: "POST", + headers, + body: JSON.stringify({ + query: options.query, + limit: options.limit || 10, + }), + }); + + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || "Failed to search memory"); + } + return res.json(); + } + + async recentMemory(options?: { userId?: string; projectId?: string; limit?: number }): Promise<{ results: MemoryEntry[] }> { + const headers = { ...this.defaultHeaders }; + if (options?.userId) headers["x-user-id"] = options.userId; + if (options?.projectId) headers["x-project-id"] = options.projectId; + + const limit = options?.limit || 10; + const res = await fetch(`${this.baseUrl}/api/memory/recent?limit=${limit}`, { + headers, + }); + + if (!res.ok) { + const err = await res.json().catch(() => ({ error: res.statusText })); + throw new Error(err.error || "Failed to get recent memory"); + } + return res.json(); + } + + async listProjects(): Promise<{ projects: Array<{ id: string; name: string }> }> { + const res = await fetch(`${this.baseUrl}/api/projects`, { + headers: this.defaultHeaders, + }); + if (!res.ok) throw new Error("Failed to list projects"); + return res.json(); + } + + async logAudit(options: { userId?: string; action: string; details?: Record }): Promise<{ id: string }> { + const headers = { ...this.defaultHeaders }; + if (options.userId) headers["x-user-id"] = options.userId; + + const res = await fetch(`${this.baseUrl}/api/audit/log`, { + method: "POST", + headers, + body: JSON.stringify({ + action: options.action, + details: options.details || {}, + }), + }); + + if (!res.ok) throw new Error("Failed to log audit event"); + return res.json(); + } +} + +// Singleton instance +let brainClient: BrainClient | null = null; + +export function getBrainClient(options?: { userId?: string; projectId?: string }): BrainClient { + if (!brainClient) { + brainClient = new BrainClient(options); + } + return brainClient; +} + +export default BrainClient; diff --git a/src/brain/index.ts b/src/brain/index.ts index 269e8df..d7c9aca 100644 --- a/src/brain/index.ts +++ b/src/brain/index.ts @@ -15,6 +15,21 @@ export type { export { BrainSearch } from "./search.js"; export type { BrainSearchResult, KeywordSearchResult } from "./search.js"; +// Core Brain Client (central knowledge base) +export { + coreBrainSearch, + coreBrainStore, + coreBrainRecent, + buildCoreBrainContext, + coreBrainHealthCheck, +} from "./core-brain.js"; +export type { + CoreBrainMemory, + CoreBrainSearchResult, + CoreBrainStoreParams, + CoreBrainSearchParams, +} from "./core-brain.js"; + // Re-export types from DB schema export type { BrainDoc, diff --git a/src/index.ts b/src/index.ts index d324244..5cb1a65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,6 +45,7 @@ import { createBillingRouter } from "./api/billing.js"; import { createModulesRouter } from "./api/modules.js"; import { createChatRouter } from "./api/chat.js"; import { createBrainRouter } from "./api/brain.js"; +import { createBrainProxyRouter } from "./api/brain-proxy.js"; import { handleSlackEvents } from "./api/slack-events.js"; import { ChatStorage } from "./chat/storage.js"; import { ChatManager } from "./chat/manager.js"; @@ -65,7 +66,7 @@ import { initEventRecorder, events } from "./lib/events.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const PORT = process.env.PORT ?? 3000; +const PORT = process.env.PORT || 3001; async function main() { console.log("🚀 Starting Code Cloud Agents..."); @@ -202,7 +203,14 @@ async function main() { app.use("/api/billing", createBillingRouter()); app.use("/api/modules", createModulesRouter()); app.use("/api/chat", createChatRouter(chatManager)); - app.use("/api/brain", createBrainRouter(db)); + // Use proxy if BRAIN_SERVER_URL is set, otherwise local + if (process.env.BRAIN_SERVER_URL) { + console.log("🧠 Brain: Proxy mode -> " + process.env.BRAIN_SERVER_URL); + app.use("/api/brain", createBrainProxyRouter()); + } else { + console.log("🧠 Brain: Local mode"); + app.use("/api/brain", createBrainRouter(db)); + } app.use("/api/agent-tasks", createAgentTasksRouter()); app.use("/api/ops", createOpsRouter(db)); diff --git a/start-backend.sh b/start-backend.sh new file mode 100755 index 0000000..86d1737 --- /dev/null +++ b/start-backend.sh @@ -0,0 +1,6 @@ +#!/bin/bash +cd /root/cloud-agents +source .env +export PORT=3001 +export BRAIN_SERVER_URL=http://49.13.158.176:5001 +exec npx tsx src/index.ts From 5ffac741c3bcdc154822897002f09cbda0f437c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Jan 2026 03:02:17 +0000 Subject: [PATCH 2/2] style: auto-fix linting and formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Auto-fixed by GitHub Actions --- AGENT_0_DEPENDENCY_FIX_REPORT_2025-12-26.md | 1 + src/api/brain-proxy.ts | 57 ++++++++++++++++----- src/brain/client.ts | 51 +++++++++++++----- src/integrations/linear/client.ts | 12 ++--- src/integrations/slack/client.ts | 8 +-- 5 files changed, 88 insertions(+), 41 deletions(-) diff --git a/AGENT_0_DEPENDENCY_FIX_REPORT_2025-12-26.md b/AGENT_0_DEPENDENCY_FIX_REPORT_2025-12-26.md index 6871995..580b550 100644 --- a/AGENT_0_DEPENDENCY_FIX_REPORT_2025-12-26.md +++ b/AGENT_0_DEPENDENCY_FIX_REPORT_2025-12-26.md @@ -907,6 +907,7 @@ b5d5aca - docs(agent-0): Add comprehensive code review report - Zeit: ~8-10h 6. **Production Deployment Verification** + ```bash ssh root@178.156.178.70 cd /root/cloud-agents diff --git a/src/api/brain-proxy.ts b/src/api/brain-proxy.ts index 8237c08..f88b26b 100644 --- a/src/api/brain-proxy.ts +++ b/src/api/brain-proxy.ts @@ -1,6 +1,6 @@ /** * Brain API Proxy - * + * * Proxies all brain requests to the central Brain-Server (49.13.158.176:5001) * Maps between Cloud-Agents brain API and The-Brain memory API */ @@ -8,7 +8,8 @@ import { Router } from "express"; import { requireAuth } from "../auth/middleware.js"; -const BRAIN_SERVER_URL = process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; +const BRAIN_SERVER_URL = + process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; interface BrainProxyOptions { fallbackToLocal?: boolean; @@ -17,7 +18,9 @@ interface BrainProxyOptions { /** * Creates Brain API Proxy router */ -export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router { +export function createBrainProxyRouter( + options: BrainProxyOptions = {}, +): Router { const router = Router(); router.use(requireAuth); @@ -26,7 +29,7 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router endpoint: string, method: string, userId: string, - body?: any + body?: any, ): Promise { const url = `${BRAIN_SERVER_URL}${endpoint}`; const headers: Record = { @@ -41,7 +44,9 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router }); if (!response.ok) { - const error = await response.json().catch(() => ({ error: response.statusText })); + const error = await response + .json() + .catch(() => ({ error: response.statusText })); throw new Error(error.error || "Brain server error"); } @@ -57,7 +62,9 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const { title, content, metadata } = req.body; if (!title || !content) { - return res.status(400).json({ success: false, error: "title and content required" }); + return res + .status(400) + .json({ success: false, error: "title and content required" }); } const result = await proxyToBrain("/api/memory/store", "POST", userId, { @@ -89,7 +96,9 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const { title, url, content, metadata } = req.body; if (!title || !url || !content) { - return res.status(400).json({ success: false, error: "title, url and content required" }); + return res + .status(400) + .json({ success: false, error: "title, url and content required" }); } const result = await proxyToBrain("/api/memory/store", "POST", userId, { @@ -119,10 +128,13 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router router.post("/ingest/file", async (req, res) => { try { const userId = (req as any).userId; - const { title, filePath, fileName, fileType, content, metadata } = req.body; + const { title, filePath, fileName, fileType, content, metadata } = + req.body; if (!title || !content) { - return res.status(400).json({ success: false, error: "title and content required" }); + return res + .status(400) + .json({ success: false, error: "title and content required" }); } const result = await proxyToBrain("/api/memory/store", "POST", userId, { @@ -159,7 +171,9 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const { query, limit = 10, mode = "keyword" } = req.body; if (!query) { - return res.status(400).json({ success: false, error: "query required" }); + return res + .status(400) + .json({ success: false, error: "query required" }); } const result = await proxyToBrain("/api/memory/search", "POST", userId, { @@ -191,7 +205,9 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const limit = parseInt(req.query.limit as string) || 10; if (!query) { - return res.status(400).json({ success: false, error: "q parameter required" }); + return res + .status(400) + .json({ success: false, error: "q parameter required" }); } const result = await proxyToBrain("/api/memory/search", "POST", userId, { @@ -208,7 +224,12 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router snippet: r.content.substring(0, 200), })); - res.json({ success: true, results, count: results.length, mode: "keyword" }); + res.json({ + success: true, + results, + count: results.length, + mode: "keyword", + }); } catch (error: any) { console.error("Brain proxy search failed:", error.message); res.status(500).json({ success: false, error: error.message }); @@ -222,7 +243,11 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const userId = (req as any).userId; const limit = parseInt(req.query.limit as string) || 50; - const result = await proxyToBrain(`/api/memory/recent?limit=${limit}`, "GET", userId); + const result = await proxyToBrain( + `/api/memory/recent?limit=${limit}`, + "GET", + userId, + ); // Transform memory entries to docs const docs = (result.results || []).map((r: any) => ({ @@ -248,7 +273,11 @@ export function createBrainProxyRouter(options: BrainProxyOptions = {}): Router const userId = (req as any).userId; // Get recent documents to calculate stats - const result = await proxyToBrain("/api/memory/recent?limit=50", "GET", userId); + const result = await proxyToBrain( + "/api/memory/recent?limit=50", + "GET", + userId, + ); const docs = result.results || []; res.json({ diff --git a/src/brain/client.ts b/src/brain/client.ts index 091916c..ba53aa3 100644 --- a/src/brain/client.ts +++ b/src/brain/client.ts @@ -1,11 +1,12 @@ /** * BrainClient - HTTP Client for Brain Server - * + * * Calls the centralized Brain Server at 49.13.158.176:5001 * instead of using local database operations. */ -const BRAIN_SERVER_URL = process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; +const BRAIN_SERVER_URL = + process.env.BRAIN_SERVER_URL || "http://49.13.158.176:5001"; export interface MemoryEntry { id: string; @@ -34,7 +35,11 @@ export class BrainClient { private baseUrl: string; private defaultHeaders: Record; - constructor(options?: { baseUrl?: string; userId?: string; projectId?: string }) { + constructor(options?: { + baseUrl?: string; + userId?: string; + projectId?: string; + }) { this.baseUrl = options?.baseUrl || BRAIN_SERVER_URL; this.defaultHeaders = { "Content-Type": "application/json", @@ -47,7 +52,11 @@ export class BrainClient { } } - async health(): Promise<{ status: string; service: string; version: string }> { + async health(): Promise<{ + status: string; + service: string; + version: string; + }> { const res = await fetch(`${this.baseUrl}/health`); if (!res.ok) throw new Error(`Brain health check failed: ${res.status}`); return res.json(); @@ -75,7 +84,9 @@ export class BrainClient { return res.json(); } - async searchMemory(options: SearchOptions): Promise<{ results: MemoryEntry[] }> { + async searchMemory( + options: SearchOptions, + ): Promise<{ results: MemoryEntry[] }> { const headers = { ...this.defaultHeaders }; if (options.userId) headers["x-user-id"] = options.userId; if (options.projectId) headers["x-project-id"] = options.projectId; @@ -96,15 +107,22 @@ export class BrainClient { return res.json(); } - async recentMemory(options?: { userId?: string; projectId?: string; limit?: number }): Promise<{ results: MemoryEntry[] }> { + async recentMemory(options?: { + userId?: string; + projectId?: string; + limit?: number; + }): Promise<{ results: MemoryEntry[] }> { const headers = { ...this.defaultHeaders }; if (options?.userId) headers["x-user-id"] = options.userId; if (options?.projectId) headers["x-project-id"] = options.projectId; const limit = options?.limit || 10; - const res = await fetch(`${this.baseUrl}/api/memory/recent?limit=${limit}`, { - headers, - }); + const res = await fetch( + `${this.baseUrl}/api/memory/recent?limit=${limit}`, + { + headers, + }, + ); if (!res.ok) { const err = await res.json().catch(() => ({ error: res.statusText })); @@ -113,7 +131,9 @@ export class BrainClient { return res.json(); } - async listProjects(): Promise<{ projects: Array<{ id: string; name: string }> }> { + async listProjects(): Promise<{ + projects: Array<{ id: string; name: string }>; + }> { const res = await fetch(`${this.baseUrl}/api/projects`, { headers: this.defaultHeaders, }); @@ -121,7 +141,11 @@ export class BrainClient { return res.json(); } - async logAudit(options: { userId?: string; action: string; details?: Record }): Promise<{ id: string }> { + async logAudit(options: { + userId?: string; + action: string; + details?: Record; + }): Promise<{ id: string }> { const headers = { ...this.defaultHeaders }; if (options.userId) headers["x-user-id"] = options.userId; @@ -142,7 +166,10 @@ export class BrainClient { // Singleton instance let brainClient: BrainClient | null = null; -export function getBrainClient(options?: { userId?: string; projectId?: string }): BrainClient { +export function getBrainClient(options?: { + userId?: string; + projectId?: string; +}): BrainClient { if (!brainClient) { brainClient = new BrainClient(options); } diff --git a/src/integrations/linear/client.ts b/src/integrations/linear/client.ts index d30f989..e394ff7 100644 --- a/src/integrations/linear/client.ts +++ b/src/integrations/linear/client.ts @@ -54,9 +54,7 @@ export interface LinearClient { teams?: LinearTeam[]; error?: string; }>; - listWorkflowStates( - teamId: string, - ): Promise<{ + listWorkflowStates(teamId: string): Promise<{ success: boolean; states?: LinearWorkflowState[]; error?: string; @@ -100,9 +98,7 @@ export function createLinearClient(config?: LinearConfig): LinearClient { * @param issue - Issue details * @returns Promise with created issue details */ - async createIssue( - issue: LinearIssue, - ): Promise<{ + async createIssue(issue: LinearIssue): Promise<{ success: boolean; issue?: LinearIssueResult; error?: string; @@ -205,9 +201,7 @@ export function createLinearClient(config?: LinearConfig): LinearClient { * @param teamId - Team ID * @returns Promise with workflow states */ - async listWorkflowStates( - teamId: string, - ): Promise<{ + async listWorkflowStates(teamId: string): Promise<{ success: boolean; states?: LinearWorkflowState[]; error?: string; diff --git a/src/integrations/slack/client.ts b/src/integrations/slack/client.ts index 532c62e..2d16345 100644 --- a/src/integrations/slack/client.ts +++ b/src/integrations/slack/client.ts @@ -32,9 +32,7 @@ export interface SlackChannel { export interface SlackClient { isEnabled(): boolean; - sendMessage( - message: SlackMessage, - ): Promise<{ + sendMessage(message: SlackMessage): Promise<{ success: boolean; message?: SlackMessageResult; error?: string; @@ -82,9 +80,7 @@ export function createSlackClient(config?: SlackConfig): SlackClient { * @param message - Message details including channel and text * @returns Promise with message result */ - async sendMessage( - message: SlackMessage, - ): Promise<{ + async sendMessage(message: SlackMessage): Promise<{ success: boolean; message?: SlackMessageResult; error?: string;