feat: ship usable local v1 with demo, workers, approvals, and docker support

This commit is contained in:
Codex
2026-03-23 12:59:41 +08:00
parent 0ab83990b2
commit 515ce72d0d
14 changed files with 1015 additions and 215 deletions

View File

@@ -1,4 +1,4 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
import { dirname } from "node:path";
import type { AppState } from "./types.js";
@@ -15,8 +15,12 @@ function defaultState(): AppState {
export class FileStore {
private state: AppState;
private readonly backupFilePath: string;
private readonly tempFilePath: string;
constructor(private readonly filePath: string) {
this.backupFilePath = `${filePath}.bak`;
this.tempFilePath = `${filePath}.tmp`;
this.ensureDirectory();
this.state = this.load();
}
@@ -42,20 +46,28 @@ export class FileStore {
}
private load(): AppState {
if (!existsSync(this.filePath)) {
return defaultState();
for (const path of [this.filePath, this.backupFilePath]) {
if (!existsSync(path)) {
continue;
}
try {
const raw = readFileSync(path, "utf8");
return { ...defaultState(), ...(JSON.parse(raw) as AppState) };
} catch {
// try backup/default
}
}
try {
const raw = readFileSync(this.filePath, "utf8");
return { ...defaultState(), ...(JSON.parse(raw) as AppState) };
} catch {
return defaultState();
}
return defaultState();
}
private save(): void {
writeFileSync(this.filePath, `${JSON.stringify(this.state, null, 2)}\n`, "utf8");
const content = `${JSON.stringify(this.state, null, 2)}\n`;
writeFileSync(this.tempFilePath, content, "utf8");
if (existsSync(this.filePath)) {
copyFileSync(this.filePath, this.backupFilePath);
}
renameSync(this.tempFilePath, this.filePath);
}
}