@pnds/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +117 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +282 -0
- package/dist/constants.d.ts +72 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +88 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/types.d.ts +454 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/ws.d.ts +49 -0
- package/dist/ws.d.ts.map +1 -0
- package/dist/ws.js +198 -0
- package/package.json +31 -0
- package/src/client.ts +435 -0
- package/src/constants.ts +119 -0
- package/src/index.ts +6 -0
- package/src/types.ts +569 -0
- package/src/ws.ts +250 -0
package/dist/ws.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { WS_EVENTS } from './constants.js';
|
|
2
|
+
export class PondWs {
|
|
3
|
+
ws = null;
|
|
4
|
+
options;
|
|
5
|
+
listeners = new Map();
|
|
6
|
+
stateListeners = new Set();
|
|
7
|
+
heartbeatTimer = null;
|
|
8
|
+
reconnectTimer = null;
|
|
9
|
+
reconnectAttempts = 0;
|
|
10
|
+
lastSeq = 0;
|
|
11
|
+
_state = 'disconnected';
|
|
12
|
+
intentionalClose = false;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = {
|
|
15
|
+
reconnect: true,
|
|
16
|
+
reconnectInterval: 1000,
|
|
17
|
+
maxReconnectInterval: 30000,
|
|
18
|
+
...options,
|
|
19
|
+
};
|
|
20
|
+
this.lastSeq = options.lastSeq ?? 0;
|
|
21
|
+
}
|
|
22
|
+
get state() {
|
|
23
|
+
return this._state;
|
|
24
|
+
}
|
|
25
|
+
async connect() {
|
|
26
|
+
this.intentionalClose = false;
|
|
27
|
+
this.setState('connecting');
|
|
28
|
+
let ticket;
|
|
29
|
+
try {
|
|
30
|
+
ticket = await this.options.getTicket();
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.error('Failed to get WS ticket:', err);
|
|
34
|
+
if (this.options.reconnect) {
|
|
35
|
+
this.scheduleReconnect();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.setState('disconnected');
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const wsUrl = ticket.ws_url || this.options.wsUrl;
|
|
43
|
+
if (!wsUrl) {
|
|
44
|
+
console.error('No WS URL available');
|
|
45
|
+
this.setState('disconnected');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const params = new URLSearchParams({ ticket: ticket.ticket });
|
|
49
|
+
if (this.lastSeq > 0) {
|
|
50
|
+
params.set('last_seq', String(this.lastSeq));
|
|
51
|
+
}
|
|
52
|
+
this.ws = new WebSocket(`${wsUrl}?${params.toString()}`);
|
|
53
|
+
this.ws.onopen = () => {
|
|
54
|
+
this.reconnectAttempts = 0;
|
|
55
|
+
this.setState('connected');
|
|
56
|
+
};
|
|
57
|
+
this.ws.onmessage = (event) => {
|
|
58
|
+
try {
|
|
59
|
+
const frame = JSON.parse(event.data);
|
|
60
|
+
this.handleFrame(frame);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// ignore malformed frames
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
this.ws.onclose = () => {
|
|
67
|
+
this.stopHeartbeat();
|
|
68
|
+
if (this.intentionalClose) {
|
|
69
|
+
this.setState('disconnected');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (this.options.reconnect) {
|
|
73
|
+
this.scheduleReconnect();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.setState('disconnected');
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this.ws.onerror = () => {
|
|
80
|
+
// onclose will fire after this
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
disconnect() {
|
|
84
|
+
this.intentionalClose = true;
|
|
85
|
+
this.stopHeartbeat();
|
|
86
|
+
this.clearReconnect();
|
|
87
|
+
if (this.ws) {
|
|
88
|
+
this.ws.close();
|
|
89
|
+
this.ws = null;
|
|
90
|
+
}
|
|
91
|
+
this.setState('disconnected');
|
|
92
|
+
}
|
|
93
|
+
// ---- Client -> Server messages ----
|
|
94
|
+
/** Tell the server which chats the user is currently viewing (no auth implications). */
|
|
95
|
+
watch(chatIds) {
|
|
96
|
+
this.send({ type: WS_EVENTS.WATCH, data: { chat_ids: chatIds } });
|
|
97
|
+
}
|
|
98
|
+
sendTyping(chatId, action, threadRootId) {
|
|
99
|
+
this.send({
|
|
100
|
+
type: WS_EVENTS.TYPING,
|
|
101
|
+
data: { chat_id: chatId, thread_root_id: threadRootId ?? null, action },
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
sendAck(messageId) {
|
|
105
|
+
this.send({ type: WS_EVENTS.ACK, data: { message_id: messageId } });
|
|
106
|
+
}
|
|
107
|
+
sendRead(chatId, lastReadId) {
|
|
108
|
+
this.send({ type: WS_EVENTS.READ, data: { chat_id: chatId, last_read_id: lastReadId } });
|
|
109
|
+
}
|
|
110
|
+
/** Send an agent heartbeat with telemetry data. */
|
|
111
|
+
sendAgentHeartbeat(sessionId, telemetry) {
|
|
112
|
+
this.send({
|
|
113
|
+
type: WS_EVENTS.AGENT_HEARTBEAT,
|
|
114
|
+
data: { session_id: sessionId, telemetry },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
sendEvent(type, data) {
|
|
118
|
+
this.send({ type, data });
|
|
119
|
+
}
|
|
120
|
+
// ---- Event listeners ----
|
|
121
|
+
on(event, handler) {
|
|
122
|
+
let set = this.listeners.get(event);
|
|
123
|
+
if (!set) {
|
|
124
|
+
set = new Set();
|
|
125
|
+
this.listeners.set(event, set);
|
|
126
|
+
}
|
|
127
|
+
set.add(handler);
|
|
128
|
+
return () => {
|
|
129
|
+
set.delete(handler);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
off(event, handler) {
|
|
133
|
+
this.listeners.get(event)?.delete(handler);
|
|
134
|
+
}
|
|
135
|
+
onStateChange(handler) {
|
|
136
|
+
this.stateListeners.add(handler);
|
|
137
|
+
return () => {
|
|
138
|
+
this.stateListeners.delete(handler);
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// ---- Internal ----
|
|
142
|
+
send(frame) {
|
|
143
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
144
|
+
this.ws.send(JSON.stringify(frame));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
handleFrame(frame) {
|
|
148
|
+
// Track seq for reconnection
|
|
149
|
+
if (frame.seq !== undefined) {
|
|
150
|
+
this.lastSeq = frame.seq;
|
|
151
|
+
}
|
|
152
|
+
// Handle hello — start heartbeat
|
|
153
|
+
if (frame.type === WS_EVENTS.HELLO) {
|
|
154
|
+
const interval = frame.data.heartbeat_interval;
|
|
155
|
+
this.startHeartbeat(interval);
|
|
156
|
+
}
|
|
157
|
+
// Dispatch to listeners
|
|
158
|
+
const handlers = this.listeners.get(frame.type);
|
|
159
|
+
if (handlers) {
|
|
160
|
+
for (const handler of handlers) {
|
|
161
|
+
handler(frame.data, frame.seq);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
startHeartbeat(intervalSec) {
|
|
166
|
+
this.stopHeartbeat();
|
|
167
|
+
this.heartbeatTimer = setInterval(() => {
|
|
168
|
+
this.send({ type: WS_EVENTS.PING, data: { ts: Date.now() } });
|
|
169
|
+
}, intervalSec * 1000);
|
|
170
|
+
}
|
|
171
|
+
stopHeartbeat() {
|
|
172
|
+
if (this.heartbeatTimer) {
|
|
173
|
+
clearInterval(this.heartbeatTimer);
|
|
174
|
+
this.heartbeatTimer = null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
scheduleReconnect() {
|
|
178
|
+
this.setState('reconnecting');
|
|
179
|
+
this.clearReconnect();
|
|
180
|
+
const delay = Math.min(this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts), this.options.maxReconnectInterval);
|
|
181
|
+
this.reconnectAttempts++;
|
|
182
|
+
this.reconnectTimer = setTimeout(() => {
|
|
183
|
+
this.connect();
|
|
184
|
+
}, delay);
|
|
185
|
+
}
|
|
186
|
+
clearReconnect() {
|
|
187
|
+
if (this.reconnectTimer) {
|
|
188
|
+
clearTimeout(this.reconnectTimer);
|
|
189
|
+
this.reconnectTimer = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
setState(state) {
|
|
193
|
+
this._state = state;
|
|
194
|
+
for (const listener of this.stateListeners) {
|
|
195
|
+
listener(state);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pnds/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript client SDK for Pond — REST + WebSocket client, shared types",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/RFlowAI/pond",
|
|
9
|
+
"directory": "ts/sdk"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^5.7.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"dev": "tsc --watch"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { ENDPOINTS } from './constants.js';
|
|
2
|
+
import type {
|
|
3
|
+
AuthTokens,
|
|
4
|
+
RegisterRequest,
|
|
5
|
+
LoginRequest,
|
|
6
|
+
CheckEmailResponse,
|
|
7
|
+
User,
|
|
8
|
+
Organization,
|
|
9
|
+
OrgMember,
|
|
10
|
+
OrgMemberRole,
|
|
11
|
+
Chat,
|
|
12
|
+
ChatMember,
|
|
13
|
+
ChatMemberRole,
|
|
14
|
+
CreateChatRequest,
|
|
15
|
+
Message,
|
|
16
|
+
SendMessageRequest,
|
|
17
|
+
PatchMessageRequest,
|
|
18
|
+
PaginatedResponse,
|
|
19
|
+
Approval,
|
|
20
|
+
PresignResponse,
|
|
21
|
+
PresignUploadRequest,
|
|
22
|
+
CreateAgentRequest,
|
|
23
|
+
CreateAgentResponse,
|
|
24
|
+
AgentWithRuntime,
|
|
25
|
+
ApiKey,
|
|
26
|
+
WsTicketResponse,
|
|
27
|
+
HostedStatus,
|
|
28
|
+
WorkspaceFiles,
|
|
29
|
+
Task,
|
|
30
|
+
CreateTaskRequest,
|
|
31
|
+
UpdateTaskRequest,
|
|
32
|
+
TaskRelation,
|
|
33
|
+
CreateTaskRelationRequest,
|
|
34
|
+
} from './types.js';
|
|
35
|
+
|
|
36
|
+
export interface PondClientOptions {
|
|
37
|
+
baseUrl?: string;
|
|
38
|
+
token?: string;
|
|
39
|
+
onTokenExpired?: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class PondClient {
|
|
43
|
+
private baseUrl: string;
|
|
44
|
+
private token: string | null;
|
|
45
|
+
private onTokenExpired?: () => void;
|
|
46
|
+
|
|
47
|
+
constructor(options: PondClientOptions = {}) {
|
|
48
|
+
this.baseUrl = options.baseUrl ?? '';
|
|
49
|
+
this.token = options.token ?? null;
|
|
50
|
+
this.onTokenExpired = options.onTokenExpired;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setToken(token: string | null) {
|
|
54
|
+
this.token = token;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getToken(): string | null {
|
|
58
|
+
return this.token;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async request<T>(
|
|
62
|
+
method: string,
|
|
63
|
+
path: string,
|
|
64
|
+
body?: unknown,
|
|
65
|
+
query?: Record<string, string | number | boolean | undefined>,
|
|
66
|
+
): Promise<T> {
|
|
67
|
+
let url = `${this.baseUrl}${path}`;
|
|
68
|
+
|
|
69
|
+
if (query) {
|
|
70
|
+
const params = new URLSearchParams();
|
|
71
|
+
for (const [key, value] of Object.entries(query)) {
|
|
72
|
+
if (value !== undefined) {
|
|
73
|
+
params.set(key, String(value));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const qs = params.toString();
|
|
77
|
+
if (qs) url += `?${qs}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const headers: Record<string, string> = {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
};
|
|
83
|
+
if (this.token) {
|
|
84
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const res = await fetch(url, {
|
|
88
|
+
method,
|
|
89
|
+
headers,
|
|
90
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (res.status === 401 && this.onTokenExpired) {
|
|
94
|
+
this.onTokenExpired();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
99
|
+
const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
|
|
100
|
+
const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
|
|
101
|
+
const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
|
|
102
|
+
throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (res.status === 204) return undefined as T;
|
|
106
|
+
return res.json() as Promise<T>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---- Auth ----
|
|
110
|
+
|
|
111
|
+
async register(req: RegisterRequest): Promise<AuthTokens> {
|
|
112
|
+
return this.request('POST', ENDPOINTS.AUTH_REGISTER, req);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async login(req: LoginRequest): Promise<AuthTokens> {
|
|
116
|
+
return this.request('POST', ENDPOINTS.AUTH_LOGIN, req);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async refreshToken(refreshToken: string): Promise<AuthTokens> {
|
|
120
|
+
return this.request('POST', ENDPOINTS.AUTH_REFRESH, { refresh_token: refreshToken });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async logout(): Promise<void> {
|
|
124
|
+
return this.request('POST', ENDPOINTS.AUTH_LOGOUT);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async checkEmail(email: string): Promise<CheckEmailResponse> {
|
|
128
|
+
return this.request('POST', ENDPOINTS.AUTH_CHECK_EMAIL, { email });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async verifyEmail(email: string, code: string): Promise<void> {
|
|
132
|
+
return this.request('POST', ENDPOINTS.AUTH_VERIFY_EMAIL, { email, code });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async resendCode(email: string): Promise<void> {
|
|
136
|
+
return this.request('POST', ENDPOINTS.AUTH_RESEND_CODE, { email });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---- WebSocket ----
|
|
140
|
+
|
|
141
|
+
async getWsTicket(): Promise<WsTicketResponse> {
|
|
142
|
+
return this.request('POST', ENDPOINTS.WS_TICKET);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---- Users ----
|
|
146
|
+
|
|
147
|
+
async getMe(): Promise<User> {
|
|
148
|
+
return this.request('GET', ENDPOINTS.USERS_ME);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async updateMe(data: Partial<Pick<User, 'display_name' | 'avatar_url'>>): Promise<User> {
|
|
152
|
+
return this.request('PATCH', ENDPOINTS.USERS_ME, data);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async getUser(id: string): Promise<User> {
|
|
156
|
+
return this.request('GET', ENDPOINTS.USER(id));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async searchUsers(q: string): Promise<User[]> {
|
|
160
|
+
const res = await this.request<{ data: User[] }>('GET', ENDPOINTS.USERS_SEARCH, undefined, { q });
|
|
161
|
+
return res.data;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---- Organizations ----
|
|
165
|
+
|
|
166
|
+
async createOrg(data: { name: string; avatar_url?: string }): Promise<Organization> {
|
|
167
|
+
return this.request('POST', ENDPOINTS.ORGS, data);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async getOrgs(): Promise<Organization[]> {
|
|
171
|
+
const res = await this.request<{ data: Organization[] }>('GET', ENDPOINTS.ORGS);
|
|
172
|
+
return res.data;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async getOrg(orgId: string): Promise<Organization> {
|
|
176
|
+
return this.request('GET', ENDPOINTS.ORG(orgId));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async updateOrg(orgId: string, data: Partial<Pick<Organization, 'name' | 'avatar_url'>>): Promise<Organization> {
|
|
180
|
+
return this.request('PATCH', ENDPOINTS.ORG(orgId), data);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async deleteOrg(orgId: string): Promise<void> {
|
|
184
|
+
return this.request('DELETE', ENDPOINTS.ORG(orgId));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async getOrgMembers(orgId: string): Promise<OrgMember[]> {
|
|
188
|
+
const res = await this.request<{ data: OrgMember[] }>('GET', ENDPOINTS.ORG_MEMBERS(orgId));
|
|
189
|
+
return res.data;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async addOrgMember(orgId: string, userId: string, role?: OrgMemberRole): Promise<void> {
|
|
193
|
+
return this.request('POST', ENDPOINTS.ORG_MEMBERS(orgId), { user_id: userId, role: role ?? 'member' });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async removeOrgMember(orgId: string, userId: string): Promise<void> {
|
|
197
|
+
return this.request('DELETE', ENDPOINTS.ORG_MEMBER(orgId, userId));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async updateOrgMember(orgId: string, userId: string, role: OrgMemberRole): Promise<void> {
|
|
201
|
+
return this.request('PATCH', ENDPOINTS.ORG_MEMBER(orgId, userId), { role });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---- Chats (org-scoped) ----
|
|
205
|
+
|
|
206
|
+
async createChat(orgId: string, req: Omit<CreateChatRequest, 'org_id'>): Promise<Chat> {
|
|
207
|
+
return this.request('POST', ENDPOINTS.CHATS(orgId), req);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async getChats(orgId: string, params?: { limit?: number; cursor?: string }): Promise<PaginatedResponse<Chat>> {
|
|
211
|
+
return this.request('GET', ENDPOINTS.CHATS(orgId), undefined, params);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async getChat(orgId: string, chatId: string): Promise<Chat> {
|
|
215
|
+
return this.request('GET', ENDPOINTS.CHAT(orgId, chatId));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async updateChat(orgId: string, chatId: string, data: Partial<Pick<Chat, 'name' | 'avatar_url' | 'description'>>): Promise<Chat> {
|
|
219
|
+
return this.request('PATCH', ENDPOINTS.CHAT(orgId, chatId), data);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async deleteChat(orgId: string, chatId: string): Promise<void> {
|
|
223
|
+
return this.request('DELETE', ENDPOINTS.CHAT(orgId, chatId));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ---- Chat Members ----
|
|
227
|
+
|
|
228
|
+
async getChatMembers(orgId: string, chatId: string): Promise<ChatMember[]> {
|
|
229
|
+
const res = await this.request<{ data: ChatMember[] }>('GET', ENDPOINTS.CHAT_MEMBERS(orgId, chatId));
|
|
230
|
+
return res.data;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async addChatMember(orgId: string, chatId: string, userId: string, role?: string): Promise<void> {
|
|
234
|
+
return this.request('POST', ENDPOINTS.CHAT_MEMBERS(orgId, chatId), { user_id: userId, role });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async removeChatMember(orgId: string, chatId: string, userId: string): Promise<void> {
|
|
238
|
+
return this.request('DELETE', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async updateChatMemberRole(orgId: string, chatId: string, userId: string, role: ChatMemberRole): Promise<void> {
|
|
242
|
+
return this.request('PATCH', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId), { role });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ---- Messages ----
|
|
246
|
+
|
|
247
|
+
async sendMessage(orgId: string, chatId: string, req: SendMessageRequest): Promise<Message> {
|
|
248
|
+
return this.request('POST', ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async getMessages(
|
|
252
|
+
orgId: string,
|
|
253
|
+
chatId: string,
|
|
254
|
+
params?: {
|
|
255
|
+
before?: string;
|
|
256
|
+
after?: string;
|
|
257
|
+
limit?: number;
|
|
258
|
+
thread_root_id?: string;
|
|
259
|
+
top_level?: boolean;
|
|
260
|
+
},
|
|
261
|
+
): Promise<PaginatedResponse<Message>> {
|
|
262
|
+
return this.request('GET', ENDPOINTS.CHAT_MESSAGES(orgId, chatId), undefined, params as Record<string, string | number | boolean | undefined>);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async getMessage(id: string): Promise<Message> {
|
|
266
|
+
return this.request('GET', ENDPOINTS.MESSAGE(id));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async editMessage(id: string, content: Message['content']): Promise<Message> {
|
|
270
|
+
return this.request('PATCH', ENDPOINTS.MESSAGE(id), { content });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async deleteMessage(id: string): Promise<void> {
|
|
274
|
+
return this.request('DELETE', ENDPOINTS.MESSAGE(id));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async patchMessage(id: string, req: PatchMessageRequest): Promise<void> {
|
|
278
|
+
return this.request('POST', ENDPOINTS.MESSAGE_PATCHES(id), req);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async getMessageReplies(
|
|
282
|
+
id: string,
|
|
283
|
+
params?: { limit?: number; before?: string; after?: string },
|
|
284
|
+
): Promise<PaginatedResponse<Message>> {
|
|
285
|
+
return this.request('GET', ENDPOINTS.MESSAGE_REPLIES(id), undefined, params);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ---- File Upload ----
|
|
289
|
+
|
|
290
|
+
async getUploadPresignUrl(req: PresignUploadRequest): Promise<PresignResponse> {
|
|
291
|
+
return this.request('POST', ENDPOINTS.UPLOAD_PRESIGN, req);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async completeUpload(attachmentId: string): Promise<{ attachment_id: string }> {
|
|
295
|
+
return this.request('POST', ENDPOINTS.UPLOAD_COMPLETE, { attachment_id: attachmentId });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---- Approvals ----
|
|
299
|
+
|
|
300
|
+
async decideApproval(id: string, decision: 'approve' | 'reject'): Promise<Approval> {
|
|
301
|
+
return this.request('POST', ENDPOINTS.APPROVAL_DECIDE(id), { decision });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async getPendingApprovals(): Promise<Approval[]> {
|
|
305
|
+
return this.request('GET', ENDPOINTS.APPROVALS_PENDING);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ---- Agents (org-scoped) ----
|
|
309
|
+
|
|
310
|
+
async createAgent(orgId: string, req: CreateAgentRequest): Promise<CreateAgentResponse> {
|
|
311
|
+
return this.request('POST', ENDPOINTS.AGENTS(orgId), req);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async getAgents(orgId: string): Promise<AgentWithRuntime[]> {
|
|
315
|
+
const res = await this.request<{ data: AgentWithRuntime[] }>('GET', ENDPOINTS.AGENTS(orgId));
|
|
316
|
+
return res.data;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async updateAgent(orgId: string, agentId: string, data: Partial<CreateAgentRequest>): Promise<User> {
|
|
320
|
+
return this.request('PATCH', ENDPOINTS.AGENT(orgId, agentId), data);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async deleteAgent(orgId: string, agentId: string): Promise<void> {
|
|
324
|
+
return this.request('DELETE', ENDPOINTS.AGENT(orgId, agentId));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async createAgentApiKey(orgId: string, agentId: string): Promise<ApiKey> {
|
|
328
|
+
return this.request('POST', ENDPOINTS.AGENT_API_KEYS(orgId, agentId));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async revokeAgentApiKey(orgId: string, agentId: string, key: string): Promise<void> {
|
|
332
|
+
return this.request('DELETE', ENDPOINTS.AGENT_API_KEY(orgId, agentId, key));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async getAgentActivity(orgId: string, agentId: string, params?: { limit?: number }): Promise<Message[]> {
|
|
336
|
+
const res = await this.request<{ data: Message[] }>('GET', ENDPOINTS.AGENT_ACTIVITY(orgId, agentId), undefined, params);
|
|
337
|
+
return res.data;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async getAgentMonitor(orgId: string, agentId: string): Promise<unknown> {
|
|
341
|
+
const res = await this.request<{ data: unknown }>('GET', ENDPOINTS.AGENT_MONITOR(orgId, agentId));
|
|
342
|
+
return res.data;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
async startAgent(orgId: string, agentId: string): Promise<void> {
|
|
346
|
+
return this.request('POST', ENDPOINTS.AGENT_START(orgId, agentId));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async stopAgent(orgId: string, agentId: string): Promise<void> {
|
|
350
|
+
return this.request('POST', ENDPOINTS.AGENT_STOP(orgId, agentId));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async getHostedStatus(orgId: string, agentId: string): Promise<HostedStatus> {
|
|
354
|
+
return this.request('GET', ENDPOINTS.AGENT_HOSTED_STATUS(orgId, agentId));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async updateWorkspace(orgId: string, agentId: string, files: WorkspaceFiles): Promise<void> {
|
|
358
|
+
return this.request('PUT', ENDPOINTS.AGENT_WORKSPACE(orgId, agentId), files);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async getAgentLogs(orgId: string, agentId: string, lines = 100): Promise<{ data: string[] }> {
|
|
362
|
+
return this.request('GET', ENDPOINTS.AGENT_LOGS(orgId, agentId), undefined, { lines });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ---- Unread ----
|
|
366
|
+
|
|
367
|
+
async getUnreadCounts(): Promise<Record<string, number>> {
|
|
368
|
+
const res = await this.request<{ data: Record<string, number> }>('GET', ENDPOINTS.UNREAD);
|
|
369
|
+
return res.data;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async markRead(orgId: string, chatId: string, messageId: string): Promise<void> {
|
|
373
|
+
return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ---- Tasks (org-scoped) ----
|
|
377
|
+
|
|
378
|
+
async createTask(orgId: string, data: CreateTaskRequest): Promise<Task> {
|
|
379
|
+
return this.request('POST', ENDPOINTS.TASKS(orgId), data);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async getTasks(orgId: string, params?: {
|
|
383
|
+
status?: string;
|
|
384
|
+
priority?: string;
|
|
385
|
+
assignee_id?: string;
|
|
386
|
+
parent_id?: string;
|
|
387
|
+
limit?: number;
|
|
388
|
+
cursor?: string;
|
|
389
|
+
sort?: string;
|
|
390
|
+
order?: string;
|
|
391
|
+
}): Promise<PaginatedResponse<Task>> {
|
|
392
|
+
return this.request('GET', ENDPOINTS.TASKS(orgId), undefined, params);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async getTask(orgId: string, taskId: string): Promise<Task> {
|
|
396
|
+
return this.request('GET', ENDPOINTS.TASK(orgId, taskId));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async updateTask(orgId: string, taskId: string, data: UpdateTaskRequest): Promise<Task> {
|
|
400
|
+
return this.request('PATCH', ENDPOINTS.TASK(orgId, taskId), data);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async deleteTask(orgId: string, taskId: string): Promise<void> {
|
|
404
|
+
return this.request('DELETE', ENDPOINTS.TASK(orgId, taskId));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async getSubtasks(orgId: string, taskId: string): Promise<Task[]> {
|
|
408
|
+
const res = await this.request<PaginatedResponse<Task>>('GET', ENDPOINTS.TASK_SUBTASKS(orgId, taskId));
|
|
409
|
+
return res.data;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async createTaskRelation(orgId: string, taskId: string, data: CreateTaskRelationRequest): Promise<TaskRelation> {
|
|
413
|
+
return this.request('POST', ENDPOINTS.TASK_RELATIONS(orgId, taskId), data);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async getTaskRelations(orgId: string, taskId: string): Promise<TaskRelation[]> {
|
|
417
|
+
const res = await this.request<{ data: TaskRelation[] }>('GET', ENDPOINTS.TASK_RELATIONS(orgId, taskId));
|
|
418
|
+
return res.data;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async deleteTaskRelation(orgId: string, taskId: string, relationId: string): Promise<void> {
|
|
422
|
+
return this.request('DELETE', ENDPOINTS.TASK_RELATION(orgId, taskId, relationId));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export class ApiError extends Error {
|
|
427
|
+
constructor(
|
|
428
|
+
public status: number,
|
|
429
|
+
message: string,
|
|
430
|
+
public code?: string,
|
|
431
|
+
) {
|
|
432
|
+
super(message);
|
|
433
|
+
this.name = 'ApiError';
|
|
434
|
+
}
|
|
435
|
+
}
|