@unboundcx/sdk 4.13.66 → 4.13.74
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/index.js +3 -0
- package/package.json +1 -1
- package/services/cobrowse.js +211 -0
- package/services/objects.js +4 -0
- package/services/permissions.js +5 -3
- package/services/taskRouter/CCService.js +23 -0
- package/services/taskRouter/ParticipantService.js +68 -0
- package/services/taskRouter/TaskService.js +5 -0
- package/services/taskRouter/TaskWorkspaceMethods.js +189 -0
package/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { PortalsService } from './services/portals.js';
|
|
|
22
22
|
import { DealRegistrationsService } from './services/dealRegistrations.js';
|
|
23
23
|
import { SipEndpointsService } from './services/sipEndpoints.js';
|
|
24
24
|
import { WebchatService } from './services/webchat.js';
|
|
25
|
+
import { CobrowseService } from './services/cobrowse.js';
|
|
25
26
|
import { MessageTemplatesService } from './services/messageTemplates.js';
|
|
26
27
|
import { ExternalOAuthService } from './services/externalOAuth.js';
|
|
27
28
|
import { GoogleCalendarService } from './services/googleCalendar.js';
|
|
@@ -118,6 +119,7 @@ class UnboundSDK extends BaseSDK {
|
|
|
118
119
|
this.dealRegistrations = new DealRegistrationsService(this);
|
|
119
120
|
this.sipEndpoints = new SipEndpointsService(this);
|
|
120
121
|
this.webchat = new WebchatService(this);
|
|
122
|
+
this.cobrowse = new CobrowseService(this);
|
|
121
123
|
this.messageTemplates = new MessageTemplatesService(this);
|
|
122
124
|
this.externalOAuth = new ExternalOAuthService(this);
|
|
123
125
|
this.googleCalendar = new GoogleCalendarService(this);
|
|
@@ -314,6 +316,7 @@ export { PortalsService } from './services/portals.js';
|
|
|
314
316
|
export { DealRegistrationsService } from './services/dealRegistrations.js';
|
|
315
317
|
export { SipEndpointsService } from './services/sipEndpoints.js';
|
|
316
318
|
export { WebchatService, WebchatWidgetsService } from './services/webchat.js';
|
|
319
|
+
export { CobrowseService, CobrowseVisitorService } from './services/cobrowse.js';
|
|
317
320
|
export { MessageTemplatesService } from './services/messageTemplates.js';
|
|
318
321
|
export { WebchatVisitorService } from './services/webchat/VisitorService.js';
|
|
319
322
|
export { ExternalOAuthService } from './services/externalOAuth.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.74",
|
|
4
4
|
"description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
|
|
3
|
+
function authHeaders(token) {
|
|
4
|
+
return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function assertWebchatSource(source, method) {
|
|
8
|
+
if (source !== 'webchat') {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`cobrowse.${method}: source '${source}' is not supported (v1 is webchat only)`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function webchatHostIds(
|
|
16
|
+
{ source, widgetId, hostId, engagementSessionId } = {},
|
|
17
|
+
method,
|
|
18
|
+
) {
|
|
19
|
+
const esId = hostId ?? engagementSessionId;
|
|
20
|
+
assertWebchatSource(source, method);
|
|
21
|
+
return { widgetId, engagementSessionId: esId };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function conversationCobrowsePath(widgetId, engagementSessionId, suffix = '') {
|
|
25
|
+
return `/webchat/widgets/${widgetId}/conversations/${engagementSessionId}/cobrowse${suffix}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Visitor cobrowse consent (session JWT). Custom UIs; embed uses
|
|
30
|
+
* webchatApi.js in P1. Paths are public `/webchat/:widgetId/cobrowse/*`.
|
|
31
|
+
*/
|
|
32
|
+
export class CobrowseVisitorService {
|
|
33
|
+
constructor(sdk) {
|
|
34
|
+
this.sdk = sdk;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async #post(action, { widgetId, token, sid } = {}) {
|
|
38
|
+
this.sdk.validateParams(
|
|
39
|
+
{ widgetId, token, sid },
|
|
40
|
+
{
|
|
41
|
+
widgetId: { type: 'string', required: true },
|
|
42
|
+
token: { type: 'string', required: true },
|
|
43
|
+
sid: { type: 'string', required: true },
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
return internalRequest(
|
|
47
|
+
this.sdk,
|
|
48
|
+
`/webchat/${widgetId}/cobrowse/${action}`,
|
|
49
|
+
'POST',
|
|
50
|
+
{ body: { sid }, ...authHeaders(token) },
|
|
51
|
+
true,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Accept a pending cobrowse request. Starts live after API 200.
|
|
57
|
+
* @param {Object} params
|
|
58
|
+
* @param {string} params.widgetId
|
|
59
|
+
* @param {string} params.token - Webchat session JWT
|
|
60
|
+
* @param {string} params.sid
|
|
61
|
+
*/
|
|
62
|
+
async accept(params) {
|
|
63
|
+
return this.#post('accept', params);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Decline a pending cobrowse request.
|
|
68
|
+
* @param {Object} params
|
|
69
|
+
* @param {string} params.widgetId
|
|
70
|
+
* @param {string} params.token
|
|
71
|
+
* @param {string} params.sid
|
|
72
|
+
*/
|
|
73
|
+
async deny(params) {
|
|
74
|
+
return this.#post('deny', params);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Visitor-initiated end of a live/requested cobrowse session.
|
|
79
|
+
* @param {Object} params
|
|
80
|
+
* @param {string} params.widgetId
|
|
81
|
+
* @param {string} params.token
|
|
82
|
+
* @param {string} params.sid
|
|
83
|
+
*/
|
|
84
|
+
async end(params) {
|
|
85
|
+
return this.#post('end', params);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Host-agnostic cobrowse surface (`sdk.cobrowse.*`). v1 adapter is
|
|
91
|
+
* WebChat; Meet later uses the same method names with `source:'meet'`.
|
|
92
|
+
* No `sdk.webchat.cobrowse`. No `getRecording` in this box.
|
|
93
|
+
*/
|
|
94
|
+
export class CobrowseService {
|
|
95
|
+
constructor(sdk) {
|
|
96
|
+
this.sdk = sdk;
|
|
97
|
+
this.visitor = new CobrowseVisitorService(sdk);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Agent request. Nested under the webchat conversation.
|
|
102
|
+
* @param {Object} params
|
|
103
|
+
* @param {'webchat'} params.source
|
|
104
|
+
* @param {string} params.widgetId
|
|
105
|
+
* @param {string} params.hostId - engagementSessionId when source is webchat
|
|
106
|
+
* @param {string} [params.engagementSessionId] - alias of hostId
|
|
107
|
+
* @param {'pointer'|'write'} [params.mode='pointer']
|
|
108
|
+
* @param {boolean} [params.record=false]
|
|
109
|
+
*/
|
|
110
|
+
async request({
|
|
111
|
+
source,
|
|
112
|
+
widgetId,
|
|
113
|
+
hostId,
|
|
114
|
+
engagementSessionId,
|
|
115
|
+
mode = 'pointer',
|
|
116
|
+
record = false,
|
|
117
|
+
} = {}) {
|
|
118
|
+
const ids = webchatHostIds(
|
|
119
|
+
{ source, widgetId, hostId, engagementSessionId },
|
|
120
|
+
'request',
|
|
121
|
+
);
|
|
122
|
+
this.sdk.validateParams(
|
|
123
|
+
{ source, widgetId, hostId: ids.engagementSessionId, mode, record },
|
|
124
|
+
{
|
|
125
|
+
source: { type: 'string', required: true },
|
|
126
|
+
widgetId: { type: 'string', required: true },
|
|
127
|
+
hostId: { type: 'string', required: true },
|
|
128
|
+
mode: { type: 'string', required: false },
|
|
129
|
+
record: { type: 'boolean', required: false },
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
return internalRequest(
|
|
133
|
+
this.sdk,
|
|
134
|
+
conversationCobrowsePath(ids.widgetId, ids.engagementSessionId, '/request'),
|
|
135
|
+
'POST',
|
|
136
|
+
{ body: { mode, record: record === true } },
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Agent end. Nested under the conversation because the HTTP route is.
|
|
142
|
+
* @param {Object} params
|
|
143
|
+
* @param {string} params.sid
|
|
144
|
+
* @param {string} params.widgetId
|
|
145
|
+
* @param {string} [params.hostId]
|
|
146
|
+
* @param {string} [params.engagementSessionId]
|
|
147
|
+
* @param {'webchat'} [params.source='webchat']
|
|
148
|
+
*/
|
|
149
|
+
async end({
|
|
150
|
+
sid,
|
|
151
|
+
widgetId,
|
|
152
|
+
hostId,
|
|
153
|
+
engagementSessionId,
|
|
154
|
+
source = 'webchat',
|
|
155
|
+
} = {}) {
|
|
156
|
+
const ids = webchatHostIds(
|
|
157
|
+
{ source, widgetId, hostId, engagementSessionId },
|
|
158
|
+
'end',
|
|
159
|
+
);
|
|
160
|
+
this.sdk.validateParams(
|
|
161
|
+
{ sid, widgetId, hostId: ids.engagementSessionId },
|
|
162
|
+
{
|
|
163
|
+
sid: { type: 'string', required: true },
|
|
164
|
+
widgetId: { type: 'string', required: true },
|
|
165
|
+
hostId: { type: 'string', required: true },
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
return internalRequest(
|
|
169
|
+
this.sdk,
|
|
170
|
+
conversationCobrowsePath(
|
|
171
|
+
ids.widgetId,
|
|
172
|
+
ids.engagementSessionId,
|
|
173
|
+
`/${sid}/end`,
|
|
174
|
+
),
|
|
175
|
+
'POST',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Current cobrowse session metadata for this host (no DOM).
|
|
181
|
+
* @param {Object} params
|
|
182
|
+
* @param {'webchat'} params.source
|
|
183
|
+
* @param {string} params.widgetId
|
|
184
|
+
* @param {string} params.hostId
|
|
185
|
+
* @param {string} [params.engagementSessionId]
|
|
186
|
+
*/
|
|
187
|
+
async getActive({
|
|
188
|
+
source,
|
|
189
|
+
widgetId,
|
|
190
|
+
hostId,
|
|
191
|
+
engagementSessionId,
|
|
192
|
+
} = {}) {
|
|
193
|
+
const ids = webchatHostIds(
|
|
194
|
+
{ source, widgetId, hostId, engagementSessionId },
|
|
195
|
+
'getActive',
|
|
196
|
+
);
|
|
197
|
+
this.sdk.validateParams(
|
|
198
|
+
{ source, widgetId, hostId: ids.engagementSessionId },
|
|
199
|
+
{
|
|
200
|
+
source: { type: 'string', required: true },
|
|
201
|
+
widgetId: { type: 'string', required: true },
|
|
202
|
+
hostId: { type: 'string', required: true },
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
return internalRequest(
|
|
206
|
+
this.sdk,
|
|
207
|
+
conversationCobrowsePath(ids.widgetId, ids.engagementSessionId, '/active'),
|
|
208
|
+
'GET',
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
package/services/objects.js
CHANGED
|
@@ -118,6 +118,7 @@ export class ObjectsService {
|
|
|
118
118
|
limit = 100,
|
|
119
119
|
nextId = null,
|
|
120
120
|
previousId = null,
|
|
121
|
+
orderBy = null,
|
|
121
122
|
orderByDirection = 'DESC',
|
|
122
123
|
expandDetails = false,
|
|
123
124
|
meta = {},
|
|
@@ -131,6 +132,7 @@ export class ObjectsService {
|
|
|
131
132
|
limit,
|
|
132
133
|
nextId,
|
|
133
134
|
previousId,
|
|
135
|
+
orderBy,
|
|
134
136
|
orderByDirection,
|
|
135
137
|
expandDetails,
|
|
136
138
|
meta,
|
|
@@ -142,6 +144,7 @@ export class ObjectsService {
|
|
|
142
144
|
limit: { type: 'number', required: false },
|
|
143
145
|
nextId: { type: 'string', required: false },
|
|
144
146
|
previousId: { type: 'string', required: false },
|
|
147
|
+
orderBy: { type: 'string', required: false }, // base-table column; ties break createdAt ASC, id ASC
|
|
145
148
|
orderByDirection: { type: 'string', required: false },
|
|
146
149
|
expandDetails: { type: 'boolean', required: false },
|
|
147
150
|
meta: { type: 'object', required: false },
|
|
@@ -153,6 +156,7 @@ export class ObjectsService {
|
|
|
153
156
|
if (limit !== 100) query.limit = limit;
|
|
154
157
|
if (nextId !== null) query.nextId = nextId;
|
|
155
158
|
if (previousId !== null) query.previousId = previousId;
|
|
159
|
+
if (orderBy !== null) query.orderBy = orderBy;
|
|
156
160
|
if (orderByDirection !== 'DESC')
|
|
157
161
|
query.orderByDirection = orderByDirection;
|
|
158
162
|
if (expandDetails) query.expandDetails = expandDetails;
|
package/services/permissions.js
CHANGED
|
@@ -570,7 +570,7 @@ export class PermissionsService {
|
|
|
570
570
|
});
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
-
/** Queues this group grants. @returns {Promise<{results: Array<{queueId, access, autoLogin}>}>} */
|
|
573
|
+
/** Queues this group grants. @returns {Promise<{results: Array<{queueId, access, autoLogin, role}>}>} */
|
|
574
574
|
async listGroupQueues(groupId) {
|
|
575
575
|
groupId = String(groupId);
|
|
576
576
|
this.sdk.validateParams(
|
|
@@ -582,10 +582,11 @@ export class PermissionsService {
|
|
|
582
582
|
|
|
583
583
|
/**
|
|
584
584
|
* Replace the group's full queue list.
|
|
585
|
-
* @param {Array<{queueId: string, access?: boolean, autoLogin?: boolean}>} queues
|
|
585
|
+
* @param {Array<{queueId: string, access?: boolean, autoLogin?: boolean, role?: string}>} queues
|
|
586
586
|
* `autoLogin` seeds the derived membership row and takes effect at the
|
|
587
587
|
* agent's next availability transition — it never logs anyone in
|
|
588
|
-
* mid-session.
|
|
588
|
+
* mid-session. `role` ('agent'|'manager', default 'agent') is the
|
|
589
|
+
* queueUsers.role materialised for group members via this queue.
|
|
589
590
|
*/
|
|
590
591
|
async setGroupQueues(groupId, queues) {
|
|
591
592
|
groupId = String(groupId);
|
|
@@ -602,6 +603,7 @@ export class PermissionsService {
|
|
|
602
603
|
queueId: String(q.queueId),
|
|
603
604
|
access: q.access !== false,
|
|
604
605
|
autoLogin: Boolean(q.autoLogin),
|
|
606
|
+
role: q.role || 'agent',
|
|
605
607
|
})),
|
|
606
608
|
},
|
|
607
609
|
});
|
|
@@ -23,6 +23,29 @@ export class CCService {
|
|
|
23
23
|
return result;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Get the queueIds the caller manages (queueUsers.role='manager', direct
|
|
28
|
+
* or group-materialised) -- for UI that needs a single "is this user a
|
|
29
|
+
* manager of at least one queue" check without gating on the coarser,
|
|
30
|
+
* account-wide `getScope().isManager`.
|
|
31
|
+
*
|
|
32
|
+
* @returns {Promise<Object>} result
|
|
33
|
+
* @returns {string[]} result.queueIds - Queue ids the caller manages
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const { queueIds } = await sdk.taskRouter.cc.getManagedQueues();
|
|
37
|
+
* const isAnyQueueManager = queueIds.length > 0;
|
|
38
|
+
*/
|
|
39
|
+
async getManagedQueues() {
|
|
40
|
+
const result = await internalRequest(
|
|
41
|
+
this.sdk,
|
|
42
|
+
'/taskRouter/queues/managed',
|
|
43
|
+
'GET',
|
|
44
|
+
{},
|
|
45
|
+
);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
26
49
|
/**
|
|
27
50
|
* Get a live Contact Center snapshot (KPIs, per-queue summaries, team roster
|
|
28
51
|
* with active tasks) scoped to a set of queues.
|
|
@@ -50,6 +50,8 @@ export class ParticipantService {
|
|
|
50
50
|
* @param {string} [options.displayName] - Optional display name shown for the external caller (kind 'external')
|
|
51
51
|
* @param {string} [options.note] - Optional note shown to the invitee
|
|
52
52
|
* @param {string} [options.bridgeRole='main'] - Voice bridge role (only 'main' is supported until the media update)
|
|
53
|
+
* @param {boolean} [options.self] - When kind is 'user', true to join the CALLER onto the task themselves (R1 self-join) instead of inviting `userId`
|
|
54
|
+
* @param {string} [options.role] - Self-join role: 'helper' (visible) or 'monitor' (private/observe, gated server-side)
|
|
53
55
|
* @returns {Promise<Object>} { participant, offerId } for kind 'user'; { helpTaskId } for kind 'queue'; { participant } for kind 'external'
|
|
54
56
|
*
|
|
55
57
|
* @example
|
|
@@ -58,6 +60,8 @@ export class ParticipantService {
|
|
|
58
60
|
* await sdk.taskRouter.participants.add({ taskId: 'task_123', kind: 'queue', queueId: 'queue_789' });
|
|
59
61
|
* @example
|
|
60
62
|
* await sdk.taskRouter.participants.add({ taskId: 'task_123', kind: 'external', phoneNumber: '+15551234567', displayName: 'Jane Doe' });
|
|
63
|
+
* @example
|
|
64
|
+
* await sdk.taskRouter.participants.add({ taskId: 'task_123', kind: 'user', self: true, role: 'helper' });
|
|
61
65
|
*/
|
|
62
66
|
async add({
|
|
63
67
|
taskId,
|
|
@@ -70,6 +74,8 @@ export class ParticipantService {
|
|
|
70
74
|
displayName,
|
|
71
75
|
note,
|
|
72
76
|
bridgeRole,
|
|
77
|
+
self,
|
|
78
|
+
role,
|
|
73
79
|
} = {}) {
|
|
74
80
|
this.sdk.validateParams(
|
|
75
81
|
{
|
|
@@ -83,6 +89,8 @@ export class ParticipantService {
|
|
|
83
89
|
displayName,
|
|
84
90
|
note,
|
|
85
91
|
bridgeRole,
|
|
92
|
+
self,
|
|
93
|
+
role,
|
|
86
94
|
},
|
|
87
95
|
{
|
|
88
96
|
taskId: { type: 'string', required: true },
|
|
@@ -95,6 +103,8 @@ export class ParticipantService {
|
|
|
95
103
|
displayName: { type: 'string', required: false },
|
|
96
104
|
note: { type: 'string', required: false },
|
|
97
105
|
bridgeRole: { type: 'string', required: false },
|
|
106
|
+
self: { type: 'boolean', required: false },
|
|
107
|
+
role: { type: 'string', required: false },
|
|
98
108
|
},
|
|
99
109
|
);
|
|
100
110
|
|
|
@@ -107,6 +117,8 @@ export class ParticipantService {
|
|
|
107
117
|
if (displayName !== undefined) params.body.displayName = displayName;
|
|
108
118
|
if (note !== undefined) params.body.note = note;
|
|
109
119
|
if (bridgeRole !== undefined) params.body.bridgeRole = bridgeRole;
|
|
120
|
+
if (self !== undefined) params.body.self = self;
|
|
121
|
+
if (role !== undefined) params.body.role = role;
|
|
110
122
|
|
|
111
123
|
return await internalRequest(
|
|
112
124
|
this.sdk,
|
|
@@ -183,8 +195,43 @@ export class ParticipantService {
|
|
|
183
195
|
);
|
|
184
196
|
}
|
|
185
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Go public: promote the caller's own private (monitor) row to a normal
|
|
200
|
+
* (helper) participant. Self-only -- the server rejects this for any
|
|
201
|
+
* participant row other than the caller's own, and only when that row's
|
|
202
|
+
* current role is 'monitor'. Runs the normal helper-join chain server-side
|
|
203
|
+
* (worker session, task-router join, capacity, Team-channel membership)
|
|
204
|
+
* and unmutes the caller's voice leg when the task has one.
|
|
205
|
+
*
|
|
206
|
+
* @param {Object} options - Options
|
|
207
|
+
* @param {string} options.taskId - Task ID
|
|
208
|
+
* @param {string} options.participantId - The caller's own (monitor) participant ID
|
|
209
|
+
* @returns {Promise<Object>}
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* await sdk.taskRouter.participants.goPublic({ taskId: 'task_123', participantId: 'tp_456' });
|
|
213
|
+
*/
|
|
214
|
+
async goPublic({ taskId, participantId } = {}) {
|
|
215
|
+
this.sdk.validateParams(
|
|
216
|
+
{ taskId, participantId },
|
|
217
|
+
{
|
|
218
|
+
taskId: { type: 'string', required: true },
|
|
219
|
+
participantId: { type: 'string', required: true },
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return await internalRequest(
|
|
224
|
+
this.sdk,
|
|
225
|
+
`/taskRouter/tasks/${taskId}/participants/${participantId}`,
|
|
226
|
+
'PATCH',
|
|
227
|
+
{ body: { role: 'helper' } },
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
186
231
|
/**
|
|
187
232
|
* Remove a participant: drop them (owner/manager) or leave (self).
|
|
233
|
+
* Also used for a private (monitor) row's self-leave -- the server routes
|
|
234
|
+
* a caller's own row through the monitor leave/hangup path automatically.
|
|
188
235
|
*
|
|
189
236
|
* @param {Object} options - Options
|
|
190
237
|
* @param {string} options.taskId - Task ID
|
|
@@ -193,6 +240,9 @@ export class ParticipantService {
|
|
|
193
240
|
*
|
|
194
241
|
* @example
|
|
195
242
|
* await sdk.taskRouter.participants.remove({ taskId: 'task_123', participantId: 'tp_456' });
|
|
243
|
+
* @example
|
|
244
|
+
* // Self-leave a private (monitor) row -- same method, own participantId
|
|
245
|
+
* await sdk.taskRouter.participants.leave({ taskId: 'task_123', participantId: 'tp_456' });
|
|
196
246
|
*/
|
|
197
247
|
async remove({ taskId, participantId }) {
|
|
198
248
|
this.sdk.validateParams(
|
|
@@ -297,4 +347,22 @@ export class ParticipantService {
|
|
|
297
347
|
'GET',
|
|
298
348
|
);
|
|
299
349
|
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Leave the caller's own participant row (helper or private/monitor).
|
|
353
|
+
* Alias for {@link remove} scoped to your own row -- the server enforces
|
|
354
|
+
* self-only removal for a monitor row via the same DELETE route (media
|
|
355
|
+
* leg hangup, row left, `task_private_leave` audit for a monitor row).
|
|
356
|
+
*
|
|
357
|
+
* @param {Object} options - Options
|
|
358
|
+
* @param {string} options.taskId - Task ID
|
|
359
|
+
* @param {string} options.participantId - The caller's own participant ID
|
|
360
|
+
* @returns {Promise<Object>}
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* await sdk.taskRouter.participants.leave({ taskId: 'task_123', participantId: 'tp_456' });
|
|
364
|
+
*/
|
|
365
|
+
async leave({ taskId, participantId }) {
|
|
366
|
+
return await this.remove({ taskId, participantId });
|
|
367
|
+
}
|
|
300
368
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { internalRequest } from '../../base.js';
|
|
2
|
+
import { taskWorkspaceMethods } from './TaskWorkspaceMethods.js';
|
|
2
3
|
export class TaskService {
|
|
3
4
|
constructor(sdk) {
|
|
4
5
|
this.sdk = sdk;
|
|
@@ -1154,3 +1155,7 @@ export class TaskService {
|
|
|
1154
1155
|
);
|
|
1155
1156
|
}
|
|
1156
1157
|
}
|
|
1158
|
+
|
|
1159
|
+
// CC task-workspace methods (claim/observe/unobserve/observers/take/access)
|
|
1160
|
+
// live in TaskWorkspaceMethods.js to keep this file from growing further.
|
|
1161
|
+
Object.assign(TaskService.prototype, taskWorkspaceMethods);
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { internalRequest } from '../../base.js';
|
|
2
|
+
|
|
3
|
+
// CC task-workspace methods (claim / observe / self-join-derived access / take)
|
|
4
|
+
// mixed onto TaskService.prototype. Split into its own file to keep
|
|
5
|
+
// TaskService.js from growing further past the 400-line guideline.
|
|
6
|
+
export const taskWorkspaceMethods = {
|
|
7
|
+
/**
|
|
8
|
+
* Claim an unassigned task (pending/waiting/parked with no assignee).
|
|
9
|
+
* Force-assigns the task to the caller's worker with a normal ring/offer
|
|
10
|
+
* (not assign-connected) -- the caller still sees the usual offer card
|
|
11
|
+
* and accepts manually.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} options - Parameters
|
|
14
|
+
* @param {string} options.taskId - The task ID to claim (required)
|
|
15
|
+
* @returns {Promise<{taskId:string, workerId:string, status:string}>}
|
|
16
|
+
*/
|
|
17
|
+
async claim(options = {}) {
|
|
18
|
+
const { taskId } = options;
|
|
19
|
+
|
|
20
|
+
this.sdk.validateParams(
|
|
21
|
+
{ taskId },
|
|
22
|
+
{ taskId: { type: 'string', required: true } },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return await internalRequest(
|
|
26
|
+
this.sdk,
|
|
27
|
+
`/taskRouter/tasks/${taskId}/claim`,
|
|
28
|
+
'POST',
|
|
29
|
+
{},
|
|
30
|
+
);
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Send an observer-presence heartbeat for a task. Call on an interval
|
|
35
|
+
* (< 45s) while a manager/queue member is watching a task read-only;
|
|
36
|
+
* membership expires automatically if heartbeats stop.
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} options - Parameters
|
|
39
|
+
* @param {string} options.taskId - The task ID to observe (required)
|
|
40
|
+
* @returns {Promise<{ok:boolean}>}
|
|
41
|
+
*/
|
|
42
|
+
async observe(options = {}) {
|
|
43
|
+
const { taskId } = options;
|
|
44
|
+
|
|
45
|
+
this.sdk.validateParams(
|
|
46
|
+
{ taskId },
|
|
47
|
+
{ taskId: { type: 'string', required: true } },
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return await internalRequest(
|
|
51
|
+
this.sdk,
|
|
52
|
+
`/taskRouter/tasks/${taskId}/observe`,
|
|
53
|
+
'POST',
|
|
54
|
+
{},
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Stop observing a task (removes the caller from the observer set).
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} options - Parameters
|
|
62
|
+
* @param {string} options.taskId - The task ID to stop observing (required)
|
|
63
|
+
* @returns {Promise<{ok:boolean}>}
|
|
64
|
+
*/
|
|
65
|
+
async unobserve(options = {}) {
|
|
66
|
+
const { taskId } = options;
|
|
67
|
+
|
|
68
|
+
this.sdk.validateParams(
|
|
69
|
+
{ taskId },
|
|
70
|
+
{ taskId: { type: 'string', required: true } },
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return await internalRequest(
|
|
74
|
+
this.sdk,
|
|
75
|
+
`/taskRouter/tasks/${taskId}/observe`,
|
|
76
|
+
'DELETE',
|
|
77
|
+
{},
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* List the current observers of a task. Manager-only.
|
|
83
|
+
*
|
|
84
|
+
* @param {Object} options - Parameters
|
|
85
|
+
* @param {string} options.taskId - The task ID (required)
|
|
86
|
+
* @returns {Promise<Array<{userId:string, name:string, since:string}>>}
|
|
87
|
+
*/
|
|
88
|
+
async observers(options = {}) {
|
|
89
|
+
const { taskId } = options;
|
|
90
|
+
|
|
91
|
+
this.sdk.validateParams(
|
|
92
|
+
{ taskId },
|
|
93
|
+
{ taskId: { type: 'string', required: true } },
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return await internalRequest(
|
|
97
|
+
this.sdk,
|
|
98
|
+
`/taskRouter/tasks/${taskId}/observers`,
|
|
99
|
+
'GET',
|
|
100
|
+
{},
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Seize ownership of a currently-assigned task away from its owner.
|
|
106
|
+
* Queue-manager only; reuses the normal transfer-offer chain with the
|
|
107
|
+
* caller as target, so the caller gets the usual offer card and accepts
|
|
108
|
+
* manually (ownership moves on accept, not on this call).
|
|
109
|
+
*
|
|
110
|
+
* @param {Object} options - Parameters
|
|
111
|
+
* @param {string} options.taskId - The task ID to take (required)
|
|
112
|
+
* @returns {Promise<{offerId:string}>}
|
|
113
|
+
*/
|
|
114
|
+
async take(options = {}) {
|
|
115
|
+
const { taskId } = options;
|
|
116
|
+
|
|
117
|
+
this.sdk.validateParams(
|
|
118
|
+
{ taskId },
|
|
119
|
+
{ taskId: { type: 'string', required: true } },
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return await internalRequest(
|
|
123
|
+
this.sdk,
|
|
124
|
+
`/taskRouter/tasks/${taskId}/take`,
|
|
125
|
+
'POST',
|
|
126
|
+
{},
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Compute the caller's entry-point access mode for a task, used to drive
|
|
132
|
+
* which open/join/take/claim actions the client offers.
|
|
133
|
+
*
|
|
134
|
+
* @param {Object} options - Parameters
|
|
135
|
+
* @param {string} options.taskId - The task ID (required)
|
|
136
|
+
* @returns {Promise<{
|
|
137
|
+
* mode: 'owner'|'helper'|'helperLimited'|'claim'|'observe'|'none',
|
|
138
|
+
* canJoin: boolean,
|
|
139
|
+
* canPrivate: boolean,
|
|
140
|
+
* canTake: boolean,
|
|
141
|
+
* canClaim: boolean,
|
|
142
|
+
* isQueueManager: boolean,
|
|
143
|
+
* isQueueMember: boolean,
|
|
144
|
+
* ownerName: string|null,
|
|
145
|
+
* }>}
|
|
146
|
+
*/
|
|
147
|
+
async access(options = {}) {
|
|
148
|
+
const { taskId } = options;
|
|
149
|
+
|
|
150
|
+
this.sdk.validateParams(
|
|
151
|
+
{ taskId },
|
|
152
|
+
{ taskId: { type: 'string', required: true } },
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return await internalRequest(
|
|
156
|
+
this.sdk,
|
|
157
|
+
`/taskRouter/tasks/${taskId}/access`,
|
|
158
|
+
'GET',
|
|
159
|
+
{},
|
|
160
|
+
);
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Exact per-status task counts (GAP B, cc-task-workspace-plan.md §2.2) --
|
|
165
|
+
* replaces client-side approximations that count a page-size-capped
|
|
166
|
+
* results list. ACL = logged-in user, scoped to the caller's account.
|
|
167
|
+
*
|
|
168
|
+
* @param {Object} options - Parameters
|
|
169
|
+
* @param {string[]} options.statuses - Task statuses to count, e.g. ['parked']
|
|
170
|
+
* @param {boolean} [options.mine] - Scope to the caller's own workerId
|
|
171
|
+
* (same filter as the mini-card strip's owned-tasks fetch)
|
|
172
|
+
* @returns {Promise<{counts: Object<string, number>}>}
|
|
173
|
+
*/
|
|
174
|
+
async counts(options = {}) {
|
|
175
|
+
const { statuses, mine } = options;
|
|
176
|
+
|
|
177
|
+
this.sdk.validateParams(
|
|
178
|
+
{ statuses },
|
|
179
|
+
{ statuses: { type: 'array', required: true } },
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return await internalRequest(this.sdk, `/taskRouter/tasks/counts`, 'GET', {
|
|
183
|
+
query: {
|
|
184
|
+
statuses: statuses.join(','),
|
|
185
|
+
...(mine !== undefined ? { mine } : {}),
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
};
|