mirror of https://github.com/openclaw/openclaw.git
60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
import path from "node:path";
|
|
import { resolveStateDir } from "../config/paths.js";
|
|
import { createAsyncLock, readJsonFile, writeJsonAtomic } from "./json-files.js";
|
|
|
|
export type VoiceWakeConfig = {
|
|
triggers: string[];
|
|
updatedAtMs: number;
|
|
};
|
|
|
|
const DEFAULT_TRIGGERS = ["openclaw", "claude", "computer"];
|
|
|
|
function resolvePath(baseDir?: string) {
|
|
const root = baseDir ?? resolveStateDir();
|
|
return path.join(root, "settings", "voicewake.json");
|
|
}
|
|
|
|
function sanitizeTriggers(triggers: string[] | undefined | null): string[] {
|
|
const cleaned = (triggers ?? [])
|
|
.map((w) => (typeof w === "string" ? w.trim() : ""))
|
|
.filter((w) => w.length > 0);
|
|
return cleaned.length > 0 ? cleaned : DEFAULT_TRIGGERS;
|
|
}
|
|
|
|
const withLock = createAsyncLock();
|
|
|
|
export function defaultVoiceWakeTriggers() {
|
|
return [...DEFAULT_TRIGGERS];
|
|
}
|
|
|
|
export async function loadVoiceWakeConfig(baseDir?: string): Promise<VoiceWakeConfig> {
|
|
const filePath = resolvePath(baseDir);
|
|
const existing = await readJsonFile<VoiceWakeConfig>(filePath);
|
|
if (!existing) {
|
|
return { triggers: defaultVoiceWakeTriggers(), updatedAtMs: 0 };
|
|
}
|
|
return {
|
|
triggers: sanitizeTriggers(existing.triggers),
|
|
updatedAtMs:
|
|
typeof existing.updatedAtMs === "number" && existing.updatedAtMs > 0
|
|
? existing.updatedAtMs
|
|
: 0,
|
|
};
|
|
}
|
|
|
|
export async function setVoiceWakeTriggers(
|
|
triggers: string[],
|
|
baseDir?: string,
|
|
): Promise<VoiceWakeConfig> {
|
|
const sanitized = sanitizeTriggers(triggers);
|
|
const filePath = resolvePath(baseDir);
|
|
return await withLock(async () => {
|
|
const next: VoiceWakeConfig = {
|
|
triggers: sanitized,
|
|
updatedAtMs: Date.now(),
|
|
};
|
|
await writeJsonAtomic(filePath, next);
|
|
return next;
|
|
});
|
|
}
|