@parall/sdk 1.53.0 → 1.55.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 +80 -68
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +140 -119
- package/dist/constants.d.ts +21 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +31 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/project-task-client.d.ts +69 -0
- package/dist/project-task-client.d.ts.map +1 -0
- package/dist/project-task-client.js +117 -0
- package/dist/subject.d.ts +25 -0
- package/dist/subject.d.ts.map +1 -0
- package/dist/subject.js +54 -0
- package/dist/task-label-client.d.ts +13 -0
- package/dist/task-label-client.d.ts.map +1 -0
- package/dist/task-label-client.js +24 -0
- package/dist/task-label-types.d.ts +33 -0
- package/dist/task-label-types.d.ts.map +1 -0
- package/dist/task-label-types.js +1 -0
- package/dist/types.d.ts +250 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -3
- package/package.json +1 -1
- package/src/client.ts +406 -453
- package/src/constants.ts +42 -5
- package/src/index.ts +2 -0
- package/src/project-task-client.ts +249 -0
- package/src/subject.ts +66 -0
- package/src/task-label-client.ts +37 -0
- package/src/task-label-types.ts +51 -0
- package/src/types.ts +282 -6
package/src/constants.ts
CHANGED
|
@@ -371,6 +371,11 @@ export const ENDPOINTS = {
|
|
|
371
371
|
ORG_MEMBERS: (orgId: string) => `${API_BASE}/orgs/${orgId}/members`,
|
|
372
372
|
ORG_MEMBERS_FORMER: (orgId: string) => `${API_BASE}/orgs/${orgId}/members/former`,
|
|
373
373
|
TEAMS: (orgId: string) => `${API_BASE}/orgs/${orgId}/teams`,
|
|
374
|
+
TEAM: (orgId: string, teamId: string) => `${API_BASE}/orgs/${orgId}/teams/${teamId}`,
|
|
375
|
+
TEAM_MEMBERS: (orgId: string, teamId: string) =>
|
|
376
|
+
`${API_BASE}/orgs/${orgId}/teams/${teamId}/members`,
|
|
377
|
+
TEAM_MEMBER: (orgId: string, teamId: string, userId: string) =>
|
|
378
|
+
`${API_BASE}/orgs/${orgId}/teams/${teamId}/members/${userId}`,
|
|
374
379
|
ORG_MEMBERS_ONLINE: (orgId: string) => `${API_BASE}/orgs/${orgId}/members/online`,
|
|
375
380
|
ORG_MEMBER: (orgId: string, userId: string) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
376
381
|
ORG_MEMBER_CHATS: (orgId: string, memberId: string) =>
|
|
@@ -562,6 +567,7 @@ export const ENDPOINTS = {
|
|
|
562
567
|
|
|
563
568
|
// Tasks (org-scoped)
|
|
564
569
|
TASKS: (orgId: string) => `${API_BASE}/orgs/${orgId}/tasks`,
|
|
570
|
+
TASK_SUBTASK_SUMMARY: (orgId: string) => `${API_BASE}/orgs/${orgId}/tasks/subtask-summary`,
|
|
565
571
|
TASK: (orgId: string, taskId: string) => `${API_BASE}/orgs/${orgId}/tasks/${taskId}`,
|
|
566
572
|
TASK_ARCHIVE: (orgId: string, taskId: string) =>
|
|
567
573
|
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/archive`,
|
|
@@ -585,9 +591,18 @@ export const ENDPOINTS = {
|
|
|
585
591
|
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/comments/${commentId}`,
|
|
586
592
|
TASK_ACTIVITIES: (orgId: string, taskId: string) =>
|
|
587
593
|
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/activities`,
|
|
594
|
+
TASK_LABELS: (orgId: string, taskId: string) =>
|
|
595
|
+
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/labels`,
|
|
596
|
+
TASK_LABEL: (orgId: string, taskId: string, labelId: string) =>
|
|
597
|
+
`${API_BASE}/orgs/${orgId}/tasks/${taskId}/labels/${labelId}`,
|
|
598
|
+
|
|
599
|
+
// Labels (org-scoped vocabulary; writes are admin-gated)
|
|
600
|
+
LABELS: (orgId: string) => `${API_BASE}/orgs/${orgId}/labels`,
|
|
601
|
+
LABEL: (orgId: string, labelId: string) => `${API_BASE}/orgs/${orgId}/labels/${labelId}`,
|
|
588
602
|
|
|
589
603
|
// Projects (org-scoped)
|
|
590
604
|
PROJECTS: (orgId: string) => `${API_BASE}/orgs/${orgId}/projects`,
|
|
605
|
+
PROJECT_TASK_SUMMARY: (orgId: string) => `${API_BASE}/orgs/${orgId}/projects/task-summary`,
|
|
591
606
|
PROJECT: (orgId: string, projectId: string) => `${API_BASE}/orgs/${orgId}/projects/${projectId}`,
|
|
592
607
|
|
|
593
608
|
// Schedules (org-scoped, platform time trigger primitive)
|
|
@@ -653,8 +668,10 @@ export const ENDPOINTS = {
|
|
|
653
668
|
SLACK_HISTORY: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/history`,
|
|
654
669
|
SLACK_MEMBERS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/members`,
|
|
655
670
|
SLACK_STATUS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/slack/status`,
|
|
656
|
-
// WeChat tier-B read
|
|
671
|
+
// WeChat tier-B read verbs (agent-only; internal research preview).
|
|
657
672
|
WECHAT_CONTACTS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/contacts`,
|
|
673
|
+
WECHAT_PROFILE: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/profile`,
|
|
674
|
+
WECHAT_STATUS: (orgId: string) => `${API_BASE}/orgs/${orgId}/agents/me/wechat/status`,
|
|
658
675
|
|
|
659
676
|
// Invitations (org-scoped, admin)
|
|
660
677
|
ORG_INVITATIONS: (orgId: string) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
@@ -822,8 +839,11 @@ export const ENDPOINTS = {
|
|
|
822
839
|
// Unified search (messages + tasks + wiki, org-scoped)
|
|
823
840
|
SEARCH: (orgId: string) => `${API_BASE}/orgs/${orgId}/search`,
|
|
824
841
|
|
|
825
|
-
// Feature flags (
|
|
826
|
-
|
|
842
|
+
// Feature flags (server-evaluated). Org-scoped by default; omit the org for
|
|
843
|
+
// the pre-org form, which resolves "cap:" capabilities only (PostHog flags
|
|
844
|
+
// are org-scoped and absent there) for callers that have no org yet.
|
|
845
|
+
FEATURE_FLAGS: (orgId?: string) =>
|
|
846
|
+
orgId ? `${API_BASE}/orgs/${orgId}/feature-flags` : `${API_BASE}/feature-flags`,
|
|
827
847
|
|
|
828
848
|
// Team templates (org-scoped, owner/admin only)
|
|
829
849
|
TEMPLATES: (orgId: string) => `${API_BASE}/orgs/${orgId}/templates`,
|
|
@@ -925,17 +945,30 @@ export const ENDPOINTS = {
|
|
|
925
945
|
ORG_CLIPS_INSTALLED: (orgId: string) => `/api/v1/orgs/${orgId}/clips/installed`,
|
|
926
946
|
ORG_CLIP_UNINSTALL: (orgId: string, clipId: string) =>
|
|
927
947
|
`/api/v1/orgs/${orgId}/clips/${clipId}/uninstall`,
|
|
948
|
+
ORG_CLIP_MCP_REMOVE: (orgId: string, clipId: string) =>
|
|
949
|
+
`/api/v1/orgs/${orgId}/clips/${clipId}/mcp-remove`,
|
|
928
950
|
// Per-org agent exec access for one clip (default all agents; PUT human-only)
|
|
929
951
|
ORG_CLIP_EXEC_ACCESS: (orgId: string, clipId: string) =>
|
|
930
952
|
`/api/v1/orgs/${orgId}/clips/${clipId}/exec-access`,
|
|
931
|
-
// MCP clip server config (cap:clip-mcp;
|
|
932
|
-
//
|
|
953
|
+
// MCP clip server config (cap:clip-mcp; cross-org is limited to installed,
|
|
954
|
+
// reviewed Official OAuth Clips). The
|
|
955
|
+
// singular family addresses the org's DEFAULT connection (pre-165
|
|
956
|
+
// back-compat shim); the plural family is the multi-connection surface —
|
|
957
|
+
// one config row (credential slot) per connection, addressed by config id.
|
|
933
958
|
ORG_CLIP_MCP_CONFIG: (orgId: string, clipId: string) =>
|
|
934
959
|
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config`,
|
|
935
960
|
ORG_CLIP_MCP_TOOLS_REFRESH: (orgId: string, clipId: string) =>
|
|
936
961
|
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/tools/refresh`,
|
|
937
962
|
ORG_CLIP_MCP_OAUTH_DISCONNECT: (orgId: string, clipId: string) =>
|
|
938
963
|
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-config/oauth/disconnect`,
|
|
964
|
+
ORG_CLIP_MCP_CONFIGS: (orgId: string, clipId: string) =>
|
|
965
|
+
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-configs`,
|
|
966
|
+
ORG_CLIP_MCP_CONFIG_BY_ID: (orgId: string, clipId: string, configId: string) =>
|
|
967
|
+
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-configs/${configId}`,
|
|
968
|
+
ORG_CLIP_MCP_TOOLS_REFRESH_BY_ID: (orgId: string, clipId: string, configId: string) =>
|
|
969
|
+
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-configs/${configId}/tools/refresh`,
|
|
970
|
+
ORG_CLIP_MCP_OAUTH_DISCONNECT_BY_ID: (orgId: string, clipId: string, configId: string) =>
|
|
971
|
+
`/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-configs/${configId}/oauth/disconnect`,
|
|
939
972
|
} as const;
|
|
940
973
|
|
|
941
974
|
/**
|
|
@@ -1007,6 +1040,9 @@ export const WS_EVENTS = {
|
|
|
1007
1040
|
PROJECT_CREATED: 'project.created',
|
|
1008
1041
|
PROJECT_UPDATED: 'project.updated',
|
|
1009
1042
|
PROJECT_DELETED: 'project.deleted',
|
|
1043
|
+
LABEL_CREATED: 'label.created',
|
|
1044
|
+
LABEL_UPDATED: 'label.updated',
|
|
1045
|
+
LABEL_DELETED: 'label.deleted',
|
|
1010
1046
|
AGENT_SESSION_NEW: 'agent_session.new',
|
|
1011
1047
|
AGENT_SESSION_UPDATE: 'agent_session.update',
|
|
1012
1048
|
AGENT_STEP_NEW: 'agent_step.new',
|
|
@@ -1017,6 +1053,7 @@ export const WS_EVENTS = {
|
|
|
1017
1053
|
ORG_JOIN_REQUEST_NEW: 'org.join_request.new',
|
|
1018
1054
|
ORG_INVITE_LINK_JOINED: 'org.invite_link.joined',
|
|
1019
1055
|
ORG_MEMBER_REMOVED: 'org.member.removed',
|
|
1056
|
+
ORG_MEMBER_PROFILE_UPDATED: 'org.member.profile.updated',
|
|
1020
1057
|
AGENT_CONFIG_UPDATE: 'agent_config.update',
|
|
1021
1058
|
PRESENCE_UPDATE: 'presence.update',
|
|
1022
1059
|
WIKI_CHANGESET_CREATED: 'wiki.changeset.created',
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from './browser-viewer.js';
|
|
|
2
2
|
export type { ParallClientOptions } from './client.js';
|
|
3
3
|
export { ApiError, ParallClient } from './client.js';
|
|
4
4
|
export * from './constants.js';
|
|
5
|
+
export * from './subject.js';
|
|
6
|
+
export * from './task-label-types.js';
|
|
5
7
|
export * from './types.js';
|
|
6
8
|
export type {
|
|
7
9
|
ParallWsOptions,
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { ENDPOINTS } from './constants.js';
|
|
2
|
+
import type {
|
|
3
|
+
CreateProjectRequest,
|
|
4
|
+
CreateTaskCommentRequest,
|
|
5
|
+
CreateTaskRelationRequest,
|
|
6
|
+
CreateTaskRequest,
|
|
7
|
+
PaginatedResponse,
|
|
8
|
+
Project,
|
|
9
|
+
ProjectTaskSummaryResponse,
|
|
10
|
+
Task,
|
|
11
|
+
TaskActivity,
|
|
12
|
+
TaskComment,
|
|
13
|
+
TaskRelation,
|
|
14
|
+
TaskRelationTargetType,
|
|
15
|
+
TaskSubtaskSummaryResponse,
|
|
16
|
+
TaskWatcher,
|
|
17
|
+
ThreadWatcher,
|
|
18
|
+
UpdateProjectRequest,
|
|
19
|
+
UpdateTaskCommentRequest,
|
|
20
|
+
UpdateTaskRequest,
|
|
21
|
+
} from './types.js';
|
|
22
|
+
|
|
23
|
+
/** Project and task REST methods shared by every ParallClient instance. */
|
|
24
|
+
export abstract class ProjectTaskClient {
|
|
25
|
+
protected abstract request<T>(
|
|
26
|
+
method: string,
|
|
27
|
+
path: string,
|
|
28
|
+
body?: unknown,
|
|
29
|
+
query?: Record<string, string | number | boolean | undefined>,
|
|
30
|
+
): Promise<T>;
|
|
31
|
+
|
|
32
|
+
async createTask(orgId: string, data: CreateTaskRequest): Promise<Task> {
|
|
33
|
+
return this.request('POST', ENDPOINTS.TASKS(orgId), data);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getTasks(
|
|
37
|
+
orgId: string,
|
|
38
|
+
params?: {
|
|
39
|
+
q?: string;
|
|
40
|
+
status?: string;
|
|
41
|
+
priority?: string;
|
|
42
|
+
assignee_id?: string;
|
|
43
|
+
parent_id?: string;
|
|
44
|
+
project_id?: string;
|
|
45
|
+
/** Comma-separated label IDs; matching uses any-of semantics. */
|
|
46
|
+
label_ids?: string;
|
|
47
|
+
limit?: number;
|
|
48
|
+
cursor?: string;
|
|
49
|
+
sort?: string;
|
|
50
|
+
order?: string;
|
|
51
|
+
scope?: 'active' | 'archived' | 'all';
|
|
52
|
+
},
|
|
53
|
+
): Promise<PaginatedResponse<Task>> {
|
|
54
|
+
return this.request('GET', ENDPOINTS.TASKS(orgId), undefined, params);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getTaskSubtaskSummary(
|
|
58
|
+
orgId: string,
|
|
59
|
+
params?: { assignee_id?: string; project_id?: string },
|
|
60
|
+
): Promise<TaskSubtaskSummaryResponse> {
|
|
61
|
+
return this.request('GET', ENDPOINTS.TASK_SUBTASK_SUMMARY(orgId), undefined, params);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getTask(orgId: string, taskId: string): Promise<Task> {
|
|
65
|
+
return this.request('GET', ENDPOINTS.TASK(orgId, taskId));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async updateTask(orgId: string, taskId: string, data: UpdateTaskRequest): Promise<Task> {
|
|
69
|
+
return this.request('PATCH', ENDPOINTS.TASK(orgId, taskId), data);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async deleteTask(orgId: string, taskId: string, opts?: { force?: boolean }): Promise<void> {
|
|
73
|
+
return this.request(
|
|
74
|
+
'DELETE',
|
|
75
|
+
ENDPOINTS.TASK(orgId, taskId),
|
|
76
|
+
undefined,
|
|
77
|
+
opts?.force ? { force: 'true' } : undefined,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async archiveTask(orgId: string, taskId: string): Promise<void> {
|
|
82
|
+
return this.request('POST', ENDPOINTS.TASK_ARCHIVE(orgId, taskId));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async restoreTask(orgId: string, taskId: string): Promise<void> {
|
|
86
|
+
return this.request('POST', ENDPOINTS.TASK_RESTORE(orgId, taskId));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async watchTask(orgId: string, taskId: string): Promise<void> {
|
|
90
|
+
return this.request('POST', ENDPOINTS.TASK_WATCH(orgId, taskId));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async unwatchTask(orgId: string, taskId: string): Promise<void> {
|
|
94
|
+
return this.request('DELETE', ENDPOINTS.TASK_WATCH(orgId, taskId));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getTaskWatchers(orgId: string, taskId: string): Promise<TaskWatcher[]> {
|
|
98
|
+
const res = await this.request<{ data: TaskWatcher[] }>(
|
|
99
|
+
'GET',
|
|
100
|
+
ENDPOINTS.TASK_WATCHERS(orgId, taskId),
|
|
101
|
+
);
|
|
102
|
+
return res.data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async subscribeTaskMember(orgId: string, taskId: string, userId: string): Promise<void> {
|
|
106
|
+
return this.request('PUT', ENDPOINTS.TASK_SUBSCRIBER(orgId, taskId, userId));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async unsubscribeTaskMember(orgId: string, taskId: string, userId: string): Promise<void> {
|
|
110
|
+
return this.request('DELETE', ENDPOINTS.TASK_SUBSCRIBER(orgId, taskId, userId));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async watchThread(threadRootId: string): Promise<void> {
|
|
114
|
+
return this.request('POST', ENDPOINTS.MESSAGE_WATCH(threadRootId));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async unwatchThread(threadRootId: string): Promise<void> {
|
|
118
|
+
return this.request('DELETE', ENDPOINTS.MESSAGE_WATCH(threadRootId));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getThreadWatchers(threadRootId: string): Promise<ThreadWatcher[]> {
|
|
122
|
+
const res = await this.request<{ data: ThreadWatcher[] }>(
|
|
123
|
+
'GET',
|
|
124
|
+
ENDPOINTS.MESSAGE_WATCHERS(threadRootId),
|
|
125
|
+
);
|
|
126
|
+
return res.data;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async isWatchingThread(threadRootId: string): Promise<boolean> {
|
|
130
|
+
const res = await this.request<{ watching: boolean }>(
|
|
131
|
+
'GET',
|
|
132
|
+
ENDPOINTS.MESSAGE_WATCHING(threadRootId),
|
|
133
|
+
);
|
|
134
|
+
return res.watching;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getSubtasks(orgId: string, taskId: string): Promise<Task[]> {
|
|
138
|
+
const res = await this.request<PaginatedResponse<Task>>(
|
|
139
|
+
'GET',
|
|
140
|
+
ENDPOINTS.TASK_SUBTASKS(orgId, taskId),
|
|
141
|
+
);
|
|
142
|
+
return res.data;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async createTaskRelation(
|
|
146
|
+
orgId: string,
|
|
147
|
+
taskId: string,
|
|
148
|
+
data: CreateTaskRelationRequest,
|
|
149
|
+
): Promise<TaskRelation> {
|
|
150
|
+
return this.request('POST', ENDPOINTS.TASK_RELATIONS(orgId, taskId), data);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async getTaskRelations(orgId: string, taskId: string): Promise<TaskRelation[]> {
|
|
154
|
+
const res = await this.request<{ data: TaskRelation[] }>(
|
|
155
|
+
'GET',
|
|
156
|
+
ENDPOINTS.TASK_RELATIONS(orgId, taskId),
|
|
157
|
+
);
|
|
158
|
+
return res.data;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async listTaskRelationsByTarget(
|
|
162
|
+
orgId: string,
|
|
163
|
+
params: { target_type: TaskRelationTargetType; target_id: string },
|
|
164
|
+
): Promise<TaskRelation[]> {
|
|
165
|
+
const query = new URLSearchParams({
|
|
166
|
+
target_type: params.target_type,
|
|
167
|
+
target_id: params.target_id,
|
|
168
|
+
});
|
|
169
|
+
const res = await this.request<{ data: TaskRelation[] }>(
|
|
170
|
+
'GET',
|
|
171
|
+
`${ENDPOINTS.TASK_RELATIONS_BY_TARGET(orgId)}?${query.toString()}`,
|
|
172
|
+
);
|
|
173
|
+
return res.data;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async deleteTaskRelation(orgId: string, taskId: string, relationId: string): Promise<void> {
|
|
177
|
+
return this.request('DELETE', ENDPOINTS.TASK_RELATION(orgId, taskId, relationId));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async getTaskComments(
|
|
181
|
+
orgId: string,
|
|
182
|
+
taskId: string,
|
|
183
|
+
params?: { limit?: number; cursor?: string; order?: string },
|
|
184
|
+
): Promise<PaginatedResponse<TaskComment>> {
|
|
185
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENTS(orgId, taskId), undefined, params);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getTaskComment(orgId: string, taskId: string, commentId: string): Promise<TaskComment> {
|
|
189
|
+
return this.request('GET', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async createTaskComment(
|
|
193
|
+
orgId: string,
|
|
194
|
+
taskId: string,
|
|
195
|
+
data: CreateTaskCommentRequest,
|
|
196
|
+
): Promise<TaskComment> {
|
|
197
|
+
return this.request('POST', ENDPOINTS.TASK_COMMENTS(orgId, taskId), data);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async updateTaskComment(
|
|
201
|
+
orgId: string,
|
|
202
|
+
taskId: string,
|
|
203
|
+
commentId: string,
|
|
204
|
+
data: UpdateTaskCommentRequest,
|
|
205
|
+
): Promise<TaskComment> {
|
|
206
|
+
return this.request('PATCH', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId), data);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async deleteTaskComment(orgId: string, taskId: string, commentId: string): Promise<void> {
|
|
210
|
+
return this.request('DELETE', ENDPOINTS.TASK_COMMENT(orgId, taskId, commentId));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async getTaskActivities(
|
|
214
|
+
orgId: string,
|
|
215
|
+
taskId: string,
|
|
216
|
+
params?: { limit?: number; cursor?: string },
|
|
217
|
+
): Promise<PaginatedResponse<TaskActivity>> {
|
|
218
|
+
return this.request('GET', ENDPOINTS.TASK_ACTIVITIES(orgId, taskId), undefined, params);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async createProject(orgId: string, data: CreateProjectRequest): Promise<Project> {
|
|
222
|
+
return this.request('POST', ENDPOINTS.PROJECTS(orgId), data);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async getProjects(orgId: string): Promise<Project[]> {
|
|
226
|
+
const res = await this.request<{ data: Project[] }>('GET', ENDPOINTS.PROJECTS(orgId));
|
|
227
|
+
return res.data;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async getProjectTaskSummary(orgId: string): Promise<ProjectTaskSummaryResponse> {
|
|
231
|
+
return this.request('GET', ENDPOINTS.PROJECT_TASK_SUMMARY(orgId));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async getProject(orgId: string, projectId: string): Promise<Project> {
|
|
235
|
+
return this.request('GET', ENDPOINTS.PROJECT(orgId, projectId));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async updateProject(
|
|
239
|
+
orgId: string,
|
|
240
|
+
projectId: string,
|
|
241
|
+
data: UpdateProjectRequest,
|
|
242
|
+
): Promise<Project> {
|
|
243
|
+
return this.request('PATCH', ENDPOINTS.PROJECT(orgId, projectId), data);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async deleteProject(orgId: string, projectId: string): Promise<void> {
|
|
247
|
+
return this.request('DELETE', ENDPOINTS.PROJECT(orgId, projectId));
|
|
248
|
+
}
|
|
249
|
+
}
|
package/src/subject.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Shared ACL subject token helpers for clients that edit permission rosters. */
|
|
2
|
+
|
|
3
|
+
export type SubjectKind = 'wildcard' | 'user' | 'team';
|
|
4
|
+
|
|
5
|
+
export interface ParsedSubject {
|
|
6
|
+
kind: SubjectKind;
|
|
7
|
+
/** Empty for wildcard, user ID for user, and slug without `@` for team. */
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TEAM_SLUG = /^[a-z0-9][a-z0-9-]*$/;
|
|
12
|
+
|
|
13
|
+
export function isValidTeamSlug(slug: string): boolean {
|
|
14
|
+
return TEAM_SLUG.test(slug);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function formatTeamSubject(slug: string): string {
|
|
18
|
+
if (!isValidTeamSlug(slug)) {
|
|
19
|
+
throw new Error(`Invalid team slug: ${slug}`);
|
|
20
|
+
}
|
|
21
|
+
return `@${slug}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseSubjectToken(token: string): ParsedSubject {
|
|
25
|
+
if (token === '*') {
|
|
26
|
+
return { kind: 'wildcard', value: '' };
|
|
27
|
+
}
|
|
28
|
+
if (token.startsWith('@')) {
|
|
29
|
+
const slug = token.slice(1);
|
|
30
|
+
if (!isValidTeamSlug(slug)) {
|
|
31
|
+
throw new Error(`Invalid team subject: ${token}`);
|
|
32
|
+
}
|
|
33
|
+
return { kind: 'team', value: slug };
|
|
34
|
+
}
|
|
35
|
+
if (!token) {
|
|
36
|
+
throw new Error('Subject token is empty');
|
|
37
|
+
}
|
|
38
|
+
return { kind: 'user', value: token };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Maximum distinct team references one ACL row may carry. Mirrors
|
|
43
|
+
* `subject.MaxTeamSubjectsPerWrite` on the server, which enforces it.
|
|
44
|
+
*/
|
|
45
|
+
export const MAX_TEAM_SUBJECTS_PER_WRITE = 200;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns unique team slugs in first-seen order for batched write validation.
|
|
49
|
+
*
|
|
50
|
+
* Syntactically invalid slugs are included rather than skipped, matching the
|
|
51
|
+
* server's `subject.TeamSlugs`: a mistyped `@Finance` must reach the existence
|
|
52
|
+
* check and be reported missing, so the write fails with a legible error
|
|
53
|
+
* instead of storing a token that matches nobody.
|
|
54
|
+
*/
|
|
55
|
+
export function teamSlugsFromSubjects(subjects: readonly string[]): string[] {
|
|
56
|
+
const seen = new Set<string>();
|
|
57
|
+
const slugs: string[] = [];
|
|
58
|
+
for (const token of subjects) {
|
|
59
|
+
if (!token.startsWith('@')) continue;
|
|
60
|
+
const slug = token.slice(1);
|
|
61
|
+
if (!slug || seen.has(slug)) continue;
|
|
62
|
+
seen.add(slug);
|
|
63
|
+
slugs.push(slug);
|
|
64
|
+
}
|
|
65
|
+
return slugs;
|
|
66
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ENDPOINTS } from './constants.js';
|
|
2
|
+
import { ProjectTaskClient } from './project-task-client.js';
|
|
3
|
+
import type {
|
|
4
|
+
AddTaskLabelsRequest,
|
|
5
|
+
CreateLabelRequest,
|
|
6
|
+
Label,
|
|
7
|
+
UpdateLabelRequest,
|
|
8
|
+
} from './task-label-types.js';
|
|
9
|
+
import type { Task } from './types.js';
|
|
10
|
+
|
|
11
|
+
/** Organization label vocabulary and incremental Task attachment methods. */
|
|
12
|
+
export abstract class TaskLabelClient extends ProjectTaskClient {
|
|
13
|
+
async getLabels(orgId: string): Promise<Label[]> {
|
|
14
|
+
const response = await this.request<{ data: Label[] }>('GET', ENDPOINTS.LABELS(orgId));
|
|
15
|
+
return response.data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async createLabel(orgId: string, data: CreateLabelRequest): Promise<Label> {
|
|
19
|
+
return this.request('POST', ENDPOINTS.LABELS(orgId), data);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async updateLabel(orgId: string, labelId: string, data: UpdateLabelRequest): Promise<Label> {
|
|
23
|
+
return this.request('PATCH', ENDPOINTS.LABEL(orgId, labelId), data);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async deleteLabel(orgId: string, labelId: string): Promise<void> {
|
|
27
|
+
return this.request('DELETE', ENDPOINTS.LABEL(orgId, labelId));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async addTaskLabels(orgId: string, taskId: string, data: AddTaskLabelsRequest): Promise<Task> {
|
|
31
|
+
return this.request('POST', ENDPOINTS.TASK_LABELS(orgId, taskId), data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async removeTaskLabel(orgId: string, taskId: string, labelId: string): Promise<Task> {
|
|
35
|
+
return this.request('DELETE', ENDPOINTS.TASK_LABEL(orgId, taskId, labelId));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type LabelColor =
|
|
2
|
+
| 'default'
|
|
3
|
+
| 'red'
|
|
4
|
+
| 'green'
|
|
5
|
+
| 'yellow'
|
|
6
|
+
| 'blue'
|
|
7
|
+
| 'lilac'
|
|
8
|
+
| 'orange'
|
|
9
|
+
| 'magenta'
|
|
10
|
+
| 'sand'
|
|
11
|
+
| 'fuchsia'
|
|
12
|
+
| 'cerulean'
|
|
13
|
+
| 'teal'
|
|
14
|
+
| 'lime';
|
|
15
|
+
|
|
16
|
+
export interface Label {
|
|
17
|
+
id: string;
|
|
18
|
+
org_id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
color: LabelColor;
|
|
21
|
+
/** Optional agent-readable guidance for when this label applies. */
|
|
22
|
+
description?: string | null;
|
|
23
|
+
created_by: string;
|
|
24
|
+
created_at: string;
|
|
25
|
+
updated_at: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CreateLabelRequest {
|
|
29
|
+
name: string;
|
|
30
|
+
color?: LabelColor;
|
|
31
|
+
description?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UpdateLabelRequest {
|
|
35
|
+
name?: string;
|
|
36
|
+
color?: LabelColor;
|
|
37
|
+
/** Explicit null clears the description. */
|
|
38
|
+
description?: string | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AddTaskLabelsRequest {
|
|
42
|
+
label_ids: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type LabelCreatedData = Label;
|
|
46
|
+
export type LabelUpdatedData = Label;
|
|
47
|
+
|
|
48
|
+
export interface LabelDeletedData {
|
|
49
|
+
label_id: string;
|
|
50
|
+
org_id: string;
|
|
51
|
+
}
|