Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

142 lines
3.5 KiB
TypeScript

import { apiFetch, apiUrl } from "@/lib/api";
import { invalidateClientCache, withClientCache } from "@/lib/client-cache";
const PERSONAS_CACHE_PREFIX = "personas:";
export type PersonaSource = "user" | "admin";
export interface PersonaInfo {
name: string;
description: string;
source: PersonaSource;
read_only: boolean;
}
export interface PersonaDetail extends PersonaInfo {
content: string;
}
export interface CreatePersonaPayload {
name: string;
description: string;
content: string;
}
export interface UpdatePersonaPayload {
description?: string;
content?: string;
rename_to?: string;
}
function normalizeSource(raw: unknown): PersonaSource {
return raw === "admin" ? "admin" : "user";
}
async function asJson(response: Response) {
if (!response.ok) {
let detail = `${response.status} ${response.statusText}`;
try {
const body = await response.json();
if (body?.detail) detail = String(body.detail);
} catch {
/* ignore */
}
throw new Error(detail);
}
return response.json();
}
function normalizeInfo(item: {
name?: unknown;
description?: unknown;
source?: unknown;
read_only?: unknown;
}): PersonaInfo {
return {
name: String(item?.name ?? ""),
description: String(item?.description ?? ""),
source: normalizeSource(item?.source),
read_only: Boolean(item?.read_only),
};
}
export async function listPersonas(options?: {
force?: boolean;
}): Promise<PersonaInfo[]> {
return withClientCache<PersonaInfo[]>(
`${PERSONAS_CACHE_PREFIX}list`,
async () => {
const response = await apiFetch(apiUrl("/api/v1/personas/list"), {
cache: "no-store",
});
const data = await asJson(response);
const items = Array.isArray(data?.personas) ? data.personas : [];
return items.map(normalizeInfo);
},
{ force: options?.force },
);
}
export async function getPersona(name: string): Promise<PersonaDetail> {
const response = await apiFetch(
apiUrl(`/api/v1/personas/${encodeURIComponent(name)}`),
{
cache: "no-store",
},
);
const data = await asJson(response);
return {
...normalizeInfo({ ...data, name: data?.name ?? name }),
content: String(data?.content ?? ""),
};
}
export async function createPersona(
payload: CreatePersonaPayload,
): Promise<PersonaInfo> {
const response = await apiFetch(apiUrl("/api/v1/personas/create"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: payload.name,
description: payload.description,
content: payload.content,
}),
});
const data = await asJson(response);
invalidatePersonasCache();
return normalizeInfo({ ...data, name: data?.name ?? payload.name });
}
export async function updatePersona(
name: string,
payload: UpdatePersonaPayload,
): Promise<PersonaInfo> {
const response = await apiFetch(
apiUrl(`/api/v1/personas/${encodeURIComponent(name)}`),
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
},
);
const data = await asJson(response);
invalidatePersonasCache();
return normalizeInfo({ ...data, name: data?.name ?? name });
}
export async function deletePersona(name: string): Promise<void> {
const response = await apiFetch(
apiUrl(`/api/v1/personas/${encodeURIComponent(name)}`),
{
method: "DELETE",
},
);
await asJson(response);
invalidatePersonasCache();
}
export function invalidatePersonasCache() {
invalidateClientCache(PERSONAS_CACHE_PREFIX);
}