mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 00:10:43 +00:00
108 lines
3.2 KiB
TypeScript
108 lines
3.2 KiB
TypeScript
import fs from "node:fs";
|
|
import { loadJsonFile, saveJsonFile } from "../../infra/json-file.js";
|
|
import { normalizeOptionalString } from "../../shared/string-coerce.js";
|
|
import { AUTH_STORE_VERSION } from "./constants.js";
|
|
import { resolveAuthStatePath } from "./paths.js";
|
|
import type { AuthProfileState, AuthProfileStateStore, ProfileUsageStats } from "./types.js";
|
|
|
|
function normalizeAuthProfileOrder(raw: unknown): AuthProfileState["order"] {
|
|
if (!raw || typeof raw !== "object") {
|
|
return undefined;
|
|
}
|
|
const normalized = Object.entries(raw as Record<string, unknown>).reduce(
|
|
(acc, [provider, value]) => {
|
|
if (!Array.isArray(value)) {
|
|
return acc;
|
|
}
|
|
const list = value.map((entry) => normalizeOptionalString(entry) ?? "").filter(Boolean);
|
|
if (list.length > 0) {
|
|
acc[provider] = list;
|
|
}
|
|
return acc;
|
|
},
|
|
{} as Record<string, string[]>,
|
|
);
|
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
}
|
|
|
|
export function coerceAuthProfileState(raw: unknown): AuthProfileState {
|
|
if (!raw || typeof raw !== "object") {
|
|
return {};
|
|
}
|
|
const record = raw as Record<string, unknown>;
|
|
return {
|
|
order: normalizeAuthProfileOrder(record.order),
|
|
lastGood:
|
|
record.lastGood && typeof record.lastGood === "object"
|
|
? (record.lastGood as Record<string, string>)
|
|
: undefined,
|
|
usageStats:
|
|
record.usageStats && typeof record.usageStats === "object"
|
|
? (record.usageStats as Record<string, ProfileUsageStats>)
|
|
: undefined,
|
|
};
|
|
}
|
|
|
|
export function mergeAuthProfileState(
|
|
base: AuthProfileState,
|
|
override: AuthProfileState,
|
|
): AuthProfileState {
|
|
const mergeRecord = <T>(left?: Record<string, T>, right?: Record<string, T>) => {
|
|
if (!left && !right) {
|
|
return undefined;
|
|
}
|
|
if (!left) {
|
|
return { ...right };
|
|
}
|
|
if (!right) {
|
|
return { ...left };
|
|
}
|
|
return { ...left, ...right };
|
|
};
|
|
|
|
return {
|
|
order: mergeRecord(base.order, override.order),
|
|
lastGood: mergeRecord(base.lastGood, override.lastGood),
|
|
usageStats: mergeRecord(base.usageStats, override.usageStats),
|
|
};
|
|
}
|
|
|
|
export function loadPersistedAuthProfileState(agentDir?: string): AuthProfileState {
|
|
return coerceAuthProfileState(loadJsonFile(resolveAuthStatePath(agentDir)));
|
|
}
|
|
|
|
export function buildPersistedAuthProfileState(
|
|
store: AuthProfileState,
|
|
): AuthProfileStateStore | null {
|
|
const state = coerceAuthProfileState(store);
|
|
if (!state.order && !state.lastGood && !state.usageStats) {
|
|
return null;
|
|
}
|
|
return {
|
|
version: AUTH_STORE_VERSION,
|
|
...(state.order ? { order: state.order } : {}),
|
|
...(state.lastGood ? { lastGood: state.lastGood } : {}),
|
|
...(state.usageStats ? { usageStats: state.usageStats } : {}),
|
|
};
|
|
}
|
|
|
|
export function savePersistedAuthProfileState(
|
|
store: AuthProfileState,
|
|
agentDir?: string,
|
|
): AuthProfileStateStore | null {
|
|
const payload = buildPersistedAuthProfileState(store);
|
|
const statePath = resolveAuthStatePath(agentDir);
|
|
if (!payload) {
|
|
try {
|
|
fs.unlinkSync(statePath);
|
|
} catch (error) {
|
|
if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") {
|
|
throw error;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
saveJsonFile(statePath, payload);
|
|
return payload;
|
|
}
|