From a4d5347e99579d65b47ef7cc0d798cc741a00944 Mon Sep 17 00:00:00 2001 From: p-sw Date: Thu, 25 Jun 2026 22:52:02 +0900 Subject: [PATCH] refactor: make root path for configurations --- .env.example | 2 +- .gitignore | 3 ++ src/brain/manager.ts | 98 +++++++++++++++++++++++++++++++------------- src/config.ts | 8 ++-- 4 files changed, 77 insertions(+), 34 deletions(-) diff --git a/.env.example b/.env.example index aef76ab..4f4190a 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ SUPERMEMORY_API_KEY= -BRAINDB_PATH=./braindb.json +BRAINBOX_ROOT_PATH=./brainbox-data OPENROUTER_API_KEY= diff --git a/.gitignore b/.gitignore index d21fc01..51b90b4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,9 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .cache *.tsbuildinfo +# brainbox data +brainbox-data/ + # IntelliJ based IDEs .idea diff --git a/src/brain/manager.ts b/src/brain/manager.ts index 1a0c935..4988df5 100644 --- a/src/brain/manager.ts +++ b/src/brain/manager.ts @@ -1,5 +1,6 @@ import { config } from "@/config"; -import { readFile, writeFile } from "fs/promises"; +import { mkdir, readFile, rm, writeFile } from "fs/promises"; +import { join } from "path"; export interface BrainItem { brainId: string; @@ -7,51 +8,90 @@ export interface BrainItem { displayName: string; baseSystemPrompt: string; } -export type BrainDB = Record; +export type BrainList = BrainItem[]; + +// Layout: +// /brains.json — BrainItem[] index, mirror +// //brain.json — BrainItem per brain, source of truth export class BrainDBManager { - constructor(private readonly braindbPath: string = config.braindbPath) {} + constructor(private readonly root: string = config.brainboxRoot) {} - private get db() { - return readFile(this.braindbPath, { encoding: "utf-8" }).then( - (content) => { - return JSON.parse(content) as BrainDB; - }, + private brainDir(brainId: string): string { + return join(this.root, brainId); + } + + private brainFile(brainId: string): string { + return join(this.brainDir(brainId), "brain.json"); + } + + private indexFile(): string { + return join(this.root, "brains.json"); + } + + private async readIndex(): Promise { + try { + const content = await readFile(this.indexFile(), { encoding: "utf-8" }); + return JSON.parse(content) as BrainList; + } catch { + return []; + } + } + + private async writeIndex(list: BrainList): Promise { + await mkdir(this.root, { recursive: true }); + await writeFile( + this.indexFile(), + JSON.stringify(list, null, 2), + { encoding: "utf-8" }, ); } - private async writeDb(db: BrainDB) { - await writeFile(this.braindbPath, JSON.stringify(db), { - encoding: "utf-8", - }); + private async writeBrain(brain: BrainItem): Promise { + await mkdir(this.brainDir(brain.brainId), { recursive: true }); + await writeFile( + this.brainFile(brain.brainId), + JSON.stringify(brain, null, 2), + { encoding: "utf-8" }, + ); } async loadBrain(brainId: string): Promise { - const brainOrNot = (await this.db)[brainId]; - return brainOrNot; + try { + const content = await readFile(this.brainFile(brainId), { + encoding: "utf-8", + }); + return JSON.parse(content) as BrainItem; + } catch { + return undefined; + } } - async saveBrain(brainId: string, brain: BrainItem) { - const db = await this.db; - db[brainId] = brain; - await this.writeDb(db); + async saveBrain(brainId: string, brain: BrainItem): Promise { + await this.writeBrain(brain); + const list = await this.readIndex(); + const idx = list.findIndex((b) => b.brainId === brainId); + if (idx >= 0) list[idx] = brain; + else list.push(brain); + await this.writeIndex(list); } - async listBrain() { - return Object.entries(await this.db).map( - ([_, { brainId, displayName }]) => ({ brainId, displayName }), - ); + async listBrain(): Promise> { + const list = await this.readIndex(); + return list.map(({ brainId, displayName }) => ({ brainId, displayName })); } - async deleteBrain(brainId: string) { - const db = await this.db; - delete db[brainId]; - await this.writeDb(db); + async deleteBrain(brainId: string): Promise { + await rm(this.brainDir(brainId), { recursive: true, force: true }); + const list = await this.readIndex(); + const filtered = list.filter((b) => b.brainId !== brainId); + if (filtered.length === list.length) return; + await this.writeIndex(filtered); } - async isBrainAvailable(brainId: string) { - return brainId in (await this.db); + async isBrainAvailable(brainId: string): Promise { + return (await this.loadBrain(brainId)) !== undefined; } } -export const brainManager = new BrainDBManager(); +export const brainManager = new BrainDBManager(); \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 02368da..6dcc8c2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,7 +4,7 @@ import { join } from "path"; export interface Config { openrouterApiKey: string; supermemoryApiKey: string; - braindbPath: string; + brainboxRoot: string; } const openrouterApiKey = process.env["OPENROUTER_API_KEY"]; @@ -13,13 +13,13 @@ if (!openrouterApiKey) throw new Error("OPENROUTER_API_KEY is missing"); const supermemoryApiKey = process.env["SUPERMEMORY_API_KEY"]; if (!supermemoryApiKey) throw new Error("SUPERMEMORY_API_KEY is missing"); -const braindbPath = join( +const brainboxRoot = join( process.cwd(), - process.env["BRAINDB_PATH"] ?? "brainbox.json", + process.env["BRAINBOX_ROOT_PATH"] ?? "brainbox-data", ); export const config: Config = { openrouterApiKey, supermemoryApiKey, - braindbPath, + brainboxRoot, };