mirror of
https://github.com/TianyiDataScience/openclaw-control-center.git
synced 2026-08-14 08:52:27 +00:00
fix: quiet missing avatar preferences on first run
Treat a missing runtime/avatar-preferences.json as the default first-run state instead of surfacing a warning, while keeping malformed files visible as normalized fallback issues. Refs #52.
This commit is contained in:
@@ -48,6 +48,13 @@ export async function loadAvatarPreferences(): Promise<AvatarPreferencesLoadResu
|
||||
parsed = JSON.parse(raw) as unknown;
|
||||
} catch (error) {
|
||||
const fallback = defaultAvatarPreferences();
|
||||
if (isNotFoundError(error)) {
|
||||
return {
|
||||
path: AVATAR_PREFERENCES_PATH,
|
||||
preferences: fallback,
|
||||
issues,
|
||||
};
|
||||
}
|
||||
const reason = error instanceof Error ? error.message : "unable to read preference file";
|
||||
issues = [`avatar preferences fallback applied: ${reason}`];
|
||||
// 只读模式:不自动创建文件,只返回默认配置
|
||||
@@ -228,6 +235,15 @@ function asObject(input: unknown): Record<string, unknown> | undefined {
|
||||
return input as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function isNotFoundError(error: unknown): boolean {
|
||||
return Boolean(
|
||||
error &&
|
||||
typeof error === "object" &&
|
||||
"code" in error &&
|
||||
(error as { code?: unknown }).code === "ENOENT",
|
||||
);
|
||||
}
|
||||
|
||||
async function writeAvatarPreferences(preferences: AvatarPreferences): Promise<void> {
|
||||
await mkdir(join(process.cwd(), "runtime"), { recursive: true });
|
||||
await writeFile(AVATAR_PREFERENCES_PATH, `${JSON.stringify(preferences, null, 2)}\n`, "utf8");
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { readFile, rm, writeFile } from "node:fs/promises";
|
||||
import {
|
||||
AVATAR_PREFERENCES_PATH,
|
||||
defaultAvatarPreferences,
|
||||
loadAvatarPreferences,
|
||||
saveAvatarPreferences,
|
||||
} from "../src/runtime/avatar-preferences";
|
||||
|
||||
test("missing avatar preferences use defaults without first-run warnings", async () => {
|
||||
const original = await readMaybe(AVATAR_PREFERENCES_PATH);
|
||||
|
||||
try {
|
||||
await rm(AVATAR_PREFERENCES_PATH, { force: true });
|
||||
|
||||
const loaded = await loadAvatarPreferences();
|
||||
assert.deepEqual(loaded.preferences.agents, {});
|
||||
assert.equal(loaded.preferences.version, 1);
|
||||
assert.deepEqual(loaded.issues, []);
|
||||
} finally {
|
||||
if (original === undefined) {
|
||||
await rm(AVATAR_PREFERENCES_PATH, { force: true });
|
||||
} else {
|
||||
await writeFile(AVATAR_PREFERENCES_PATH, original, "utf8");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("avatar preferences still persist after save and reload", async () => {
|
||||
const original = await readMaybe(AVATAR_PREFERENCES_PATH);
|
||||
|
||||
try {
|
||||
await saveAvatarPreferences({
|
||||
...defaultAvatarPreferences(),
|
||||
agents: {
|
||||
main: {
|
||||
mode: "pixel",
|
||||
animal: "panda",
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const loaded = await loadAvatarPreferences();
|
||||
assert.equal(loaded.preferences.agents.main?.mode, "pixel");
|
||||
assert.equal(loaded.preferences.agents.main?.animal, "panda");
|
||||
} finally {
|
||||
if (original === undefined) {
|
||||
await rm(AVATAR_PREFERENCES_PATH, { force: true });
|
||||
} else {
|
||||
await writeFile(AVATAR_PREFERENCES_PATH, original, "utf8");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function readMaybe(path: string): Promise<string | undefined> {
|
||||
try {
|
||||
return await readFile(path, "utf8");
|
||||
} catch (error) {
|
||||
if (
|
||||
error &&
|
||||
typeof error === "object" &&
|
||||
"code" in error &&
|
||||
(error as { code?: unknown }).code === "ENOENT"
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user