refactor: make root path for configurations

This commit is contained in:
2026-06-25 22:52:02 +09:00
parent e0189fb002
commit a4d5347e99
4 changed files with 77 additions and 34 deletions

View File

@@ -1,5 +1,5 @@
SUPERMEMORY_API_KEY= SUPERMEMORY_API_KEY=
BRAINDB_PATH=./braindb.json BRAINBOX_ROOT_PATH=./brainbox-data
OPENROUTER_API_KEY= OPENROUTER_API_KEY=

3
.gitignore vendored
View File

@@ -27,6 +27,9 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
.cache .cache
*.tsbuildinfo *.tsbuildinfo
# brainbox data
brainbox-data/
# IntelliJ based IDEs # IntelliJ based IDEs
.idea .idea

View File

@@ -1,5 +1,6 @@
import { config } from "@/config"; 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 { export interface BrainItem {
brainId: string; brainId: string;
@@ -7,51 +8,90 @@ export interface BrainItem {
displayName: string; displayName: string;
baseSystemPrompt: string; baseSystemPrompt: string;
} }
export type BrainDB = Record<string, BrainItem>; export type BrainList = BrainItem[];
// Layout:
// <root>/brains.json — BrainItem[] index, mirror
// <root>/<brainId>/brain.json — BrainItem per brain, source of truth
export class BrainDBManager { export class BrainDBManager {
constructor(private readonly braindbPath: string = config.braindbPath) {} constructor(private readonly root: string = config.brainboxRoot) {}
private get db() { private brainDir(brainId: string): string {
return readFile(this.braindbPath, { encoding: "utf-8" }).then( return join(this.root, brainId);
(content) => { }
return JSON.parse(content) as BrainDB;
}, 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<BrainList> {
try {
const content = await readFile(this.indexFile(), { encoding: "utf-8" });
return JSON.parse(content) as BrainList;
} catch {
return [];
}
}
private async writeIndex(list: BrainList): Promise<void> {
await mkdir(this.root, { recursive: true });
await writeFile(
this.indexFile(),
JSON.stringify(list, null, 2),
{ encoding: "utf-8" },
); );
} }
private async writeDb(db: BrainDB) { private async writeBrain(brain: BrainItem): Promise<void> {
await writeFile(this.braindbPath, JSON.stringify(db), { await mkdir(this.brainDir(brain.brainId), { recursive: true });
encoding: "utf-8", await writeFile(
}); this.brainFile(brain.brainId),
JSON.stringify(brain, null, 2),
{ encoding: "utf-8" },
);
} }
async loadBrain(brainId: string): Promise<BrainItem | undefined> { async loadBrain(brainId: string): Promise<BrainItem | undefined> {
const brainOrNot = (await this.db)[brainId]; try {
return brainOrNot; 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) { async saveBrain(brainId: string, brain: BrainItem): Promise<void> {
const db = await this.db; await this.writeBrain(brain);
db[brainId] = brain; const list = await this.readIndex();
await this.writeDb(db); const idx = list.findIndex((b) => b.brainId === brainId);
if (idx >= 0) list[idx] = brain;
else list.push(brain);
await this.writeIndex(list);
} }
async listBrain() { async listBrain(): Promise<Array<{ brainId: string; displayName: string }>> {
return Object.entries(await this.db).map( const list = await this.readIndex();
([_, { brainId, displayName }]) => ({ brainId, displayName }), return list.map(({ brainId, displayName }) => ({ brainId, displayName }));
);
} }
async deleteBrain(brainId: string) { async deleteBrain(brainId: string): Promise<void> {
const db = await this.db; await rm(this.brainDir(brainId), { recursive: true, force: true });
delete db[brainId]; const list = await this.readIndex();
await this.writeDb(db); const filtered = list.filter((b) => b.brainId !== brainId);
if (filtered.length === list.length) return;
await this.writeIndex(filtered);
} }
async isBrainAvailable(brainId: string) { async isBrainAvailable(brainId: string): Promise<boolean> {
return brainId in (await this.db); return (await this.loadBrain(brainId)) !== undefined;
} }
} }
export const brainManager = new BrainDBManager(); export const brainManager = new BrainDBManager();

View File

@@ -4,7 +4,7 @@ import { join } from "path";
export interface Config { export interface Config {
openrouterApiKey: string; openrouterApiKey: string;
supermemoryApiKey: string; supermemoryApiKey: string;
braindbPath: string; brainboxRoot: string;
} }
const openrouterApiKey = process.env["OPENROUTER_API_KEY"]; 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"]; const supermemoryApiKey = process.env["SUPERMEMORY_API_KEY"];
if (!supermemoryApiKey) throw new Error("SUPERMEMORY_API_KEY is missing"); if (!supermemoryApiKey) throw new Error("SUPERMEMORY_API_KEY is missing");
const braindbPath = join( const brainboxRoot = join(
process.cwd(), process.cwd(),
process.env["BRAINDB_PATH"] ?? "brainbox.json", process.env["BRAINBOX_ROOT_PATH"] ?? "brainbox-data",
); );
export const config: Config = { export const config: Config = {
openrouterApiKey, openrouterApiKey,
supermemoryApiKey, supermemoryApiKey,
braindbPath, brainboxRoot,
}; };