Files
Agent-System/web/src/api/documentRevision.ts
T

109 lines
3.4 KiB
TypeScript

import type {
ChangeReport,
DiffResult,
DocumentVersion,
PatchContentBody,
RevisionState,
} from '../features/documentRevision/types';
import { mergeAuthHeaders } from './authHeaders';
async function api<T>(path: string, init?: RequestInit): Promise<T> {
const extra =
init?.headers instanceof Headers
? Object.fromEntries(init.headers.entries())
: ((init?.headers as Record<string, string> | undefined) ?? {});
const contentType =
typeof extra['Content-Type'] === 'string' ? extra['Content-Type'] : 'application/json';
const headers = mergeAuthHeaders(extra, contentType);
const res = await fetch(path, {
credentials: 'include',
...init,
headers,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
const err = new Error((data as { error?: string }).error || res.statusText) as Error & {
status: number;
code?: string;
conflict?: unknown;
body?: unknown;
};
err.status = res.status;
err.code = (data as { code?: string }).code;
err.conflict = (data as { conflict?: unknown }).conflict;
err.body = data;
throw err;
}
return data as T;
}
export function getRevisionState(docId: string) {
return api<RevisionState>(`/api/v1/documents/${encodeURIComponent(docId)}/revision-state`);
}
export function listVersions(docId: string) {
return api<{ versions: DocumentVersion[] }>(`/api/v1/documents/${encodeURIComponent(docId)}/versions`);
}
export function getVersionContent(docId: string, versionNo: number, view: 'final' | 'baseline' = 'final') {
return api<{ content_markdown: string; content_sha256: string }>(
`/api/v1/documents/${encodeURIComponent(docId)}/versions/${versionNo}?view=${view}`,
);
}
export function getDiff(docId: string, fromVersion: number, toVersion: number) {
return api<DiffResult>(
`/api/v1/documents/${encodeURIComponent(docId)}/diff?from_version=${fromVersion}&to_version=${toVersion}&from_view=final&to_view=final`,
);
}
export function getManualOverlay(docId: string) {
return api<DiffResult>(`/api/v1/documents/${encodeURIComponent(docId)}/manual-overlay`);
}
export function patchDocumentContent(
docId: string,
body: PatchContentBody,
opts: { etag: string; idempotencyKey: string },
) {
return api<{ state: RevisionState; manual_overlay: DiffResult; conflict?: unknown }>(
`/api/v1/documents/${encodeURIComponent(docId)}/content`,
{
method: 'PATCH',
headers: {
'If-Match': `"${opts.etag}"`,
'Idempotency-Key': opts.idempotencyKey,
},
body: JSON.stringify(body),
},
);
}
export function getChangeReport(docId: string, reportId: number) {
return api<ChangeReport>(`/api/v1/documents/${encodeURIComponent(docId)}/change-reports/${reportId}`);
}
export function decideAgentCandidate(
docId: string,
candidateId: number,
body: {
decision: 'commit' | 'reject';
confirmed?: boolean;
expected_input_sha?: string;
expected_output_sha?: string;
reason?: string;
},
) {
return api<RevisionState>(
`/api/v1/documents/${encodeURIComponent(docId)}/agent-candidates/${candidateId}/decision`,
{ method: 'POST', body: JSON.stringify(body) },
);
}
export function patchNotification(id: number, status: 'read' | 'acted' | 'dismissed') {
return api<{ ok: boolean }>(`/api/v1/notifications/${id}`, {
method: 'PATCH',
body: JSON.stringify({ status }),
});
}