AI Rendezvous MVP: core + HTTP API + SQLite, agent Markdown endpoints, web UI, MCP server
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@ai-rendezvous/client-sdk",
|
||||
"version": "0.1.0",
|
||||
"description": "Thin TypeScript client for the AI Rendezvous HTTP API. Harness integrations build on this.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc -b"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-rendezvous/core": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
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}`);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user