feat: implement PI core chat timeline
This commit is contained in:
1 parent
54d8bb2e4a
commit
612135f911
40 files changed
+3881
-119
No files matched your search
@@ -66,6 +66,8 @@ type HostApiProxyResponse = {
|
||||
ok?: boolean;
|
||||
json?: unknown;
|
||||
text?: string;
|
||||
bytes?: Uint8Array;
|
||||
contentType?: string;
|
||||
};
|
||||
error?: { message?: string } | string;
|
||||
// backward compatibility fields
|
||||
@@ -80,6 +82,8 @@ type HostApiProxyData = {
|
||||
ok?: boolean;
|
||||
json?: unknown;
|
||||
text?: string;
|
||||
bytes?: Uint8Array;
|
||||
contentType?: string;
|
||||
transport?: 'dispatcher' | 'loopback';
|
||||
};
|
||||
|
||||
@@ -297,6 +301,65 @@ export async function hostApiFetch<T>(path: string, init?: RequestInit): Promise
|
||||
}
|
||||
}
|
||||
|
||||
export interface HostApiBinaryData {
|
||||
bytes: Uint8Array;
|
||||
contentType: string;
|
||||
}
|
||||
|
||||
export async function hostApiFetchBytes(path: string): Promise<HostApiBinaryData> {
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
const response = await invokeIpc<HostApiProxyResponse>('hostapi:fetch', {
|
||||
path,
|
||||
method: 'GET',
|
||||
headers: { Accept: 'application/octet-stream' },
|
||||
body: null,
|
||||
});
|
||||
if (!response.ok) throw new Error(resolveProxyErrorMessage(response.error));
|
||||
const data = response.data;
|
||||
if (!data || data.ok === false || (typeof data.status === 'number' && data.status >= 400)) {
|
||||
throw createProxyHttpError(data ?? {});
|
||||
}
|
||||
if (!(data.bytes instanceof Uint8Array) || !data.contentType) {
|
||||
throw new Error('Host API binary response is unavailable');
|
||||
}
|
||||
trackUiEvent('hostapi.fetch', {
|
||||
route: telemetryRoute(path),
|
||||
method: 'GET',
|
||||
source: 'ipc-proxy',
|
||||
durationMs: Date.now() - startedAt,
|
||||
status: data.status ?? 200,
|
||||
});
|
||||
return { bytes: data.bytes, contentType: data.contentType };
|
||||
} catch (error) {
|
||||
const normalized = normalizeAppError(error, { source: 'ipc-proxy', path, method: 'GET' });
|
||||
if (!shouldFallbackToBrowser(normalized.message) || !allowLocalhostFallback()) {
|
||||
throw normalized;
|
||||
}
|
||||
}
|
||||
|
||||
const [token, hostApiBase] = await Promise.all([getHostApiToken(), getHostApiBaseUrl()]);
|
||||
const response = await fetch(`${hostApiBase}${path}`, {
|
||||
headers: {
|
||||
Accept: 'application/octet-stream',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw normalizeAppError(new Error(`HTTP ${response.status}`), {
|
||||
source: 'browser-fallback',
|
||||
path,
|
||||
method: 'GET',
|
||||
status: response.status,
|
||||
});
|
||||
}
|
||||
return {
|
||||
bytes: new Uint8Array(await response.arrayBuffer()),
|
||||
contentType: response.headers.get('content-type')?.split(';', 1)[0]?.trim()
|
||||
|| 'application/octet-stream',
|
||||
};
|
||||
}
|
||||
|
||||
export function createHostEventSource(path = '/api/events'): EventSource {
|
||||
// EventSource does not support custom headers, so pass the auth token
|
||||
// as a query parameter. The server accepts both mechanisms.
|
||||
|
||||
Reference in new issue
Block a user