Files
AI-Rendezvous/packages/client-sdk/src/index.ts
T
a.andreev bb0eb6979e
deploy / deploy (push) Canceled after 0s
Participants report taking the room into work (join)
- POST /api/rooms/:id/join + rendezvous_join MCP tool; creator auto-joined
- joined_at shown to agents, observer (invite not confirmed yet) and used by
  the turn indicator; what_you_should_do_next starts with joining
- fix: participant list order is now stable (insertion order)
2026-09-06 21:35:03 +03:00

102 lines
3.4 KiB
TypeScript

import type { CreateRoomInput, CreatedRoom, RoomView } from '@ai-rendezvous/core';
export class ApiError extends Error {
constructor(readonly status: number, readonly code: string, message: string) {
super(message);
}
}
/**
* Thin client for the AI Rendezvous HTTP API. Uses only fetch — no harness
* assumptions, suitable for integrations in any Node-based environment.
*/
export class RendezvousClient {
constructor(
readonly baseUrl: string,
readonly token?: string,
) {}
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'content-type': 'application/json',
...(this.token ? { authorization: `Bearer ${this.token}` } : {}),
},
body: body === undefined ? undefined : JSON.stringify(body),
});
const text = await res.text();
const data = text ? JSON.parse(text) : {};
if (!res.ok) {
const err = (data as { error?: { code?: string; message?: string } }).error ?? {};
throw new ApiError(res.status, err.code ?? 'unknown', err.message ?? res.statusText);
}
return data as T;
}
createRoom(input: CreateRoomInput): Promise<CreatedRoom> {
return this.request<CreatedRoom>('POST', '/api/rooms', input);
}
getRoom(roomId: string): Promise<RoomView> {
return this.request<RoomView>('GET', `/api/rooms/${roomId}`);
}
join(roomId: string): Promise<{ joined: boolean; participants: { role: string; joined_at: string | null }[] }> {
return this.request('POST', `/api/rooms/${roomId}/join`, {});
}
postMessage(roomId: string, content: string): Promise<unknown> {
return this.request('POST', `/api/rooms/${roomId}/messages`, { content });
}
ask(
roomId: string,
question: string,
opts: { blocking?: boolean; addressed_to_participant_id?: string } = {},
): Promise<unknown> {
return this.request('POST', `/api/rooms/${roomId}/questions`, {
question,
blocking: opts.blocking ?? true,
...(opts.addressed_to_participant_id
? { addressed_to_participant_id: opts.addressed_to_participant_id }
: {}),
});
}
resolve(roomId: string, questionId: string, resolution: string): Promise<unknown> {
return this.request('POST', `/api/rooms/${roomId}/questions/${questionId}/resolve`, { resolution });
}
proposeContract(roomId: string, markdown: string): Promise<{ version: number }> {
return this.request('PUT', `/api/rooms/${roomId}/contract`, { markdown });
}
agree(roomId: string): Promise<{
agreed_contract_version: number;
room_status: string;
everyone_agreed: boolean;
}> {
return this.request('POST', `/api/rooms/${roomId}/agree`, {});
}
async finalMarkdown(roomId: string): Promise<string> {
const res = await fetch(`${this.baseUrl}/api/rooms/${roomId}/final.md`, {
headers: this.token ? { authorization: `Bearer ${this.token}` } : {},
});
if (!res.ok) throw new ApiError(res.status, 'unknown', await res.text());
return res.text();
}
async inviteMarkdown(inviteUrl: string): Promise<string> {
const res = await fetch(`${inviteUrl}.md`);
if (!res.ok) throw new ApiError(res.status, 'unknown', await res.text());
return res.text();
}
/** Client for a specific participant. */
as(token: string): RendezvousClient {
return new RendezvousClient(this.baseUrl, token);
}
}