async function request(path, opts = {}) {
const res = await fetch(path, {
headers: { 'Content-Type': 'application/json', ...(opts.headers || {}) },
...opts
});
if (!res.ok) {
let detail = '';
try {
detail = await res.text();
} catch {
}
throw new Error(`${res.status} ${res.statusText}: ${detail}`);
}
if (res.status === 204) return null;
const ct = res.headers.get('content-type') || '';
if (ct.includes('application/json')) return res.json();
return res.text();
}
export const api = {
health: () => request('/health'),
status: () => request('/api/v1/status'),
config: () => request('/api/v1/config'),
listPalaces: () => request('/api/v1/palaces'),
getPalace: (id) => request(`/api/v1/palaces/${encodeURIComponent(id)}`),
listDrawers: (id, { room, tag, limit } = {}) => {
const params = new URLSearchParams();
if (room) params.set('room', room);
if (tag) params.set('tag', tag);
if (limit) params.set('limit', String(limit));
const qs = params.toString();
return request(
`/api/v1/palaces/${encodeURIComponent(id)}/drawers${qs ? `?${qs}` : ''}`
);
},
logsTail: (n = 200) =>
request(`/api/v1/logs/tail?n=${encodeURIComponent(n)}`),
dreamStatus: () => request('/api/v1/dream/status'),
dreamRun: () => request('/api/v1/dream/run', { method: 'POST' }),
stopDaemon: () => request('/api/v1/admin/stop', { method: 'POST' })
};