| const BASE = ""; |
|
|
| |
| |
| let _sessionToken: string | null = null; |
|
|
| async function fetchJSON<T>(url: string, init?: RequestInit): Promise<T> { |
| const res = await fetch(`${BASE}${url}`, init); |
| if (!res.ok) { |
| const text = await res.text().catch(() => res.statusText); |
| throw new Error(`${res.status}: ${text}`); |
| } |
| return res.json(); |
| } |
|
|
| async function getSessionToken(): Promise<string> { |
| if (_sessionToken) return _sessionToken; |
| const resp = await fetchJSON<{ token: string }>("/api/auth/session-token"); |
| _sessionToken = resp.token; |
| return _sessionToken; |
| } |
|
|
| export const api = { |
| getStatus: () => fetchJSON<StatusResponse>("/api/status"), |
| getSessions: (limit = 20, offset = 0) => |
| fetchJSON<PaginatedSessions>(`/api/sessions?limit=${limit}&offset=${offset}`), |
| getSessionMessages: (id: string) => |
| fetchJSON<SessionMessagesResponse>(`/api/sessions/${encodeURIComponent(id)}/messages`), |
| deleteSession: (id: string) => |
| fetchJSON<{ ok: boolean }>(`/api/sessions/${encodeURIComponent(id)}`, { |
| method: "DELETE", |
| }), |
| getLogs: (params: { file?: string; lines?: number; level?: string; component?: string }) => { |
| const qs = new URLSearchParams(); |
| if (params.file) qs.set("file", params.file); |
| if (params.lines) qs.set("lines", String(params.lines)); |
| if (params.level && params.level !== "ALL") qs.set("level", params.level); |
| if (params.component && params.component !== "all") qs.set("component", params.component); |
| return fetchJSON<LogsResponse>(`/api/logs?${qs.toString()}`); |
| }, |
| getAnalytics: (days: number) => |
| fetchJSON<AnalyticsResponse>(`/api/analytics/usage?days=${days}`), |
| getConfig: () => fetchJSON<Record<string, unknown>>("/api/config"), |
| getDefaults: () => fetchJSON<Record<string, unknown>>("/api/config/defaults"), |
| getSchema: () => fetchJSON<{ fields: Record<string, unknown>; category_order: string[] }>("/api/config/schema"), |
| saveConfig: (config: Record<string, unknown>) => |
| fetchJSON<{ ok: boolean }>("/api/config", { |
| method: "PUT", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ config }), |
| }), |
| getConfigRaw: () => fetchJSON<{ yaml: string }>("/api/config/raw"), |
| saveConfigRaw: (yaml_text: string) => |
| fetchJSON<{ ok: boolean }>("/api/config/raw", { |
| method: "PUT", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ yaml_text }), |
| }), |
| getEnvVars: () => fetchJSON<Record<string, EnvVarInfo>>("/api/env"), |
| setEnvVar: (key: string, value: string) => |
| fetchJSON<{ ok: boolean }>("/api/env", { |
| method: "PUT", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ key, value }), |
| }), |
| deleteEnvVar: (key: string) => |
| fetchJSON<{ ok: boolean }>("/api/env", { |
| method: "DELETE", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ key }), |
| }), |
| revealEnvVar: async (key: string) => { |
| const token = await getSessionToken(); |
| return fetchJSON<{ key: string; value: string }>("/api/env/reveal", { |
| method: "POST", |
| headers: { |
| "Content-Type": "application/json", |
| Authorization: `Bearer ${token}`, |
| }, |
| body: JSON.stringify({ key }), |
| }); |
| }, |
|
|
| |
| getCronJobs: () => fetchJSON<CronJob[]>("/api/cron/jobs"), |
| createCronJob: (job: { prompt: string; schedule: string; name?: string; deliver?: string }) => |
| fetchJSON<CronJob>("/api/cron/jobs", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify(job), |
| }), |
| pauseCronJob: (id: string) => |
| fetchJSON<{ ok: boolean }>(`/api/cron/jobs/${id}/pause`, { method: "POST" }), |
| resumeCronJob: (id: string) => |
| fetchJSON<{ ok: boolean }>(`/api/cron/jobs/${id}/resume`, { method: "POST" }), |
| triggerCronJob: (id: string) => |
| fetchJSON<{ ok: boolean }>(`/api/cron/jobs/${id}/trigger`, { method: "POST" }), |
| deleteCronJob: (id: string) => |
| fetchJSON<{ ok: boolean }>(`/api/cron/jobs/${id}`, { method: "DELETE" }), |
|
|
| |
| getSkills: () => fetchJSON<SkillInfo[]>("/api/skills"), |
| toggleSkill: (name: string, enabled: boolean) => |
| fetchJSON<{ ok: boolean }>("/api/skills/toggle", { |
| method: "PUT", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ name, enabled }), |
| }), |
| getToolsets: () => fetchJSON<ToolsetInfo[]>("/api/tools/toolsets"), |
|
|
| |
| searchSessions: (q: string) => |
| fetchJSON<SessionSearchResponse>(`/api/sessions/search?q=${encodeURIComponent(q)}`), |
|
|
| |
| getOAuthProviders: () => |
| fetchJSON<OAuthProvidersResponse>("/api/providers/oauth"), |
| disconnectOAuthProvider: async (providerId: string) => { |
| const token = await getSessionToken(); |
| return fetchJSON<{ ok: boolean; provider: string }>( |
| `/api/providers/oauth/${encodeURIComponent(providerId)}`, |
| { |
| method: "DELETE", |
| headers: { Authorization: `Bearer ${token}` }, |
| }, |
| ); |
| }, |
| startOAuthLogin: async (providerId: string) => { |
| const token = await getSessionToken(); |
| return fetchJSON<OAuthStartResponse>( |
| `/api/providers/oauth/${encodeURIComponent(providerId)}/start`, |
| { |
| method: "POST", |
| headers: { |
| "Content-Type": "application/json", |
| Authorization: `Bearer ${token}`, |
| }, |
| body: "{}", |
| }, |
| ); |
| }, |
| submitOAuthCode: async (providerId: string, sessionId: string, code: string) => { |
| const token = await getSessionToken(); |
| return fetchJSON<OAuthSubmitResponse>( |
| `/api/providers/oauth/${encodeURIComponent(providerId)}/submit`, |
| { |
| method: "POST", |
| headers: { |
| "Content-Type": "application/json", |
| Authorization: `Bearer ${token}`, |
| }, |
| body: JSON.stringify({ session_id: sessionId, code }), |
| }, |
| ); |
| }, |
| pollOAuthSession: (providerId: string, sessionId: string) => |
| fetchJSON<OAuthPollResponse>( |
| `/api/providers/oauth/${encodeURIComponent(providerId)}/poll/${encodeURIComponent(sessionId)}`, |
| ), |
| cancelOAuthSession: async (sessionId: string) => { |
| const token = await getSessionToken(); |
| return fetchJSON<{ ok: boolean }>( |
| `/api/providers/oauth/sessions/${encodeURIComponent(sessionId)}`, |
| { |
| method: "DELETE", |
| headers: { Authorization: `Bearer ${token}` }, |
| }, |
| ); |
| }, |
| }; |
|
|
| export interface PlatformStatus { |
| error_code?: string; |
| error_message?: string; |
| state: string; |
| updated_at: string; |
| } |
|
|
| export interface StatusResponse { |
| active_sessions: number; |
| config_path: string; |
| config_version: number; |
| env_path: string; |
| gateway_exit_reason: string | null; |
| gateway_pid: number | null; |
| gateway_platforms: Record<string, PlatformStatus>; |
| gateway_running: boolean; |
| gateway_state: string | null; |
| gateway_updated_at: string | null; |
| hermes_home: string; |
| latest_config_version: number; |
| release_date: string; |
| version: string; |
| } |
|
|
| export interface SessionInfo { |
| id: string; |
| source: string | null; |
| model: string | null; |
| title: string | null; |
| started_at: number; |
| ended_at: number | null; |
| last_active: number; |
| is_active: boolean; |
| message_count: number; |
| tool_call_count: number; |
| input_tokens: number; |
| output_tokens: number; |
| preview: string | null; |
| } |
|
|
| export interface PaginatedSessions { |
| sessions: SessionInfo[]; |
| total: number; |
| limit: number; |
| offset: number; |
| } |
|
|
| export interface EnvVarInfo { |
| is_set: boolean; |
| redacted_value: string | null; |
| description: string; |
| url: string | null; |
| category: string; |
| is_password: boolean; |
| tools: string[]; |
| advanced: boolean; |
| } |
|
|
| export interface SessionMessage { |
| role: "user" | "assistant" | "system" | "tool"; |
| content: string | null; |
| tool_calls?: Array<{ |
| id: string; |
| function: { name: string; arguments: string }; |
| }>; |
| tool_name?: string; |
| tool_call_id?: string; |
| timestamp?: number; |
| } |
|
|
| export interface SessionMessagesResponse { |
| session_id: string; |
| messages: SessionMessage[]; |
| } |
|
|
| export interface LogsResponse { |
| file: string; |
| lines: string[]; |
| } |
|
|
| export interface AnalyticsDailyEntry { |
| day: string; |
| input_tokens: number; |
| output_tokens: number; |
| cache_read_tokens: number; |
| reasoning_tokens: number; |
| estimated_cost: number; |
| actual_cost: number; |
| sessions: number; |
| } |
|
|
| export interface AnalyticsModelEntry { |
| model: string; |
| input_tokens: number; |
| output_tokens: number; |
| estimated_cost: number; |
| sessions: number; |
| } |
|
|
| export interface AnalyticsResponse { |
| daily: AnalyticsDailyEntry[]; |
| by_model: AnalyticsModelEntry[]; |
| totals: { |
| total_input: number; |
| total_output: number; |
| total_cache_read: number; |
| total_reasoning: number; |
| total_estimated_cost: number; |
| total_actual_cost: number; |
| total_sessions: number; |
| }; |
| } |
|
|
| export interface CronJob { |
| id: string; |
| name?: string; |
| prompt: string; |
| schedule: { kind: string; expr: string; display: string }; |
| schedule_display: string; |
| enabled: boolean; |
| state: string; |
| deliver?: string; |
| last_run_at?: string | null; |
| next_run_at?: string | null; |
| last_error?: string | null; |
| } |
|
|
| export interface SkillInfo { |
| name: string; |
| description: string; |
| category: string; |
| enabled: boolean; |
| } |
|
|
| export interface ToolsetInfo { |
| name: string; |
| label: string; |
| description: string; |
| enabled: boolean; |
| configured: boolean; |
| tools: string[]; |
| } |
|
|
| export interface SessionSearchResult { |
| session_id: string; |
| snippet: string; |
| role: string | null; |
| source: string | null; |
| model: string | null; |
| session_started: number | null; |
| } |
|
|
| export interface SessionSearchResponse { |
| results: SessionSearchResult[]; |
| } |
|
|
| |
|
|
| export interface OAuthProviderStatus { |
| logged_in: boolean; |
| source?: string | null; |
| source_label?: string | null; |
| token_preview?: string | null; |
| expires_at?: string | null; |
| has_refresh_token?: boolean; |
| last_refresh?: string | null; |
| error?: string; |
| } |
|
|
| export interface OAuthProvider { |
| id: string; |
| name: string; |
| |
| |
| flow: "pkce" | "device_code" | "external"; |
| cli_command: string; |
| docs_url: string; |
| status: OAuthProviderStatus; |
| } |
|
|
| export interface OAuthProvidersResponse { |
| providers: OAuthProvider[]; |
| } |
|
|
| |
| export type OAuthStartResponse = |
| | { |
| session_id: string; |
| flow: "pkce"; |
| auth_url: string; |
| expires_in: number; |
| } |
| | { |
| session_id: string; |
| flow: "device_code"; |
| user_code: string; |
| verification_url: string; |
| expires_in: number; |
| poll_interval: number; |
| }; |
|
|
| export interface OAuthSubmitResponse { |
| ok: boolean; |
| status: "approved" | "error"; |
| message?: string; |
| } |
|
|
| export interface OAuthPollResponse { |
| session_id: string; |
| status: "pending" | "approved" | "denied" | "expired" | "error"; |
| error_message?: string | null; |
| expires_at?: number | null; |
| } |
|
|