- Copy complete source code packages from original CodeNomad project - Add root package.json with npm workspace configuration - Include electron-app, server, ui, tauri-app, and opencode-config packages - Fix Launch-Windows.bat and Launch-Dev-Windows.bat to work with correct npm scripts - Fix Launch-Unix.sh to work with correct npm scripts - Launchers now correctly call npm run dev:electron which launches Electron app
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import fs from "fs"
|
|
import path from "path"
|
|
import { EventBus } from "../events/bus"
|
|
import { Logger } from "../logger"
|
|
import { ConfigFile, ConfigFileSchema, DEFAULT_CONFIG } from "./schema"
|
|
|
|
export class ConfigStore {
|
|
private cache: ConfigFile = DEFAULT_CONFIG
|
|
private loaded = false
|
|
|
|
constructor(
|
|
private readonly configPath: string,
|
|
private readonly eventBus: EventBus | undefined,
|
|
private readonly logger: Logger,
|
|
) {}
|
|
|
|
load(): ConfigFile {
|
|
if (this.loaded) {
|
|
return this.cache
|
|
}
|
|
|
|
try {
|
|
const resolved = this.resolvePath(this.configPath)
|
|
if (fs.existsSync(resolved)) {
|
|
const content = fs.readFileSync(resolved, "utf-8")
|
|
const parsed = JSON.parse(content)
|
|
this.cache = ConfigFileSchema.parse(parsed)
|
|
this.logger.debug({ resolved }, "Loaded existing config file")
|
|
} else {
|
|
this.cache = DEFAULT_CONFIG
|
|
this.logger.debug({ resolved }, "No config file found, using defaults")
|
|
}
|
|
} catch (error) {
|
|
this.logger.warn({ err: error }, "Failed to load config, using defaults")
|
|
this.cache = DEFAULT_CONFIG
|
|
}
|
|
|
|
this.loaded = true
|
|
return this.cache
|
|
}
|
|
|
|
get(): ConfigFile {
|
|
return this.load()
|
|
}
|
|
|
|
replace(config: ConfigFile) {
|
|
const validated = ConfigFileSchema.parse(config)
|
|
this.commit(validated)
|
|
}
|
|
|
|
private commit(next: ConfigFile) {
|
|
this.cache = next
|
|
this.loaded = true
|
|
this.persist()
|
|
const published = Boolean(this.eventBus)
|
|
this.eventBus?.publish({ type: "config.appChanged", config: this.cache })
|
|
this.logger.debug({ broadcast: published }, "Config SSE event emitted")
|
|
this.logger.trace({ config: this.cache }, "Config payload")
|
|
}
|
|
|
|
private persist() {
|
|
try {
|
|
const resolved = this.resolvePath(this.configPath)
|
|
fs.mkdirSync(path.dirname(resolved), { recursive: true })
|
|
fs.writeFileSync(resolved, JSON.stringify(this.cache, null, 2), "utf-8")
|
|
this.logger.debug({ resolved }, "Persisted config file")
|
|
} catch (error) {
|
|
this.logger.warn({ err: error }, "Failed to persist config")
|
|
}
|
|
}
|
|
|
|
private resolvePath(filePath: string) {
|
|
if (filePath.startsWith("~/")) {
|
|
return path.join(process.env.HOME ?? "", filePath.slice(2))
|
|
}
|
|
return path.resolve(filePath)
|
|
}
|
|
}
|