@parall/sdk 1.12.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 +329 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +718 -0
- package/dist/constants.d.ts +161 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +194 -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 +1130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/ws.d.ts +83 -0
- package/dist/ws.d.ts.map +1 -0
- package/dist/ws.js +354 -0
- package/package.json +31 -0
- package/src/client.ts +1069 -0
- package/src/constants.ts +265 -0
- package/src/index.ts +6 -0
- package/src/types.ts +1399 -0
- package/src/ws.ts +404 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import { ENDPOINTS } from './constants.js';
|
|
2
|
+
export class ParallClient {
|
|
3
|
+
baseUrl;
|
|
4
|
+
token;
|
|
5
|
+
onTokenExpired;
|
|
6
|
+
getRefreshToken;
|
|
7
|
+
setTokens;
|
|
8
|
+
refreshPromise = null;
|
|
9
|
+
swimlaneName;
|
|
10
|
+
/** Auth endpoints excluded from automatic 401 refresh to prevent recursion. */
|
|
11
|
+
static AUTH_PATHS = new Set([
|
|
12
|
+
'/auth/login', '/auth/register', '/auth/refresh', '/auth/logout',
|
|
13
|
+
'/auth/verify-email', '/auth/resend-code', '/auth/check-email',
|
|
14
|
+
]);
|
|
15
|
+
/** Proactive refresh when token expires within this window (seconds). */
|
|
16
|
+
static REFRESH_THRESHOLD_S = 5 * 60;
|
|
17
|
+
/** Build headers common to all requests (auth, swimlane). */
|
|
18
|
+
buildHeaders(extra) {
|
|
19
|
+
const headers = {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
...extra,
|
|
22
|
+
};
|
|
23
|
+
if (this.token) {
|
|
24
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
25
|
+
}
|
|
26
|
+
if (this.swimlaneName) {
|
|
27
|
+
headers['X-Prll-Swimlane'] = this.swimlaneName;
|
|
28
|
+
}
|
|
29
|
+
return headers;
|
|
30
|
+
}
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.baseUrl = options.baseUrl ?? '';
|
|
33
|
+
this.token = options.token ?? null;
|
|
34
|
+
this.onTokenExpired = options.onTokenExpired;
|
|
35
|
+
this.getRefreshToken = options.getRefreshToken;
|
|
36
|
+
this.setTokens = options.setTokens;
|
|
37
|
+
this.swimlaneName = options.swimlaneName;
|
|
38
|
+
}
|
|
39
|
+
setToken(token) {
|
|
40
|
+
this.token = token;
|
|
41
|
+
}
|
|
42
|
+
getToken() {
|
|
43
|
+
return this.token;
|
|
44
|
+
}
|
|
45
|
+
/** Decode the `exp` claim from a JWT without verifying the signature. */
|
|
46
|
+
static decodeJwtExp(token) {
|
|
47
|
+
try {
|
|
48
|
+
const parts = token.split('.');
|
|
49
|
+
if (parts.length !== 3)
|
|
50
|
+
return null;
|
|
51
|
+
// base64url → standard base64
|
|
52
|
+
const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
53
|
+
const padded = b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), '=');
|
|
54
|
+
const payload = JSON.parse(atob(padded));
|
|
55
|
+
return typeof payload.exp === 'number' ? payload.exp : null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Proactive refresh: if the current access token expires within
|
|
63
|
+
* REFRESH_THRESHOLD_S, refresh it **before** sending the request.
|
|
64
|
+
* No-op when the token is still fresh, missing, or un-parseable.
|
|
65
|
+
*/
|
|
66
|
+
async ensureFreshToken(path) {
|
|
67
|
+
if (!this.token || !this.getRefreshToken)
|
|
68
|
+
return;
|
|
69
|
+
const pathSuffix = path.replace(/^\/api\/v1/, '');
|
|
70
|
+
if (ParallClient.AUTH_PATHS.has(pathSuffix))
|
|
71
|
+
return;
|
|
72
|
+
const exp = ParallClient.decodeJwtExp(this.token);
|
|
73
|
+
if (exp === null)
|
|
74
|
+
return;
|
|
75
|
+
if (exp - Date.now() / 1000 > ParallClient.REFRESH_THRESHOLD_S)
|
|
76
|
+
return;
|
|
77
|
+
await this.tryRefresh();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Single-flight token refresh. Multiple concurrent 401s share one promise
|
|
81
|
+
* so only one refresh request is made.
|
|
82
|
+
*/
|
|
83
|
+
async tryRefresh() {
|
|
84
|
+
const rt = this.getRefreshToken?.();
|
|
85
|
+
if (!rt)
|
|
86
|
+
return false;
|
|
87
|
+
if (!this.refreshPromise) {
|
|
88
|
+
this.refreshPromise = this.refreshToken(rt);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const tokens = await this.refreshPromise;
|
|
92
|
+
this.setToken(tokens.access_token);
|
|
93
|
+
this.setTokens?.(tokens);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
this.refreshPromise = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async request(method, path, body, query, retried = false) {
|
|
104
|
+
// Proactive refresh: block until token is fresh (no-op if still valid)
|
|
105
|
+
if (!retried) {
|
|
106
|
+
await this.ensureFreshToken(path);
|
|
107
|
+
}
|
|
108
|
+
let url = `${this.baseUrl}${path}`;
|
|
109
|
+
if (query) {
|
|
110
|
+
const params = new URLSearchParams();
|
|
111
|
+
for (const [key, value] of Object.entries(query)) {
|
|
112
|
+
if (value !== undefined) {
|
|
113
|
+
params.set(key, String(value));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const qs = params.toString();
|
|
117
|
+
if (qs)
|
|
118
|
+
url += `?${qs}`;
|
|
119
|
+
}
|
|
120
|
+
const headers = this.buildHeaders();
|
|
121
|
+
let res;
|
|
122
|
+
try {
|
|
123
|
+
res = await fetch(url, {
|
|
124
|
+
method,
|
|
125
|
+
headers,
|
|
126
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
127
|
+
signal: AbortSignal.timeout(15_000),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (err instanceof DOMException && err.name === 'TimeoutError') {
|
|
132
|
+
throw new ApiError(0, 'Request timed out', 'REQUEST_TIMEOUT');
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
if (res.status === 401) {
|
|
137
|
+
const pathSuffix = path.replace(/^\/api\/v1/, '');
|
|
138
|
+
const isAuthPath = ParallClient.AUTH_PATHS.has(pathSuffix);
|
|
139
|
+
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
140
|
+
const refreshed = await this.tryRefresh();
|
|
141
|
+
if (refreshed) {
|
|
142
|
+
// Retry once with the new token
|
|
143
|
+
return this.request(method, path, body, query, true);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Only fire for non-auth paths — auth endpoints (e.g. /auth/refresh)
|
|
147
|
+
// just throw; the original caller's path handles the notification.
|
|
148
|
+
if (this.onTokenExpired && !isAuthPath) {
|
|
149
|
+
this.onTokenExpired();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!res.ok) {
|
|
153
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
154
|
+
const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error : undefined;
|
|
155
|
+
const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
|
|
156
|
+
const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
|
|
157
|
+
throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
|
|
158
|
+
}
|
|
159
|
+
if (res.status === 204)
|
|
160
|
+
return undefined;
|
|
161
|
+
// 202 Accepted may have an empty body (e.g., async restart) or a JSON body.
|
|
162
|
+
if (res.status === 202) {
|
|
163
|
+
const text = await res.text();
|
|
164
|
+
if (!text)
|
|
165
|
+
return undefined;
|
|
166
|
+
return JSON.parse(text);
|
|
167
|
+
}
|
|
168
|
+
return res.json();
|
|
169
|
+
}
|
|
170
|
+
// ---- Auth ----
|
|
171
|
+
async register(req) {
|
|
172
|
+
return this.request('POST', ENDPOINTS.AUTH_REGISTER, req);
|
|
173
|
+
}
|
|
174
|
+
async login(req) {
|
|
175
|
+
return this.request('POST', ENDPOINTS.AUTH_LOGIN, req);
|
|
176
|
+
}
|
|
177
|
+
async refreshToken(refreshToken) {
|
|
178
|
+
return this.request('POST', ENDPOINTS.AUTH_REFRESH, { refresh_token: refreshToken });
|
|
179
|
+
}
|
|
180
|
+
async logout() {
|
|
181
|
+
return this.request('POST', ENDPOINTS.AUTH_LOGOUT);
|
|
182
|
+
}
|
|
183
|
+
async checkEmail(email) {
|
|
184
|
+
return this.request('POST', ENDPOINTS.AUTH_CHECK_EMAIL, { email });
|
|
185
|
+
}
|
|
186
|
+
async verifyEmail(email, code) {
|
|
187
|
+
return this.request('POST', ENDPOINTS.AUTH_VERIFY_EMAIL, { email, code });
|
|
188
|
+
}
|
|
189
|
+
async resendCode(email) {
|
|
190
|
+
return this.request('POST', ENDPOINTS.AUTH_RESEND_CODE, { email });
|
|
191
|
+
}
|
|
192
|
+
// ---- WebSocket ----
|
|
193
|
+
async getWsTicket() {
|
|
194
|
+
return this.request('POST', ENDPOINTS.WS_TICKET);
|
|
195
|
+
}
|
|
196
|
+
// ---- Users ----
|
|
197
|
+
async getMe() {
|
|
198
|
+
return this.request('GET', ENDPOINTS.USERS_ME);
|
|
199
|
+
}
|
|
200
|
+
async updateMe(data) {
|
|
201
|
+
return this.request('PATCH', ENDPOINTS.USERS_ME, data);
|
|
202
|
+
}
|
|
203
|
+
async getUser(id) {
|
|
204
|
+
return this.request('GET', ENDPOINTS.USER(id));
|
|
205
|
+
}
|
|
206
|
+
// ---- Organizations ----
|
|
207
|
+
async createOrg(data) {
|
|
208
|
+
return this.request('POST', ENDPOINTS.ORGS, data);
|
|
209
|
+
}
|
|
210
|
+
async getOrgs() {
|
|
211
|
+
const res = await this.request('GET', ENDPOINTS.ORGS);
|
|
212
|
+
return res.data;
|
|
213
|
+
}
|
|
214
|
+
async getOrg(orgId) {
|
|
215
|
+
return this.request('GET', ENDPOINTS.ORG(orgId));
|
|
216
|
+
}
|
|
217
|
+
async updateOrg(orgId, data) {
|
|
218
|
+
return this.request('PATCH', ENDPOINTS.ORG(orgId), data);
|
|
219
|
+
}
|
|
220
|
+
async deleteOrg(orgId) {
|
|
221
|
+
return this.request('DELETE', ENDPOINTS.ORG(orgId));
|
|
222
|
+
}
|
|
223
|
+
async getOrgMembers(orgId) {
|
|
224
|
+
const res = await this.request('GET', ENDPOINTS.ORG_MEMBERS(orgId));
|
|
225
|
+
return res.data;
|
|
226
|
+
}
|
|
227
|
+
async getOnlineMembers(orgId) {
|
|
228
|
+
const res = await this.request('GET', ENDPOINTS.ORG_MEMBERS_ONLINE(orgId));
|
|
229
|
+
return res.user_ids ?? [];
|
|
230
|
+
}
|
|
231
|
+
async removeOrgMember(orgId, userId) {
|
|
232
|
+
return this.request('DELETE', ENDPOINTS.ORG_MEMBER(orgId, userId));
|
|
233
|
+
}
|
|
234
|
+
async updateOrgMember(orgId, userId, role) {
|
|
235
|
+
return this.request('PATCH', ENDPOINTS.ORG_MEMBER(orgId, userId), { role });
|
|
236
|
+
}
|
|
237
|
+
// ---- Invitations ----
|
|
238
|
+
async createInvitation(orgId, email, role) {
|
|
239
|
+
return this.request('POST', ENDPOINTS.ORG_INVITATIONS(orgId), { email, role });
|
|
240
|
+
}
|
|
241
|
+
async getOrgInvitations(orgId) {
|
|
242
|
+
const res = await this.request('GET', ENDPOINTS.ORG_INVITATIONS(orgId));
|
|
243
|
+
return res.data;
|
|
244
|
+
}
|
|
245
|
+
async revokeInvitation(orgId, invId) {
|
|
246
|
+
return this.request('DELETE', ENDPOINTS.ORG_INVITATION(orgId, invId));
|
|
247
|
+
}
|
|
248
|
+
async resendInvitation(orgId, invId) {
|
|
249
|
+
return this.request('POST', ENDPOINTS.ORG_INVITATION_RESEND(orgId, invId));
|
|
250
|
+
}
|
|
251
|
+
async getMyInvitations() {
|
|
252
|
+
const res = await this.request('GET', ENDPOINTS.MY_INVITATIONS);
|
|
253
|
+
return res.data;
|
|
254
|
+
}
|
|
255
|
+
async acceptInvitation(id) {
|
|
256
|
+
return this.request('POST', ENDPOINTS.INVITATION_ACCEPT(id));
|
|
257
|
+
}
|
|
258
|
+
async declineInvitation(id) {
|
|
259
|
+
return this.request('POST', ENDPOINTS.INVITATION_DECLINE(id));
|
|
260
|
+
}
|
|
261
|
+
async getInvitationByToken(token) {
|
|
262
|
+
return this.request('GET', ENDPOINTS.INVITATION_BY_TOKEN(token));
|
|
263
|
+
}
|
|
264
|
+
// ---- Direct Messages (org-scoped) ----
|
|
265
|
+
async sendDirectMessage(orgId, req) {
|
|
266
|
+
return this.request('POST', ENDPOINTS.DM(orgId), req);
|
|
267
|
+
}
|
|
268
|
+
// ---- Chats (org-scoped) ----
|
|
269
|
+
async createChat(orgId, req) {
|
|
270
|
+
return this.request('POST', ENDPOINTS.CHATS(orgId), req);
|
|
271
|
+
}
|
|
272
|
+
async getChats(orgId, params) {
|
|
273
|
+
return this.request('GET', ENDPOINTS.CHATS(orgId), undefined, params);
|
|
274
|
+
}
|
|
275
|
+
async getChat(orgId, chatId) {
|
|
276
|
+
return this.request('GET', ENDPOINTS.CHAT(orgId, chatId));
|
|
277
|
+
}
|
|
278
|
+
async updateChat(orgId, chatId, data) {
|
|
279
|
+
return this.request('PATCH', ENDPOINTS.CHAT(orgId, chatId), data);
|
|
280
|
+
}
|
|
281
|
+
async deleteChat(orgId, chatId) {
|
|
282
|
+
return this.request('DELETE', ENDPOINTS.CHAT(orgId, chatId));
|
|
283
|
+
}
|
|
284
|
+
// ---- Chat Members ----
|
|
285
|
+
async getChatMembers(orgId, chatId) {
|
|
286
|
+
const res = await this.request('GET', ENDPOINTS.CHAT_MEMBERS(orgId, chatId));
|
|
287
|
+
return res.data;
|
|
288
|
+
}
|
|
289
|
+
async addChatMember(orgId, chatId, userId, role) {
|
|
290
|
+
return this.request('POST', ENDPOINTS.CHAT_MEMBERS(orgId, chatId), { user_id: userId, role });
|
|
291
|
+
}
|
|
292
|
+
async removeChatMember(orgId, chatId, userId) {
|
|
293
|
+
return this.request('DELETE', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId));
|
|
294
|
+
}
|
|
295
|
+
async updateChatMemberRole(orgId, chatId, userId, role) {
|
|
296
|
+
return this.request('PATCH', ENDPOINTS.CHAT_MEMBER(orgId, chatId, userId), { role });
|
|
297
|
+
}
|
|
298
|
+
// ---- Messages ----
|
|
299
|
+
async sendMessage(orgId, chatId, req) {
|
|
300
|
+
return this.request('POST', ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req);
|
|
301
|
+
}
|
|
302
|
+
async getMessages(orgId, chatId, params) {
|
|
303
|
+
return this.request('GET', ENDPOINTS.CHAT_MESSAGES(orgId, chatId), undefined, params);
|
|
304
|
+
}
|
|
305
|
+
async getMessage(id) {
|
|
306
|
+
return this.request('GET', ENDPOINTS.MESSAGE(id));
|
|
307
|
+
}
|
|
308
|
+
async editMessage(id, content) {
|
|
309
|
+
return this.request('PATCH', ENDPOINTS.MESSAGE(id), { content });
|
|
310
|
+
}
|
|
311
|
+
async deleteMessage(id) {
|
|
312
|
+
return this.request('DELETE', ENDPOINTS.MESSAGE(id));
|
|
313
|
+
}
|
|
314
|
+
async patchMessage(id, req) {
|
|
315
|
+
return this.request('POST', ENDPOINTS.MESSAGE_PATCHES(id), req);
|
|
316
|
+
}
|
|
317
|
+
async getMessageReplies(id, params) {
|
|
318
|
+
return this.request('GET', ENDPOINTS.MESSAGE_REPLIES(id), undefined, params);
|
|
319
|
+
}
|
|
320
|
+
// ---- File Upload ----
|
|
321
|
+
async getUploadPresignUrl(orgId, req) {
|
|
322
|
+
return this.request('POST', ENDPOINTS.UPLOAD_PRESIGN(orgId), req);
|
|
323
|
+
}
|
|
324
|
+
async completeUpload(orgId, attachmentId) {
|
|
325
|
+
return this.request('POST', ENDPOINTS.UPLOAD_COMPLETE(orgId), { attachment_id: attachmentId });
|
|
326
|
+
}
|
|
327
|
+
async getFileUrl(id) {
|
|
328
|
+
return this.request('GET', ENDPOINTS.FILE(id));
|
|
329
|
+
}
|
|
330
|
+
// ---- Approvals ----
|
|
331
|
+
async decideApproval(id, decision) {
|
|
332
|
+
return this.request('POST', ENDPOINTS.APPROVAL_DECIDE(id), { decision });
|
|
333
|
+
}
|
|
334
|
+
async getPendingApprovals() {
|
|
335
|
+
const res = await this.request('GET', ENDPOINTS.APPROVALS_PENDING);
|
|
336
|
+
return res.data;
|
|
337
|
+
}
|
|
338
|
+
// ---- Agents (org-scoped) ----
|
|
339
|
+
async createAgent(orgId, req) {
|
|
340
|
+
return this.request('POST', ENDPOINTS.AGENTS(orgId), req);
|
|
341
|
+
}
|
|
342
|
+
async getAgents(orgId) {
|
|
343
|
+
const res = await this.request('GET', ENDPOINTS.AGENTS(orgId));
|
|
344
|
+
return res.data;
|
|
345
|
+
}
|
|
346
|
+
async updateAgent(orgId, agentId, data) {
|
|
347
|
+
return this.request('PATCH', ENDPOINTS.AGENT(orgId, agentId), data);
|
|
348
|
+
}
|
|
349
|
+
async deleteAgent(orgId, agentId) {
|
|
350
|
+
return this.request('DELETE', ENDPOINTS.AGENT(orgId, agentId));
|
|
351
|
+
}
|
|
352
|
+
async createAgentApiKey(orgId, agentId) {
|
|
353
|
+
return this.request('POST', ENDPOINTS.AGENT_API_KEYS(orgId, agentId));
|
|
354
|
+
}
|
|
355
|
+
async revokeAgentApiKey(orgId, agentId, key) {
|
|
356
|
+
return this.request('DELETE', ENDPOINTS.AGENT_API_KEY(orgId, agentId, key));
|
|
357
|
+
}
|
|
358
|
+
async getAgentActivity(orgId, agentId, params) {
|
|
359
|
+
const res = await this.request('GET', ENDPOINTS.AGENT_ACTIVITY(orgId, agentId), undefined, params);
|
|
360
|
+
return res.data;
|
|
361
|
+
}
|
|
362
|
+
async getAgentMonitor(orgId, agentId) {
|
|
363
|
+
const res = await this.request('GET', ENDPOINTS.AGENT_MONITOR(orgId, agentId));
|
|
364
|
+
return res.data;
|
|
365
|
+
}
|
|
366
|
+
// ---- Agent Bootstrap (self) ----
|
|
367
|
+
async getAgentMe(orgId) {
|
|
368
|
+
return this.request('GET', ENDPOINTS.AGENT_ME(orgId));
|
|
369
|
+
}
|
|
370
|
+
// ---- Agent Sessions (org-scoped) ----
|
|
371
|
+
async createAgentSession(orgId, agentId, req) {
|
|
372
|
+
return this.request('POST', ENDPOINTS.AGENT_SESSIONS(orgId, agentId), req);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* List agent sessions.
|
|
376
|
+
* @param params.status - Comma-separated status filter (e.g., `'active,idle'`).
|
|
377
|
+
*/
|
|
378
|
+
async getAgentSessions(orgId, agentId, params) {
|
|
379
|
+
const res = await this.request('GET', ENDPOINTS.AGENT_SESSIONS(orgId, agentId), undefined, params);
|
|
380
|
+
return res.data;
|
|
381
|
+
}
|
|
382
|
+
async getAgentSession(orgId, agentId, sessionId) {
|
|
383
|
+
return this.request('GET', ENDPOINTS.AGENT_SESSION(orgId, agentId, sessionId));
|
|
384
|
+
}
|
|
385
|
+
async updateAgentSession(orgId, agentId, sessionId, req) {
|
|
386
|
+
return this.request('PATCH', ENDPOINTS.AGENT_SESSION(orgId, agentId, sessionId), req);
|
|
387
|
+
}
|
|
388
|
+
async createAgentStep(orgId, agentId, sessionId, req) {
|
|
389
|
+
return this.request('POST', ENDPOINTS.AGENT_SESSION_STEPS(orgId, agentId, sessionId), req);
|
|
390
|
+
}
|
|
391
|
+
async getAgentSessionSteps(orgId, agentId, sessionId, params) {
|
|
392
|
+
const res = await this.request('GET', ENDPOINTS.AGENT_SESSION_STEPS(orgId, agentId, sessionId), undefined, params);
|
|
393
|
+
return res.data;
|
|
394
|
+
}
|
|
395
|
+
/** Fetch all pending tasks (todo/in_progress) assigned to an agent. Pages automatically. */
|
|
396
|
+
async getAgentTasks(orgId, agentId) {
|
|
397
|
+
const all = [];
|
|
398
|
+
let cursor;
|
|
399
|
+
do {
|
|
400
|
+
const params = { limit: '100' };
|
|
401
|
+
if (cursor)
|
|
402
|
+
params.cursor = cursor;
|
|
403
|
+
const res = await this.request('GET', ENDPOINTS.AGENT_TASKS(orgId, agentId), undefined, params);
|
|
404
|
+
all.push(...res.data);
|
|
405
|
+
cursor = res.has_more ? res.next_cursor : undefined;
|
|
406
|
+
} while (cursor);
|
|
407
|
+
return all;
|
|
408
|
+
}
|
|
409
|
+
// ---- Machines (org-scoped) ----
|
|
410
|
+
async getMachines(orgId) {
|
|
411
|
+
const res = await this.request('GET', ENDPOINTS.MACHINES(orgId));
|
|
412
|
+
return res.data;
|
|
413
|
+
}
|
|
414
|
+
async getMachine(orgId, machineId) {
|
|
415
|
+
return this.request('GET', ENDPOINTS.MACHINE(orgId, machineId));
|
|
416
|
+
}
|
|
417
|
+
async deleteMachine(orgId, machineId) {
|
|
418
|
+
return this.request('DELETE', ENDPOINTS.MACHINE(orgId, machineId));
|
|
419
|
+
}
|
|
420
|
+
async startMachine(orgId, machineId) {
|
|
421
|
+
return this.request('POST', ENDPOINTS.MACHINE_START(orgId, machineId));
|
|
422
|
+
}
|
|
423
|
+
async stopMachine(orgId, machineId) {
|
|
424
|
+
return this.request('POST', ENDPOINTS.MACHINE_STOP(orgId, machineId));
|
|
425
|
+
}
|
|
426
|
+
async getMachineStatus(orgId, machineId) {
|
|
427
|
+
return this.request('GET', ENDPOINTS.MACHINE_STATUS(orgId, machineId));
|
|
428
|
+
}
|
|
429
|
+
async getMachineLogs(orgId, machineId, lines = 100) {
|
|
430
|
+
return this.request('GET', ENDPOINTS.MACHINE_LOGS(orgId, machineId), undefined, { lines });
|
|
431
|
+
}
|
|
432
|
+
async restartMachine(orgId, machineId) {
|
|
433
|
+
return this.request('POST', ENDPOINTS.MACHINE_RESTART(orgId, machineId));
|
|
434
|
+
}
|
|
435
|
+
async restartAllMachines(orgId) {
|
|
436
|
+
return this.request('POST', ENDPOINTS.MACHINE_RESTART_ALL(orgId));
|
|
437
|
+
}
|
|
438
|
+
async updateMachineWorkspace(orgId, machineId, files) {
|
|
439
|
+
return this.request('PUT', ENDPOINTS.MACHINE_WORKSPACE(orgId, machineId), files);
|
|
440
|
+
}
|
|
441
|
+
// Wiki mount/token methods removed — wiki-service handles all wiki endpoints.
|
|
442
|
+
// ---- Unread ----
|
|
443
|
+
async getUnreadCounts() {
|
|
444
|
+
const res = await this.request('GET', ENDPOINTS.UNREAD);
|
|
445
|
+
return res.data;
|
|
446
|
+
}
|
|
447
|
+
async markRead(orgId, chatId, messageId) {
|
|
448
|
+
return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
|
|
449
|
+
}
|
|
450
|
+
// ---- Inbox ----
|
|
451
|
+
async getInbox(orgId, params) {
|
|
452
|
+
return this.request('GET', ENDPOINTS.INBOX(orgId), undefined, params);
|
|
453
|
+
}
|
|
454
|
+
async getInboxUnreadCount(orgId) {
|
|
455
|
+
return this.request('GET', ENDPOINTS.INBOX_UNREAD_COUNT(orgId));
|
|
456
|
+
}
|
|
457
|
+
async markInboxRead(orgId, id) {
|
|
458
|
+
return this.request('PATCH', ENDPOINTS.INBOX_ITEM_READ(orgId, id));
|
|
459
|
+
}
|
|
460
|
+
async archiveInboxItem(orgId, id) {
|
|
461
|
+
return this.request('PATCH', ENDPOINTS.INBOX_ITEM_ARCHIVE(orgId, id));
|
|
462
|
+
}
|
|
463
|
+
// snoozeInboxItem / unsnoozeInboxItem deferred until un-snooze cron worker is implemented
|
|
464
|
+
async markAllInboxRead(orgId) {
|
|
465
|
+
return this.request('POST', ENDPOINTS.INBOX_MARK_ALL_READ(orgId));
|
|
466
|
+
}
|
|
467
|
+
async archiveAllInbox(orgId) {
|
|
468
|
+
return this.request('POST', ENDPOINTS.INBOX_ARCHIVE_ALL(orgId));
|
|
469
|
+
}
|
|
470
|
+
/** Mark an inbox item as read by its source (source_type + source_id) rather than inbox item ID. */
|
|
471
|
+
async ackInbox(orgId, source) {
|
|
472
|
+
return this.request('POST', ENDPOINTS.INBOX_ACK(orgId), source);
|
|
473
|
+
}
|
|
474
|
+
async deleteInboxItem(orgId, id) {
|
|
475
|
+
return this.request('DELETE', ENDPOINTS.INBOX_ITEM(orgId, id));
|
|
476
|
+
}
|
|
477
|
+
// ---- Dispatch (agent event delivery) ----
|
|
478
|
+
async getDispatch(orgId, params) {
|
|
479
|
+
return this.request('GET', ENDPOINTS.DISPATCH(orgId), undefined, params);
|
|
480
|
+
}
|
|
481
|
+
async getDispatchPendingCount(orgId) {
|
|
482
|
+
return this.request('GET', ENDPOINTS.DISPATCH_PENDING_COUNT(orgId));
|
|
483
|
+
}
|
|
484
|
+
async ackDispatch(orgId, source) {
|
|
485
|
+
return this.request('POST', ENDPOINTS.DISPATCH_ACK(orgId), source);
|
|
486
|
+
}
|
|
487
|
+
async ackDispatchByID(orgId, id) {
|
|
488
|
+
return this.request('POST', ENDPOINTS.DISPATCH_ACK_BY_ID(orgId, id));
|
|
489
|
+
}
|
|
490
|
+
// ---- Platform Config ----
|
|
491
|
+
/**
|
|
492
|
+
* Fetch platform config for the authenticated agent.
|
|
493
|
+
* Pass currentVersion to enable conditional fetch (If-None-Match / 304).
|
|
494
|
+
* Returns null when the server responds with 304 (config unchanged).
|
|
495
|
+
*/
|
|
496
|
+
async getPlatformConfig(currentVersion) {
|
|
497
|
+
const url = `${this.baseUrl}${ENDPOINTS.PLATFORM_CONFIG}`;
|
|
498
|
+
const extra = {};
|
|
499
|
+
if (currentVersion !== undefined) {
|
|
500
|
+
extra['If-None-Match'] = currentVersion;
|
|
501
|
+
}
|
|
502
|
+
const headers = this.buildHeaders(extra);
|
|
503
|
+
const res = await fetch(url, {
|
|
504
|
+
method: 'GET',
|
|
505
|
+
headers,
|
|
506
|
+
signal: AbortSignal.timeout(15_000),
|
|
507
|
+
});
|
|
508
|
+
if (res.status === 304)
|
|
509
|
+
return null;
|
|
510
|
+
if (!res.ok) {
|
|
511
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
512
|
+
const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error : undefined;
|
|
513
|
+
const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
|
|
514
|
+
const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
|
|
515
|
+
throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
|
|
516
|
+
}
|
|
517
|
+
return res.json();
|
|
518
|
+
}
|
|
519
|
+
// ---- Tasks (org-scoped) ----
|
|
520
|
+
async createTask(orgId, data) {
|
|
521
|
+
return this.request('POST', ENDPOINTS.TASKS(orgId), data);
|
|
522
|
+
}
|
|
523
|
+
async getTasks(orgId, params) {
|
|
524
|
+
return this.request('GET', ENDPOINTS.TASKS(orgId), undefined, params);
|
|
525
|
+
}
|
|
526
|
+
async getTask(orgId, taskId) {
|
|
527
|
+
return this.request('GET', ENDPOINTS.TASK(orgId, taskId));
|
|
528
|
+
}
|
|
529
|
+
async updateTask(orgId, taskId, data) {
|
|
530
|
+
return this.request('PATCH', ENDPOINTS.TASK(orgId, taskId), data);
|
|
531
|
+
}
|
|
532
|
+
async deleteTask(orgId, taskId) {
|
|
533
|
+
return this.request('DELETE', ENDPOINTS.TASK(orgId, taskId));
|
|
534
|
+
}
|
|
535
|
+
async getSubtasks(orgId, taskId) {
|
|
536
|
+
const res = await this.request('GET', ENDPOINTS.TASK_SUBTASKS(orgId, taskId));
|
|
537
|
+
return res.data;
|
|
538
|
+
}
|
|
539
|
+
async createTaskRelation(orgId, taskId, data) {
|
|
540
|
+
return this.request('POST', ENDPOINTS.TASK_RELATIONS(orgId, taskId), data);
|
|
541
|
+
}
|
|
542
|
+
async getTaskRelations(orgId, taskId) {
|
|
543
|
+
const res = await this.request('GET', ENDPOINTS.TASK_RELATIONS(orgId, taskId));
|
|
544
|
+
return res.data;
|
|
545
|
+
}
|
|
546
|
+
async deleteTaskRelation(orgId, taskId, relationId) {
|
|
547
|
+
return this.request('DELETE', ENDPOINTS.TASK_RELATION(orgId, taskId, relationId));
|
|
548
|
+
}
|
|
549
|
+
// ---- Task Comments ----
|
|
550
|
+
async getTaskComments(orgId, taskId, params) {
|
|
551
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENTS(orgId, taskId), undefined, params);
|
|
552
|
+
}
|
|
553
|
+
async getTaskComment(orgId, taskId, commentId) {
|
|
554
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
555
|
+
}
|
|
556
|
+
async createTaskComment(orgId, taskId, data) {
|
|
557
|
+
return this.request('POST', ENDPOINTS.TASK_COMMENTS(orgId, taskId), data);
|
|
558
|
+
}
|
|
559
|
+
async updateTaskComment(orgId, taskId, commentId, data) {
|
|
560
|
+
return this.request('PATCH', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId), data);
|
|
561
|
+
}
|
|
562
|
+
async deleteTaskComment(orgId, taskId, commentId) {
|
|
563
|
+
return this.request('DELETE', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
564
|
+
}
|
|
565
|
+
// ---- Task Activities ----
|
|
566
|
+
async getTaskActivities(orgId, taskId, params) {
|
|
567
|
+
return this.request('GET', ENDPOINTS.TASK_ACTIVITIES(orgId, taskId), undefined, params);
|
|
568
|
+
}
|
|
569
|
+
// ---- Projects (org-scoped) ----
|
|
570
|
+
async createProject(orgId, data) {
|
|
571
|
+
return this.request('POST', ENDPOINTS.PROJECTS(orgId), data);
|
|
572
|
+
}
|
|
573
|
+
async getProjects(orgId) {
|
|
574
|
+
const res = await this.request('GET', ENDPOINTS.PROJECTS(orgId));
|
|
575
|
+
return res.data;
|
|
576
|
+
}
|
|
577
|
+
async getProject(orgId, projectId) {
|
|
578
|
+
return this.request('GET', ENDPOINTS.PROJECT(orgId, projectId));
|
|
579
|
+
}
|
|
580
|
+
async updateProject(orgId, projectId, data) {
|
|
581
|
+
return this.request('PATCH', ENDPOINTS.PROJECT(orgId, projectId), data);
|
|
582
|
+
}
|
|
583
|
+
async deleteProject(orgId, projectId) {
|
|
584
|
+
return this.request('DELETE', ENDPOINTS.PROJECT(orgId, projectId));
|
|
585
|
+
}
|
|
586
|
+
// ---- Wikis (org-scoped) ----
|
|
587
|
+
async createWiki(orgId, data) {
|
|
588
|
+
return this.request('POST', ENDPOINTS.WIKIS(orgId), data);
|
|
589
|
+
}
|
|
590
|
+
async getWikis(orgId) {
|
|
591
|
+
const res = await this.request('GET', ENDPOINTS.WIKIS(orgId));
|
|
592
|
+
return res.data;
|
|
593
|
+
}
|
|
594
|
+
async getWiki(orgId, wikiId) {
|
|
595
|
+
return this.request('GET', ENDPOINTS.WIKI(orgId, wikiId));
|
|
596
|
+
}
|
|
597
|
+
async getWikiTree(orgId, wikiId, params) {
|
|
598
|
+
return this.request('GET', ENDPOINTS.WIKI_TREE(orgId, wikiId), undefined, params);
|
|
599
|
+
}
|
|
600
|
+
async getWikiBlob(orgId, wikiId, params) {
|
|
601
|
+
return this.request('GET', ENDPOINTS.WIKI_BLOB(orgId, wikiId), undefined, params);
|
|
602
|
+
}
|
|
603
|
+
async getWikiNodeSections(orgId, wikiId, params) {
|
|
604
|
+
return this.request('GET', ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), undefined, params);
|
|
605
|
+
}
|
|
606
|
+
async searchWiki(orgId, wikiId, params) {
|
|
607
|
+
return this.request('GET', ENDPOINTS.WIKI_SEARCH(orgId, wikiId), undefined, params);
|
|
608
|
+
}
|
|
609
|
+
async getWikiPageIndex(orgId, wikiId, params) {
|
|
610
|
+
return this.request('GET', ENDPOINTS.WIKI_PAGE_INDEX(orgId, wikiId), undefined, params);
|
|
611
|
+
}
|
|
612
|
+
async getWikiRefs(orgId, wikiId, params) {
|
|
613
|
+
return this.request('GET', ENDPOINTS.WIKI_REFS(orgId, wikiId), undefined, params);
|
|
614
|
+
}
|
|
615
|
+
async checkWikiRefs(orgId, wikiId, params) {
|
|
616
|
+
return this.request('GET', ENDPOINTS.WIKI_REFS_CHECK(orgId, wikiId), undefined, params);
|
|
617
|
+
}
|
|
618
|
+
async createWikiChangeset(orgId, wikiId, data) {
|
|
619
|
+
return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId), data));
|
|
620
|
+
}
|
|
621
|
+
async getWikiChangesets(orgId, wikiId) {
|
|
622
|
+
const res = await this.request('GET', ENDPOINTS.WIKI_CHANGESETS(orgId, wikiId));
|
|
623
|
+
return res.data.map(normalizeWikiChangeset);
|
|
624
|
+
}
|
|
625
|
+
async getWikiChangeset(orgId, wikiId, changesetId) {
|
|
626
|
+
return normalizeWikiChangeset(await this.request('GET', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId)));
|
|
627
|
+
}
|
|
628
|
+
async updateWikiChangeset(orgId, wikiId, changesetId, data) {
|
|
629
|
+
return normalizeWikiChangeset(await this.request('PATCH', ENDPOINTS.WIKI_CHANGESET(orgId, wikiId, changesetId), data));
|
|
630
|
+
}
|
|
631
|
+
async getWikiChangesetDiff(orgId, wikiId, changesetId) {
|
|
632
|
+
return this.request('GET', ENDPOINTS.WIKI_CHANGESET_DIFF(orgId, wikiId, changesetId));
|
|
633
|
+
}
|
|
634
|
+
async mergeWikiChangeset(orgId, wikiId, changesetId) {
|
|
635
|
+
return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESET_MERGE(orgId, wikiId, changesetId)));
|
|
636
|
+
}
|
|
637
|
+
async closeWikiChangeset(orgId, wikiId, changesetId) {
|
|
638
|
+
return normalizeWikiChangeset(await this.request('POST', ENDPOINTS.WIKI_CHANGESET_CLOSE(orgId, wikiId, changesetId)));
|
|
639
|
+
}
|
|
640
|
+
// ---- Wiki Path Scopes (AFCS ACL) ----
|
|
641
|
+
async getWikiPathScopes(orgId, wikiId) {
|
|
642
|
+
const res = await this.request('GET', ENDPOINTS.WIKI_PATH_SCOPES(orgId, wikiId));
|
|
643
|
+
return res.data;
|
|
644
|
+
}
|
|
645
|
+
async createWikiPathScope(orgId, wikiId, data) {
|
|
646
|
+
return this.request('POST', ENDPOINTS.WIKI_PATH_SCOPES(orgId, wikiId), data);
|
|
647
|
+
}
|
|
648
|
+
async deleteWikiPathScope(orgId, wikiId, scopeId) {
|
|
649
|
+
await this.request('DELETE', ENDPOINTS.WIKI_PATH_SCOPE(orgId, wikiId, scopeId));
|
|
650
|
+
}
|
|
651
|
+
async getWikiAccessStatus(orgId, wikiId, path) {
|
|
652
|
+
return this.request('GET', ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), undefined, path ? { path } : undefined);
|
|
653
|
+
}
|
|
654
|
+
async createWikiAccessRequest(orgId, wikiId, data) {
|
|
655
|
+
await this.request('POST', ENDPOINTS.WIKI_ACCESS_REQUESTS(orgId, wikiId), data);
|
|
656
|
+
}
|
|
657
|
+
// ---- Wiki History (commits, file commits, blame) ----
|
|
658
|
+
async getWikiCommits(orgId, wikiId, params) {
|
|
659
|
+
return this.request('GET', ENDPOINTS.WIKI_COMMITS(orgId, wikiId), undefined, params);
|
|
660
|
+
}
|
|
661
|
+
async getWikiFileCommits(orgId, wikiId, path, params) {
|
|
662
|
+
return this.request('GET', ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), undefined, { path, ...params });
|
|
663
|
+
}
|
|
664
|
+
async getWikiBlame(orgId, wikiId, path, ref) {
|
|
665
|
+
return this.request('GET', ENDPOINTS.WIKI_BLAME(orgId, wikiId), undefined, { path, ref });
|
|
666
|
+
}
|
|
667
|
+
// ---- Wiki Operations (audit log) ----
|
|
668
|
+
async getWikiOperations(orgId, wikiId, params) {
|
|
669
|
+
return this.request('GET', ENDPOINTS.WIKI_OPERATIONS(orgId, wikiId), undefined, params);
|
|
670
|
+
}
|
|
671
|
+
async revertWikiOperation(orgId, wikiId, opId) {
|
|
672
|
+
return this.request('POST', ENDPOINTS.WIKI_OPERATION_REVERT(orgId, wikiId, opId));
|
|
673
|
+
}
|
|
674
|
+
// ---- References ----
|
|
675
|
+
async resolveRefs(orgId, refs) {
|
|
676
|
+
return this.request('POST', ENDPOINTS.REFS_RESOLVE(orgId), { refs });
|
|
677
|
+
}
|
|
678
|
+
async getBacklinks(orgId, params) {
|
|
679
|
+
return this.request('GET', ENDPOINTS.REFS_BACKLINKS(orgId), undefined, params);
|
|
680
|
+
}
|
|
681
|
+
async checkBrokenRefs(orgId) {
|
|
682
|
+
return this.request('GET', ENDPOINTS.REFS_CHECK(orgId));
|
|
683
|
+
}
|
|
684
|
+
// ---- Push Notifications ----
|
|
685
|
+
async subscribePush(body) {
|
|
686
|
+
return this.request('POST', ENDPOINTS.PUSH_SUBSCRIBE, body);
|
|
687
|
+
}
|
|
688
|
+
async unsubscribePush(token) {
|
|
689
|
+
return this.request('DELETE', ENDPOINTS.PUSH_UNSUBSCRIBE, { token });
|
|
690
|
+
}
|
|
691
|
+
async getVapidKey() {
|
|
692
|
+
return this.request('GET', ENDPOINTS.PUSH_VAPID_KEY);
|
|
693
|
+
}
|
|
694
|
+
// ---- Notification Preferences ----
|
|
695
|
+
async getNotificationPreferences() {
|
|
696
|
+
return this.request('GET', ENDPOINTS.NOTIFICATION_PREFERENCES);
|
|
697
|
+
}
|
|
698
|
+
async updateNotificationPreferences(prefs) {
|
|
699
|
+
return this.request('PATCH', ENDPOINTS.NOTIFICATION_PREFERENCES, { prefs });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function normalizeWikiChangeset(changeset) {
|
|
703
|
+
return {
|
|
704
|
+
...changeset,
|
|
705
|
+
changed_paths: changeset.changed_paths ?? [],
|
|
706
|
+
file_changes: changeset.file_changes ?? [],
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
export class ApiError extends Error {
|
|
710
|
+
status;
|
|
711
|
+
code;
|
|
712
|
+
constructor(status, message, code) {
|
|
713
|
+
super(message);
|
|
714
|
+
this.status = status;
|
|
715
|
+
this.code = code;
|
|
716
|
+
this.name = 'ApiError';
|
|
717
|
+
}
|
|
718
|
+
}
|