First Release of Claw3D (#11)
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { resolveStateDir } from "@/lib/clawdbot/paths";
|
||||
import type { StandupMeeting, StandupMeetingStore } from "@/lib/office/standup/types";
|
||||
|
||||
const STORE_DIR = "claw3d";
|
||||
const STORE_FILE = "standup-store.json";
|
||||
|
||||
const ensureDirectory = (dirPath: string) => {
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
};
|
||||
|
||||
const resolveStorePath = () => {
|
||||
const dir = path.join(resolveStateDir(), STORE_DIR);
|
||||
ensureDirectory(dir);
|
||||
return path.join(dir, STORE_FILE);
|
||||
};
|
||||
|
||||
const defaultStore = (): StandupMeetingStore => ({
|
||||
activeMeeting: null,
|
||||
lastMeeting: null,
|
||||
});
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
Boolean(value && typeof value === "object" && !Array.isArray(value));
|
||||
|
||||
const normalizeMeeting = (value: unknown): StandupMeeting | null => {
|
||||
if (!isRecord(value)) return null;
|
||||
if (typeof value.id !== "string") return null;
|
||||
if (!Array.isArray(value.cards) || !Array.isArray(value.participantOrder)) return null;
|
||||
if (!Array.isArray(value.arrivedAgentIds)) return null;
|
||||
if (typeof value.startedAt !== "string" || typeof value.updatedAt !== "string") return null;
|
||||
return value as StandupMeeting;
|
||||
};
|
||||
|
||||
const readStore = (): StandupMeetingStore => {
|
||||
const storePath = resolveStorePath();
|
||||
if (!fs.existsSync(storePath)) {
|
||||
return defaultStore();
|
||||
}
|
||||
const raw = fs.readFileSync(storePath, "utf8");
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (!isRecord(parsed)) return defaultStore();
|
||||
return {
|
||||
activeMeeting: normalizeMeeting(parsed.activeMeeting),
|
||||
lastMeeting: normalizeMeeting(parsed.lastMeeting),
|
||||
};
|
||||
};
|
||||
|
||||
const writeStore = (store: StandupMeetingStore) => {
|
||||
const storePath = resolveStorePath();
|
||||
fs.writeFileSync(storePath, JSON.stringify(store, null, 2), "utf8");
|
||||
};
|
||||
|
||||
export const loadStandupMeetingStore = (): StandupMeetingStore => readStore();
|
||||
|
||||
export const loadActiveStandupMeeting = (): StandupMeeting | null => readStore().activeMeeting;
|
||||
|
||||
export const saveStandupMeeting = (meeting: StandupMeeting | null): StandupMeetingStore => {
|
||||
const current = readStore();
|
||||
const next: StandupMeetingStore = {
|
||||
activeMeeting: meeting,
|
||||
lastMeeting: meeting ?? current.lastMeeting,
|
||||
};
|
||||
if (meeting?.phase === "complete") {
|
||||
next.lastMeeting = meeting;
|
||||
}
|
||||
writeStore(next);
|
||||
return next;
|
||||
};
|
||||
|
||||
export const updateStandupMeeting = (
|
||||
updater: (meeting: StandupMeeting | null) => StandupMeeting | null
|
||||
): StandupMeetingStore => {
|
||||
const current = readStore();
|
||||
const nextMeeting = updater(current.activeMeeting);
|
||||
const nextStore: StandupMeetingStore = {
|
||||
activeMeeting: nextMeeting,
|
||||
lastMeeting:
|
||||
nextMeeting?.phase === "complete"
|
||||
? nextMeeting
|
||||
: current.lastMeeting,
|
||||
};
|
||||
writeStore(nextStore);
|
||||
return nextStore;
|
||||
};
|
||||
Reference in New Issue
Block a user