omniAI / frontend /src /api.ts
hasimjaneef's picture
Publish validated findings before turn end and trace pipeline latency
79e11ca verified
Raw History Blame Contribute Delete
3.67 kB
import type { Config, Holding, MediaChunk, Session, Snapshot } from './types';
export class ApiError extends Error { constructor(message: string, public status: number) { super(message); } }
// Memory only: never persist passwords or tokens in browser storage or URLs.
// This also works when the Hugging Face iframe cannot retain login cookies.
let workspaceToken = '';
async function request<T>(path: string, body?: unknown, token?: string, signal?: AbortSignal): Promise<T> {
const workspaceRequest = path === '/config' || path === '/sessions';
const response = await fetch(`/api${path}`, {method: body === undefined ? 'GET' : 'POST', headers: { ...(body === undefined ? {} : {'Content-Type': 'application/json'}), ...(token ? {'X-Session-Token': token} : {}), ...(workspaceRequest && workspaceToken ? {'X-Workspace-Token': workspaceToken} : {}) }, body: body === undefined ? undefined : JSON.stringify(body), signal, credentials: 'same-origin', cache: 'no-store', redirect: 'error'});
if (!response.ok) {
if (response.status === 401 && workspaceRequest) workspaceToken = '';
// Never reflect arbitrary upstream response bodies, URLs, or credentials into the UI.
let message = `Request could not be completed (${response.status}).`;
try { const error = await response.json(); if (typeof error.detail === 'string' && error.detail.length < 350 && !/Bearer|hf_|token|https?:\/\//i.test(error.detail)) message = error.detail; } catch { /* retain safe generic message */ }
throw new ApiError(message, response.status);
}
return response.json() as Promise<T>;
}
export const api = {
login: async (password: string) => {
workspaceToken = '';
const result = await request<{authenticated: boolean; workspace_token: string}>('/login', {password});
if (!result.authenticated || typeof result.workspace_token !== 'string' || !/^[A-Za-z0-9_-]{43}$/.test(result.workspace_token)) {
throw new ApiError('Workspace login could not be confirmed. Try unlocking again.', 502);
}
workspaceToken = result.workspace_token;
},
config: () => request<Config>('/config'),
create: (holdings: Holding[], objective: string, duration_ms: number | null, source_name: string, signal?: AbortSignal) => request<Session>('/sessions', {holdings, objective, duration_ms, source_name, authorized: true}, undefined, signal),
poll: (s: Session, signal?: AbortSignal, afterRevision?: number) => request<Snapshot>(`/sessions/${s.session_id}${afterRevision === undefined ? '' : `?after_revision=${afterRevision}&wait_seconds=20`}`, undefined, s.session_token, signal),
timings: (s: Session) => request<Record<string, unknown>>(`/sessions/${s.session_id}/timings`, undefined, s.session_token),
chunk: (s: Session, chunk: MediaChunk, signal?: AbortSignal) => request<{accepted_sequence: number; processed_sequence: number; queue_depth: number; queue_capacity: number; duplicate: boolean}>(`/sessions/${s.session_id}/chunks`, chunk, s.session_token, signal),
epoch: (s: Session, source_time_ms: number, reason: string, signal?: AbortSignal) => request<{epoch: number}>(`/sessions/${s.session_id}/epoch`, {source_time_ms, reason}, s.session_token, signal),
diagnostic: (s: Session, code: string, message: string, source_start_ms?: number, source_end_ms?: number) => request(`/sessions/${s.session_id}/diagnostics`, {epoch: s.epoch, code, message, source_start_ms, source_end_ms}, s.session_token),
complete: (s: Session, signal?: AbortSignal) => request<Snapshot>(`/sessions/${s.session_id}/complete`, {}, s.session_token, signal),
stop: (s: Session) => request<Snapshot>(`/sessions/${s.session_id}/stop`, {}, s.session_token),
};