Add waiting arcade MVP

This commit is contained in:
Anthony Ettinger 2026-06-06 22:09:22 +00:00
parent b1d8fe475a
commit 4682b261c0
16 changed files with 1192 additions and 2 deletions

View file

@ -0,0 +1,87 @@
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { homedir } from "node:os";
export type JsonObject = Record<string, unknown>;
export const defaultConfig: JsonObject = {
waiting: {
arcade: {
enabled: true,
defaultGame: "hangman",
random: false,
autoStartAfterSeconds: 0,
interruptOnApproval: true,
interruptOnDone: true,
interruptOnError: true,
showTaskStatusOverlay: true
}
}
};
export function configPath() {
return join(homedir(), ".logicsrc", "config.json");
}
export function readConfig() {
const file = configPath();
if (!existsSync(file)) {
return structuredClone(defaultConfig);
}
const parsed = JSON.parse(readFileSync(file, "utf8")) as JsonObject;
return mergeConfig(structuredClone(defaultConfig), parsed);
}
export function writeConfig(config: JsonObject) {
const file = configPath();
mkdirSync(dirname(file), { recursive: true });
writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
return file;
}
export function getConfigValue(path: string, config = readConfig()) {
return path.split(".").reduce<unknown>((current, key) => (isObject(current) ? current[key] : undefined), config);
}
export function setConfigValue(path: string, rawValue: string, config = readConfig()) {
const parts = path.split(".").filter(Boolean);
if (parts.length === 0) {
throw new Error("Config path cannot be empty.");
}
let current: JsonObject = config;
for (const part of parts.slice(0, -1)) {
if (!isObject(current[part])) {
current[part] = {};
}
current = current[part] as JsonObject;
}
current[parts[parts.length - 1] as string] = parseConfigValue(rawValue);
return config;
}
export function parseConfigValue(value: string): unknown {
if (value === "true") return true;
if (value === "false") return false;
if (value === "null") return null;
if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
try {
return JSON.parse(value) as unknown;
} catch {
return value;
}
}
function mergeConfig(base: JsonObject, override: JsonObject): JsonObject {
for (const [key, value] of Object.entries(override)) {
if (isObject(value) && isObject(base[key])) {
base[key] = mergeConfig(base[key] as JsonObject, value);
} else {
base[key] = value;
}
}
return base;
}
function isObject(value: unknown): value is JsonObject {
return typeof value === "object" && value !== null && !Array.isArray(value);
}