@pnds/sdk 0.1.1 → 0.2.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 +74 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +211 -25
- package/dist/constants.d.ts +40 -7
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +47 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +255 -29
- package/dist/types.d.ts.map +1 -1
- package/dist/ws.d.ts +24 -0
- package/dist/ws.d.ts.map +1 -1
- package/dist/ws.js +27 -0
- package/package.json +1 -1
- package/src/client.ts +278 -28
- package/src/constants.ts +65 -14
- package/src/index.ts +1 -1
- package/src/types.ts +310 -34
- package/src/ws.ts +31 -0
package/dist/ws.d.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import type { WsEventMap, WsTicketResponse } from './types.js';
|
|
2
2
|
export type WsEventType = keyof WsEventMap;
|
|
3
3
|
export type WsEventHandler<T extends WsEventType> = (data: WsEventMap[T], seq?: number) => void;
|
|
4
|
+
export type WsClientEventMap = {
|
|
5
|
+
'ping': {
|
|
6
|
+
ts: number;
|
|
7
|
+
};
|
|
8
|
+
'typing': {
|
|
9
|
+
chat_id: string;
|
|
10
|
+
thread_root_id: string | null;
|
|
11
|
+
action: 'start' | 'stop';
|
|
12
|
+
};
|
|
13
|
+
'ack': {
|
|
14
|
+
message_id: string;
|
|
15
|
+
};
|
|
16
|
+
'read': {
|
|
17
|
+
chat_id: string;
|
|
18
|
+
last_read_id: string;
|
|
19
|
+
};
|
|
20
|
+
'watch': {
|
|
21
|
+
chat_ids: string[];
|
|
22
|
+
};
|
|
23
|
+
'agent.heartbeat': {
|
|
24
|
+
session_id: string;
|
|
25
|
+
telemetry?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
4
28
|
export type WsConnectionState = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';
|
|
5
29
|
export interface PondWsOptions {
|
|
6
30
|
/** Async function that fetches a one-time WS ticket from the API. */
|
package/dist/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAW,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExE,MAAM,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC;AAC3C,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,WAAW,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhG,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;AAE7F,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,SAAS,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3C,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAkG;IACjH,OAAO,CAAC,SAAS,CAAuD;IACxE,OAAO,CAAC,cAAc,CAAiD;IACvE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAqC;IACnD,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,EAAE,aAAa;IAUlC,IAAI,KAAK,IAAI,iBAAiB,CAE7B;IAEK,OAAO;
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"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"}
|
package/dist/ws.js
CHANGED
|
@@ -50,7 +50,33 @@ export class PondWs {
|
|
|
50
50
|
params.set('last_seq', String(this.lastSeq));
|
|
51
51
|
}
|
|
52
52
|
this.ws = new WebSocket(`${wsUrl}?${params.toString()}`);
|
|
53
|
+
// Force-reconnect if not connected within 15s.
|
|
54
|
+
// Handles edge case where Node 22 WebSocket transitions to CLOSED without
|
|
55
|
+
// firing onclose (e.g. TCP reset during Cloudflare rolling update).
|
|
56
|
+
const ws = this.ws;
|
|
57
|
+
const connectTimeout = setTimeout(() => {
|
|
58
|
+
if (this._state === 'connected' || this.intentionalClose)
|
|
59
|
+
return;
|
|
60
|
+
ws.onclose = null;
|
|
61
|
+
ws.onopen = null;
|
|
62
|
+
ws.onerror = null;
|
|
63
|
+
try {
|
|
64
|
+
ws.close();
|
|
65
|
+
}
|
|
66
|
+
catch { /* ignore */ }
|
|
67
|
+
// Only reconnect if this socket is still the active one.
|
|
68
|
+
// If connect() was called again, a newer socket owns the lifecycle.
|
|
69
|
+
if (ws !== this.ws)
|
|
70
|
+
return;
|
|
71
|
+
if (this.options.reconnect) {
|
|
72
|
+
this.scheduleReconnect();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.setState('disconnected');
|
|
76
|
+
}
|
|
77
|
+
}, 15_000);
|
|
53
78
|
this.ws.onopen = () => {
|
|
79
|
+
clearTimeout(connectTimeout);
|
|
54
80
|
this.reconnectAttempts = 0;
|
|
55
81
|
this.setState('connected');
|
|
56
82
|
};
|
|
@@ -64,6 +90,7 @@ export class PondWs {
|
|
|
64
90
|
}
|
|
65
91
|
};
|
|
66
92
|
this.ws.onclose = () => {
|
|
93
|
+
clearTimeout(connectTimeout);
|
|
67
94
|
this.stopHeartbeat();
|
|
68
95
|
if (this.intentionalClose) {
|
|
69
96
|
this.setState('disconnected');
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ENDPOINTS } from './constants.js';
|
|
|
2
2
|
import type {
|
|
3
3
|
AuthTokens,
|
|
4
4
|
RegisterRequest,
|
|
5
|
+
RegisterResponse,
|
|
5
6
|
LoginRequest,
|
|
6
7
|
CheckEmailResponse,
|
|
7
8
|
User,
|
|
@@ -19,35 +20,64 @@ import type {
|
|
|
19
20
|
Approval,
|
|
20
21
|
PresignResponse,
|
|
21
22
|
PresignUploadRequest,
|
|
23
|
+
FileUrlResponse,
|
|
22
24
|
CreateAgentRequest,
|
|
23
25
|
CreateAgentResponse,
|
|
24
26
|
AgentWithRuntime,
|
|
25
27
|
ApiKey,
|
|
26
28
|
WsTicketResponse,
|
|
27
|
-
|
|
29
|
+
Machine,
|
|
28
30
|
WorkspaceFiles,
|
|
29
31
|
Task,
|
|
30
32
|
CreateTaskRequest,
|
|
31
33
|
UpdateTaskRequest,
|
|
32
34
|
TaskRelation,
|
|
33
35
|
CreateTaskRelationRequest,
|
|
36
|
+
TaskComment,
|
|
37
|
+
CreateTaskCommentRequest,
|
|
38
|
+
UpdateTaskCommentRequest,
|
|
39
|
+
TaskActivity,
|
|
40
|
+
Project,
|
|
41
|
+
CreateProjectRequest,
|
|
42
|
+
UpdateProjectRequest,
|
|
43
|
+
AgentRunDB,
|
|
44
|
+
AgentStep,
|
|
45
|
+
CreateAgentRunRequest,
|
|
46
|
+
CreateAgentStepRequest,
|
|
47
|
+
UpdateAgentRunRequest,
|
|
48
|
+
OrgInvitation,
|
|
49
|
+
InvitationPublicInfo,
|
|
50
|
+
PlatformConfigResponse,
|
|
34
51
|
} from './types.js';
|
|
35
52
|
|
|
36
53
|
export interface PondClientOptions {
|
|
37
54
|
baseUrl?: string;
|
|
38
55
|
token?: string;
|
|
39
56
|
onTokenExpired?: () => void;
|
|
57
|
+
getRefreshToken?: () => string | null;
|
|
58
|
+
setTokens?: (tokens: { access_token: string; refresh_token: string }) => void;
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
export class PondClient {
|
|
43
62
|
private baseUrl: string;
|
|
44
63
|
private token: string | null;
|
|
45
64
|
private onTokenExpired?: () => void;
|
|
65
|
+
private getRefreshToken?: () => string | null;
|
|
66
|
+
private setTokens?: (tokens: { access_token: string; refresh_token: string }) => void;
|
|
67
|
+
private refreshPromise: Promise<AuthTokens> | null = null;
|
|
68
|
+
|
|
69
|
+
/** Auth endpoints excluded from automatic 401 refresh to prevent recursion. */
|
|
70
|
+
private static readonly AUTH_PATHS = new Set([
|
|
71
|
+
'/auth/login', '/auth/register', '/auth/refresh',
|
|
72
|
+
'/auth/verify-email', '/auth/resend-code', '/auth/check-email',
|
|
73
|
+
]);
|
|
46
74
|
|
|
47
75
|
constructor(options: PondClientOptions = {}) {
|
|
48
76
|
this.baseUrl = options.baseUrl ?? '';
|
|
49
77
|
this.token = options.token ?? null;
|
|
50
78
|
this.onTokenExpired = options.onTokenExpired;
|
|
79
|
+
this.getRefreshToken = options.getRefreshToken;
|
|
80
|
+
this.setTokens = options.setTokens;
|
|
51
81
|
}
|
|
52
82
|
|
|
53
83
|
setToken(token: string | null) {
|
|
@@ -58,11 +88,36 @@ export class PondClient {
|
|
|
58
88
|
return this.token;
|
|
59
89
|
}
|
|
60
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Single-flight token refresh. Multiple concurrent 401s share one promise
|
|
93
|
+
* so only one refresh request is made.
|
|
94
|
+
*/
|
|
95
|
+
private async tryRefresh(): Promise<boolean> {
|
|
96
|
+
const rt = this.getRefreshToken?.();
|
|
97
|
+
if (!rt) return false;
|
|
98
|
+
|
|
99
|
+
if (!this.refreshPromise) {
|
|
100
|
+
this.refreshPromise = this.refreshToken(rt);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const tokens = await this.refreshPromise;
|
|
105
|
+
this.setToken(tokens.access_token);
|
|
106
|
+
this.setTokens?.(tokens);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
} finally {
|
|
111
|
+
this.refreshPromise = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
61
115
|
private async request<T>(
|
|
62
116
|
method: string,
|
|
63
117
|
path: string,
|
|
64
118
|
body?: unknown,
|
|
65
119
|
query?: Record<string, string | number | boolean | undefined>,
|
|
120
|
+
retried = false,
|
|
66
121
|
): Promise<T> {
|
|
67
122
|
let url = `${this.baseUrl}${path}`;
|
|
68
123
|
|
|
@@ -84,14 +139,36 @@ export class PondClient {
|
|
|
84
139
|
headers['Authorization'] = `Bearer ${this.token}`;
|
|
85
140
|
}
|
|
86
141
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
142
|
+
let res: Response;
|
|
143
|
+
try {
|
|
144
|
+
res = await fetch(url, {
|
|
145
|
+
method,
|
|
146
|
+
headers,
|
|
147
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
148
|
+
signal: AbortSignal.timeout(15_000),
|
|
149
|
+
});
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err instanceof DOMException && err.name === 'TimeoutError') {
|
|
152
|
+
throw new ApiError(0, 'Request timed out', 'REQUEST_TIMEOUT');
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
92
156
|
|
|
93
|
-
if (res.status === 401
|
|
94
|
-
|
|
157
|
+
if (res.status === 401) {
|
|
158
|
+
const pathSuffix = path.replace(/^\/api\/v1/, '');
|
|
159
|
+
const isAuthPath = PondClient.AUTH_PATHS.has(pathSuffix);
|
|
160
|
+
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
161
|
+
const refreshed = await this.tryRefresh();
|
|
162
|
+
if (refreshed) {
|
|
163
|
+
// Retry once with the new token
|
|
164
|
+
return this.request<T>(method, path, body, query, true);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Only fire for non-auth paths — auth endpoints (e.g. /auth/refresh)
|
|
168
|
+
// just throw; the original caller's path handles the notification.
|
|
169
|
+
if (this.onTokenExpired && !isAuthPath) {
|
|
170
|
+
this.onTokenExpired();
|
|
171
|
+
}
|
|
95
172
|
}
|
|
96
173
|
|
|
97
174
|
if (!res.ok) {
|
|
@@ -108,7 +185,7 @@ export class PondClient {
|
|
|
108
185
|
|
|
109
186
|
// ---- Auth ----
|
|
110
187
|
|
|
111
|
-
async register(req: RegisterRequest): Promise<
|
|
188
|
+
async register(req: RegisterRequest): Promise<RegisterResponse> {
|
|
112
189
|
return this.request('POST', ENDPOINTS.AUTH_REGISTER, req);
|
|
113
190
|
}
|
|
114
191
|
|
|
@@ -128,7 +205,7 @@ export class PondClient {
|
|
|
128
205
|
return this.request('POST', ENDPOINTS.AUTH_CHECK_EMAIL, { email });
|
|
129
206
|
}
|
|
130
207
|
|
|
131
|
-
async verifyEmail(email: string, code: string): Promise<
|
|
208
|
+
async verifyEmail(email: string, code: string): Promise<AuthTokens> {
|
|
132
209
|
return this.request('POST', ENDPOINTS.AUTH_VERIFY_EMAIL, { email, code });
|
|
133
210
|
}
|
|
134
211
|
|
|
@@ -189,10 +266,6 @@ export class PondClient {
|
|
|
189
266
|
return res.data;
|
|
190
267
|
}
|
|
191
268
|
|
|
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
269
|
async removeOrgMember(orgId: string, userId: string): Promise<void> {
|
|
197
270
|
return this.request('DELETE', ENDPOINTS.ORG_MEMBER(orgId, userId));
|
|
198
271
|
}
|
|
@@ -201,6 +274,42 @@ export class PondClient {
|
|
|
201
274
|
return this.request('PATCH', ENDPOINTS.ORG_MEMBER(orgId, userId), { role });
|
|
202
275
|
}
|
|
203
276
|
|
|
277
|
+
// ---- Invitations ----
|
|
278
|
+
|
|
279
|
+
async createInvitation(orgId: string, email: string, role?: OrgMemberRole): Promise<OrgInvitation> {
|
|
280
|
+
return this.request('POST', ENDPOINTS.ORG_INVITATIONS(orgId), { email, role });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getOrgInvitations(orgId: string): Promise<OrgInvitation[]> {
|
|
284
|
+
const res = await this.request<{ data: OrgInvitation[] }>('GET', ENDPOINTS.ORG_INVITATIONS(orgId));
|
|
285
|
+
return res.data;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async revokeInvitation(orgId: string, invId: string): Promise<void> {
|
|
289
|
+
return this.request('DELETE', ENDPOINTS.ORG_INVITATION(orgId, invId));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async resendInvitation(orgId: string, invId: string): Promise<void> {
|
|
293
|
+
return this.request('POST', ENDPOINTS.ORG_INVITATION_RESEND(orgId, invId));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async getMyInvitations(): Promise<OrgInvitation[]> {
|
|
297
|
+
const res = await this.request<{ data: OrgInvitation[] }>('GET', ENDPOINTS.MY_INVITATIONS);
|
|
298
|
+
return res.data;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async acceptInvitation(id: string): Promise<OrgMember> {
|
|
302
|
+
return this.request('POST', ENDPOINTS.INVITATION_ACCEPT(id));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async declineInvitation(id: string): Promise<void> {
|
|
306
|
+
return this.request('POST', ENDPOINTS.INVITATION_DECLINE(id));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async getInvitationByToken(token: string): Promise<InvitationPublicInfo> {
|
|
310
|
+
return this.request('GET', ENDPOINTS.INVITATION_BY_TOKEN(token));
|
|
311
|
+
}
|
|
312
|
+
|
|
204
313
|
// ---- Chats (org-scoped) ----
|
|
205
314
|
|
|
206
315
|
async createChat(orgId: string, req: Omit<CreateChatRequest, 'org_id'>): Promise<Chat> {
|
|
@@ -287,12 +396,16 @@ export class PondClient {
|
|
|
287
396
|
|
|
288
397
|
// ---- File Upload ----
|
|
289
398
|
|
|
290
|
-
async getUploadPresignUrl(req: PresignUploadRequest): Promise<PresignResponse> {
|
|
291
|
-
return this.request('POST', ENDPOINTS.UPLOAD_PRESIGN, req);
|
|
399
|
+
async getUploadPresignUrl(orgId: string, req: PresignUploadRequest): Promise<PresignResponse> {
|
|
400
|
+
return this.request('POST', ENDPOINTS.UPLOAD_PRESIGN(orgId), req);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async completeUpload(orgId: string, attachmentId: string): Promise<{ attachment_id: string }> {
|
|
404
|
+
return this.request('POST', ENDPOINTS.UPLOAD_COMPLETE(orgId), { attachment_id: attachmentId });
|
|
292
405
|
}
|
|
293
406
|
|
|
294
|
-
async
|
|
295
|
-
return this.request('
|
|
407
|
+
async getFileUrl(id: string): Promise<FileUrlResponse> {
|
|
408
|
+
return this.request('GET', ENDPOINTS.FILE(id));
|
|
296
409
|
}
|
|
297
410
|
|
|
298
411
|
// ---- Approvals ----
|
|
@@ -342,24 +455,67 @@ export class PondClient {
|
|
|
342
455
|
return res.data;
|
|
343
456
|
}
|
|
344
457
|
|
|
345
|
-
|
|
346
|
-
|
|
458
|
+
// ---- Agent Runs (org-scoped) ----
|
|
459
|
+
|
|
460
|
+
async createAgentRun(orgId: string, agentId: string, req: CreateAgentRunRequest): Promise<AgentRunDB> {
|
|
461
|
+
return this.request('POST', ENDPOINTS.AGENT_RUNS(orgId, agentId), req);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async getAgentRuns(orgId: string, agentId: string, params?: { limit?: number }): Promise<AgentRunDB[]> {
|
|
465
|
+
const res = await this.request<{ data: AgentRunDB[] }>('GET', ENDPOINTS.AGENT_RUNS(orgId, agentId), undefined, params);
|
|
466
|
+
return res.data;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async getAgentRun(orgId: string, agentId: string, runId: string): Promise<AgentRunDB> {
|
|
470
|
+
return this.request('GET', ENDPOINTS.AGENT_RUN(orgId, agentId, runId));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async updateAgentRun(orgId: string, agentId: string, runId: string, req: UpdateAgentRunRequest): Promise<AgentRunDB> {
|
|
474
|
+
return this.request('PATCH', ENDPOINTS.AGENT_RUN(orgId, agentId, runId), req);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async createAgentStep(orgId: string, agentId: string, runId: string, req: CreateAgentStepRequest): Promise<AgentStep> {
|
|
478
|
+
return this.request('POST', ENDPOINTS.AGENT_RUN_STEPS(orgId, agentId, runId), req);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async getAgentRunSteps(orgId: string, agentId: string, runId: string, params?: { limit?: number }): Promise<AgentStep[]> {
|
|
482
|
+
const res = await this.request<{ data: AgentStep[] }>('GET', ENDPOINTS.AGENT_RUN_STEPS(orgId, agentId, runId), undefined, params);
|
|
483
|
+
return res.data;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ---- Machines (org-scoped) ----
|
|
487
|
+
|
|
488
|
+
async getMachines(orgId: string): Promise<Machine[]> {
|
|
489
|
+
const res = await this.request<{ data: Machine[] }>('GET', ENDPOINTS.MACHINES(orgId));
|
|
490
|
+
return res.data;
|
|
347
491
|
}
|
|
348
492
|
|
|
349
|
-
async
|
|
350
|
-
return this.request('
|
|
493
|
+
async getMachine(orgId: string, machineId: string): Promise<Machine> {
|
|
494
|
+
return this.request('GET', ENDPOINTS.MACHINE(orgId, machineId));
|
|
351
495
|
}
|
|
352
496
|
|
|
353
|
-
async
|
|
354
|
-
return this.request('
|
|
497
|
+
async deleteMachine(orgId: string, machineId: string): Promise<void> {
|
|
498
|
+
return this.request('DELETE', ENDPOINTS.MACHINE(orgId, machineId));
|
|
355
499
|
}
|
|
356
500
|
|
|
357
|
-
async
|
|
358
|
-
return this.request('
|
|
501
|
+
async startMachine(orgId: string, machineId: string): Promise<void> {
|
|
502
|
+
return this.request('POST', ENDPOINTS.MACHINE_START(orgId, machineId));
|
|
359
503
|
}
|
|
360
504
|
|
|
361
|
-
async
|
|
362
|
-
return this.request('
|
|
505
|
+
async stopMachine(orgId: string, machineId: string): Promise<void> {
|
|
506
|
+
return this.request('POST', ENDPOINTS.MACHINE_STOP(orgId, machineId));
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async getMachineStatus(orgId: string, machineId: string): Promise<{ state: string }> {
|
|
510
|
+
return this.request('GET', ENDPOINTS.MACHINE_STATUS(orgId, machineId));
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
async getMachineLogs(orgId: string, machineId: string, lines = 100): Promise<{ data: string[] }> {
|
|
514
|
+
return this.request('GET', ENDPOINTS.MACHINE_LOGS(orgId, machineId), undefined, { lines });
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async updateMachineWorkspace(orgId: string, machineId: string, files: WorkspaceFiles): Promise<void> {
|
|
518
|
+
return this.request('PUT', ENDPOINTS.MACHINE_WORKSPACE(orgId, machineId), files);
|
|
363
519
|
}
|
|
364
520
|
|
|
365
521
|
// ---- Unread ----
|
|
@@ -373,6 +529,44 @@ export class PondClient {
|
|
|
373
529
|
return this.request('POST', ENDPOINTS.CHAT_READ(orgId, chatId), { message_id: messageId });
|
|
374
530
|
}
|
|
375
531
|
|
|
532
|
+
// ---- Platform Config ----
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Fetch platform config for the authenticated agent.
|
|
536
|
+
* Pass currentVersion to enable conditional fetch (If-None-Match / 304).
|
|
537
|
+
* Returns null when the server responds with 304 (config unchanged).
|
|
538
|
+
*/
|
|
539
|
+
async getPlatformConfig(currentVersion?: string): Promise<PlatformConfigResponse | null> {
|
|
540
|
+
const url = `${this.baseUrl}${ENDPOINTS.PLATFORM_CONFIG}`;
|
|
541
|
+
const headers: Record<string, string> = {
|
|
542
|
+
'Content-Type': 'application/json',
|
|
543
|
+
};
|
|
544
|
+
if (this.token) {
|
|
545
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
546
|
+
}
|
|
547
|
+
if (currentVersion !== undefined) {
|
|
548
|
+
headers['If-None-Match'] = currentVersion;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const res = await fetch(url, {
|
|
552
|
+
method: 'GET',
|
|
553
|
+
headers,
|
|
554
|
+
signal: AbortSignal.timeout(15_000),
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
if (res.status === 304) return null;
|
|
558
|
+
|
|
559
|
+
if (!res.ok) {
|
|
560
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
561
|
+
const errorObj = errorBody?.error && typeof errorBody.error === 'object' ? errorBody.error as { message?: string; code?: string } : undefined;
|
|
562
|
+
const errMsg = errorObj?.message ?? (typeof errorBody?.error === 'string' ? errorBody.error : undefined);
|
|
563
|
+
const errCode = errorObj?.code ?? (typeof errorBody?.code === 'string' ? errorBody.code : undefined);
|
|
564
|
+
throw new ApiError(res.status, errMsg ?? res.statusText, errCode);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return res.json() as Promise<PlatformConfigResponse>;
|
|
568
|
+
}
|
|
569
|
+
|
|
376
570
|
// ---- Tasks (org-scoped) ----
|
|
377
571
|
|
|
378
572
|
async createTask(orgId: string, data: CreateTaskRequest): Promise<Task> {
|
|
@@ -384,6 +578,7 @@ export class PondClient {
|
|
|
384
578
|
priority?: string;
|
|
385
579
|
assignee_id?: string;
|
|
386
580
|
parent_id?: string;
|
|
581
|
+
project_id?: string;
|
|
387
582
|
limit?: number;
|
|
388
583
|
cursor?: string;
|
|
389
584
|
sort?: string;
|
|
@@ -421,6 +616,61 @@ export class PondClient {
|
|
|
421
616
|
async deleteTaskRelation(orgId: string, taskId: string, relationId: string): Promise<void> {
|
|
422
617
|
return this.request('DELETE', ENDPOINTS.TASK_RELATION(orgId, taskId, relationId));
|
|
423
618
|
}
|
|
619
|
+
|
|
620
|
+
// ---- Task Comments ----
|
|
621
|
+
|
|
622
|
+
async getTaskComments(orgId: string, taskId: string, params?: {
|
|
623
|
+
limit?: number; cursor?: string; order?: string;
|
|
624
|
+
}): Promise<PaginatedResponse<TaskComment>> {
|
|
625
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENTS(orgId, taskId), undefined, params);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async getTaskComment(orgId: string, taskId: string, commentId: string): Promise<TaskComment> {
|
|
629
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
async createTaskComment(orgId: string, taskId: string, data: CreateTaskCommentRequest): Promise<TaskComment> {
|
|
633
|
+
return this.request('POST', ENDPOINTS.TASK_COMMENTS(orgId, taskId), data);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async updateTaskComment(orgId: string, taskId: string, commentId: string, data: UpdateTaskCommentRequest): Promise<TaskComment> {
|
|
637
|
+
return this.request('PATCH', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId), data);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
async deleteTaskComment(orgId: string, taskId: string, commentId: string): Promise<void> {
|
|
641
|
+
return this.request('DELETE', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ---- Task Activities ----
|
|
645
|
+
|
|
646
|
+
async getTaskActivities(orgId: string, taskId: string, params?: {
|
|
647
|
+
limit?: number; cursor?: string;
|
|
648
|
+
}): Promise<PaginatedResponse<TaskActivity>> {
|
|
649
|
+
return this.request('GET', ENDPOINTS.TASK_ACTIVITIES(orgId, taskId), undefined, params);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// ---- Projects (org-scoped) ----
|
|
653
|
+
|
|
654
|
+
async createProject(orgId: string, data: CreateProjectRequest): Promise<Project> {
|
|
655
|
+
return this.request('POST', ENDPOINTS.PROJECTS(orgId), data);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async getProjects(orgId: string): Promise<Project[]> {
|
|
659
|
+
const res = await this.request<{ data: Project[] }>('GET', ENDPOINTS.PROJECTS(orgId));
|
|
660
|
+
return res.data;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
async getProject(orgId: string, projectId: string): Promise<Project> {
|
|
664
|
+
return this.request('GET', ENDPOINTS.PROJECT(orgId, projectId));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
async updateProject(orgId: string, projectId: string, data: UpdateProjectRequest): Promise<Project> {
|
|
668
|
+
return this.request('PATCH', ENDPOINTS.PROJECT(orgId, projectId), data);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
async deleteProject(orgId: string, projectId: string): Promise<void> {
|
|
672
|
+
return this.request('DELETE', ENDPOINTS.PROJECT(orgId, projectId));
|
|
673
|
+
}
|
|
424
674
|
}
|
|
425
675
|
|
|
426
676
|
export class ApiError extends Error {
|
package/src/constants.ts
CHANGED
|
@@ -43,9 +43,10 @@ export const ENDPOINTS = {
|
|
|
43
43
|
MESSAGE_PATCHES: (id: string) => `${API_BASE}/messages/${id}/patches`,
|
|
44
44
|
MESSAGE_REPLIES: (id: string) => `${API_BASE}/messages/${id}/replies`,
|
|
45
45
|
|
|
46
|
-
// Upload (
|
|
47
|
-
UPLOAD_PRESIGN: `${API_BASE}/upload/presign`,
|
|
48
|
-
UPLOAD_COMPLETE: `${API_BASE}/upload/complete`,
|
|
46
|
+
// Upload (org-scoped)
|
|
47
|
+
UPLOAD_PRESIGN: (orgId: string) => `${API_BASE}/orgs/${orgId}/upload/presign`,
|
|
48
|
+
UPLOAD_COMPLETE: (orgId: string) => `${API_BASE}/orgs/${orgId}/upload/complete`,
|
|
49
|
+
FILE: (id: string) => `${API_BASE}/files/${id}`,
|
|
49
50
|
|
|
50
51
|
// Approvals (global)
|
|
51
52
|
APPROVAL_DECIDE: (id: string) => `${API_BASE}/approvals/${id}/decide`,
|
|
@@ -62,16 +63,26 @@ export const ENDPOINTS = {
|
|
|
62
63
|
`${API_BASE}/orgs/${orgId}/agents/${agentId}/activity`,
|
|
63
64
|
AGENT_MONITOR: (orgId: string, agentId: string) =>
|
|
64
65
|
`${API_BASE}/orgs/${orgId}/agents/${agentId}/monitor`,
|
|
65
|
-
|
|
66
|
-
`${API_BASE}/orgs/${orgId}/agents/${agentId}/
|
|
67
|
-
|
|
68
|
-
`${API_BASE}/orgs/${orgId}/agents/${agentId}/
|
|
69
|
-
|
|
70
|
-
`${API_BASE}/orgs/${orgId}/agents/${agentId}/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`${API_BASE}/orgs/${orgId}/
|
|
66
|
+
AGENT_RUNS: (orgId: string, agentId: string) =>
|
|
67
|
+
`${API_BASE}/orgs/${orgId}/agents/${agentId}/runs`,
|
|
68
|
+
AGENT_RUN: (orgId: string, agentId: string, runId: string) =>
|
|
69
|
+
`${API_BASE}/orgs/${orgId}/agents/${agentId}/runs/${runId}`,
|
|
70
|
+
AGENT_RUN_STEPS: (orgId: string, agentId: string, runId: string) =>
|
|
71
|
+
`${API_BASE}/orgs/${orgId}/agents/${agentId}/runs/${runId}/steps`,
|
|
72
|
+
// Machines (org-scoped)
|
|
73
|
+
MACHINES: (orgId: string) => `${API_BASE}/orgs/${orgId}/machines`,
|
|
74
|
+
MACHINE: (orgId: string, machineId: string) =>
|
|
75
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}`,
|
|
76
|
+
MACHINE_START: (orgId: string, machineId: string) =>
|
|
77
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}/start`,
|
|
78
|
+
MACHINE_STOP: (orgId: string, machineId: string) =>
|
|
79
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}/stop`,
|
|
80
|
+
MACHINE_STATUS: (orgId: string, machineId: string) =>
|
|
81
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}/status`,
|
|
82
|
+
MACHINE_LOGS: (orgId: string, machineId: string) =>
|
|
83
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}/logs`,
|
|
84
|
+
MACHINE_WORKSPACE: (orgId: string, machineId: string) =>
|
|
85
|
+
`${API_BASE}/orgs/${orgId}/machines/${machineId}/workspace`,
|
|
75
86
|
|
|
76
87
|
// Tasks (org-scoped)
|
|
77
88
|
TASKS: (orgId: string) => `${API_BASE}/orgs/${orgId}/tasks`,
|
|
@@ -82,12 +93,38 @@ export const ENDPOINTS = {
|
|
|
82
93
|
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/relations`,
|
|
83
94
|
TASK_RELATION: (orgId: string, taskId: string, relId: string) =>
|
|
84
95
|
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/relations/${relId}`,
|
|
96
|
+
TASK_COMMENTS: (orgId: string, taskId: string) =>
|
|
97
|
+
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/comments`,
|
|
98
|
+
TASK_COMMENT: (orgId: string, taskId: string, commentId: string) =>
|
|
99
|
+
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/comments/${commentId}`,
|
|
100
|
+
TASK_ACTIVITIES: (orgId: string, taskId: string) =>
|
|
101
|
+
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/activities`,
|
|
102
|
+
|
|
103
|
+
// Projects (org-scoped)
|
|
104
|
+
PROJECTS: (orgId: string) => `${API_BASE}/orgs/${orgId}/projects`,
|
|
105
|
+
PROJECT: (orgId: string, projectId: string) => `${API_BASE}/orgs/${orgId}/projects/${projectId}`,
|
|
106
|
+
|
|
107
|
+
// Invitations (org-scoped, admin)
|
|
108
|
+
ORG_INVITATIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
109
|
+
ORG_INVITATION: (orgId: string, invId: string) =>
|
|
110
|
+
`${API_BASE}/orgs/${orgId}/invitations/${invId}`,
|
|
111
|
+
ORG_INVITATION_RESEND: (orgId: string, invId: string) =>
|
|
112
|
+
`${API_BASE}/orgs/${orgId}/invitations/${invId}/resend`,
|
|
113
|
+
|
|
114
|
+
// Invitations (invitee)
|
|
115
|
+
MY_INVITATIONS: `${API_BASE}/invitations/pending`,
|
|
116
|
+
INVITATION_ACCEPT: (id: string) => `${API_BASE}/invitations/${id}/accept`,
|
|
117
|
+
INVITATION_DECLINE: (id: string) => `${API_BASE}/invitations/${id}/decline`,
|
|
118
|
+
INVITATION_BY_TOKEN: (token: string) => `${API_BASE}/invitations/by-token/${token}`,
|
|
85
119
|
|
|
86
120
|
// Unread
|
|
87
121
|
UNREAD: `${API_BASE}/me/unread`,
|
|
88
122
|
CHAT_READ: (orgId: string, chatId: string) =>
|
|
89
123
|
`${API_BASE}/orgs/${orgId}/chats/${chatId}/read`,
|
|
90
124
|
|
|
125
|
+
// Platform config (agent-scoped, not org-scoped)
|
|
126
|
+
PLATFORM_CONFIG: `${API_BASE}/agents/platform-config`,
|
|
127
|
+
|
|
91
128
|
} as const;
|
|
92
129
|
|
|
93
130
|
// WebSocket event types
|
|
@@ -110,10 +147,24 @@ export const WS_EVENTS = {
|
|
|
110
147
|
MESSAGE_DELETE: 'message.delete',
|
|
111
148
|
TYPING_UPDATE: 'typing.update',
|
|
112
149
|
CHAT_UPDATE: 'chat.update',
|
|
113
|
-
APPROVAL_UPDATE: 'approval.update',
|
|
150
|
+
APPROVAL_UPDATE: 'approval.update', // deprecated
|
|
151
|
+
CARD_UPDATE: 'card.update',
|
|
114
152
|
RECOVERY_OVERFLOW: 'recovery.overflow',
|
|
115
153
|
MEMBERSHIP_CHANGED: 'membership.changed',
|
|
116
154
|
TASK_CREATED: 'task.created',
|
|
117
155
|
TASK_UPDATED: 'task.updated',
|
|
118
156
|
TASK_DELETED: 'task.deleted',
|
|
157
|
+
TASK_COMMENT_CREATED: 'task.comment.created',
|
|
158
|
+
TASK_COMMENT_UPDATED: 'task.comment.updated',
|
|
159
|
+
TASK_COMMENT_DELETED: 'task.comment.deleted',
|
|
160
|
+
PROJECT_CREATED: 'project.created',
|
|
161
|
+
PROJECT_UPDATED: 'project.updated',
|
|
162
|
+
PROJECT_DELETED: 'project.deleted',
|
|
163
|
+
AGENT_RUN_NEW: 'agent_run.new',
|
|
164
|
+
AGENT_RUN_UPDATE: 'agent_run.update',
|
|
165
|
+
AGENT_STEP_NEW: 'agent_step.new',
|
|
166
|
+
INVITATION_NEW: 'invitation.new',
|
|
167
|
+
INVITATION_ACCEPTED: 'invitation.accepted',
|
|
168
|
+
INVITATION_DECLINED: 'invitation.declined',
|
|
169
|
+
AGENT_CONFIG_UPDATE: 'agent_config.update',
|
|
119
170
|
} as const;
|
package/src/index.ts
CHANGED
|
@@ -3,4 +3,4 @@ export * from './constants.js';
|
|
|
3
3
|
export { PondClient, ApiError } from './client.js';
|
|
4
4
|
export type { PondClientOptions } from './client.js';
|
|
5
5
|
export { PondWs } from './ws.js';
|
|
6
|
-
export type { PondWsOptions, WsConnectionState, WsEventType, WsEventHandler } from './ws.js';
|
|
6
|
+
export type { PondWsOptions, WsConnectionState, WsEventType, WsEventHandler, WsClientEventMap } from './ws.js';
|