kikikita's picture
feat: implement NPC model switching and admin API for model management
e2d35c0
Raw
History Blame Contribute Delete
2.51 kB
import type { AdminModelsSnapshot, WorldSnapshot } from "./types";
const API_BASE = import.meta.env.VITE_WORLD_SIMULATOR_API ?? defaultApiBase();
const ADMIN_TOKEN_STORAGE_KEY = "world-simulator-admin-token";
function defaultApiBase(): string {
if (window.location.hostname === "127.0.0.1" && window.location.port === "5173") {
return "http://127.0.0.1:8000";
}
return window.location.origin;
}
export class ApiResponseError extends Error {
readonly status: number;
constructor(message: string, status: number) {
super(message);
this.name = "ApiResponseError";
this.status = status;
}
}
export async function fetchWorldSnapshot(
signal?: AbortSignal,
options: { warmup?: boolean } = {},
): Promise<WorldSnapshot> {
const url = new URL(`${API_BASE}/scene/state`, window.location.origin);
if (options.warmup) {
url.searchParams.set("warmup", "1");
}
const response = await fetch(url, { signal });
if (!response.ok) {
throw new ApiResponseError(`World snapshot failed: ${response.status}`, response.status);
}
return (await response.json()) as WorldSnapshot;
}
export async function tickWorld(): Promise<void> {
const response = await fetch(`${API_BASE}/tick`, { method: "POST" });
if (!response.ok) {
throw new ApiResponseError(`World tick failed: ${response.status}`, response.status);
}
}
export async function fetchAdminModels(signal?: AbortSignal): Promise<AdminModelsSnapshot> {
const response = await fetch(`${API_BASE}/admin/models`, {
headers: adminHeaders(),
signal,
});
if (!response.ok) {
throw new ApiResponseError(`Model profiles failed: ${response.status}`, response.status);
}
return (await response.json()) as AdminModelsSnapshot;
}
export async function setNpcModelProfile(npcId: string, profileId: string): Promise<void> {
const response = await fetch(`${API_BASE}/admin/npcs/${encodeURIComponent(npcId)}/model`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...adminHeaders(),
},
body: JSON.stringify({ profile_id: profileId }),
});
if (!response.ok) {
throw new ApiResponseError(`NPC model switch failed: ${response.status}`, response.status);
}
}
function adminHeaders(): Record<string, string> {
return {
"X-Admin-Token": adminToken(),
};
}
function adminToken(): string {
return (
import.meta.env.VITE_WORLD_SIMULATOR_ADMIN_TOKEN ||
window.localStorage.getItem(ADMIN_TOKEN_STORAGE_KEY) ||
"dev-admin-token"
);
}