@cosmoops/connect-sdk 0.43.0 → 0.44.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/CHANGELOG.md +58 -0
- package/dist/cjs/client/components/call-button.js +38 -4
- package/dist/cjs/server/client.js +1 -1
- package/dist/cjs/server/resources/calls.js +26 -0
- package/dist/esm/client/components/call-button.js +38 -4
- package/dist/esm/server/client.js +1 -1
- package/dist/esm/server/resources/calls.js +26 -0
- package/dist/types/client/components/call-button.d.ts +6 -0
- package/dist/types/server/client.d.ts +1 -1
- package/dist/types/server/resources/calls.d.ts +26 -1
- package/dist/types/types.d.ts +20 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,51 @@
|
|
|
3
3
|
`@cosmoops/connect-sdk` follows semver, with the 0.x convention that **minor
|
|
4
4
|
versions may carry breaking changes** and patch versions never do.
|
|
5
5
|
|
|
6
|
+
## 0.44.0
|
|
7
|
+
|
|
8
|
+
### `ConnectCall.dialledAt`: was the person's phone rung?
|
|
9
|
+
|
|
10
|
+
`unansweredReason: "cancelled"` covers two calls that need opposite handling:
|
|
11
|
+
your user hanging up while the person's phone rang, and your user giving up
|
|
12
|
+
before anybody was rung. The first left a missed call on the person's phone;
|
|
13
|
+
the second never happened. Nothing on the call told them apart, so an app
|
|
14
|
+
reading a call back after hang-up had to skip both and wait for the webhook.
|
|
15
|
+
|
|
16
|
+
- **`dialledAt`** on `ConnectCall` (from `calls.get()` and `calls.list()`) is
|
|
17
|
+
when Connect asked the carrier to ring the person, on a `member` call. Set
|
|
18
|
+
means a cancelled call was a missed one - file it as missed, exactly as
|
|
19
|
+
`call.missed` would. `null` means nobody was rung - file nothing. Connect's
|
|
20
|
+
webhooks already follow the same rule.
|
|
21
|
+
- It is on the record before any hang-up can be, so it never needs a retry.
|
|
22
|
+
`null` on agent calls and on member calls placed before this release.
|
|
23
|
+
|
|
24
|
+
Nothing to wire if you file calls from `call.completed` / `call.missed` only.
|
|
25
|
+
|
|
26
|
+
### `calls.list({ placedByApplication: true })`: the calls your UI placed
|
|
27
|
+
|
|
28
|
+
A call placed with `<ConnectCallButton>` leaves from the workspace's own
|
|
29
|
+
number, which is assigned to no application - so `calls.list()` never
|
|
30
|
+
returned it, and an app whose webhook was delivered to another deployment had
|
|
31
|
+
no way to find out the call happened. `placedByApplication: true` (with an
|
|
32
|
+
optional `workspaceId`) lists exactly those calls, newest first: reconcile a
|
|
33
|
+
call log from it.
|
|
34
|
+
|
|
35
|
+
### Call recording, from inside your app
|
|
36
|
+
|
|
37
|
+
`connect.calls.getCallSettings({ asUser })` and
|
|
38
|
+
`connect.calls.setCallRecording({ recordingEnabled, asUser })` read and switch
|
|
39
|
+
the workspace's call recording - the same switch as Connect's Calling screen.
|
|
40
|
+
`canManage` is the member's own `channel:manage`; draw the switch disabled
|
|
41
|
+
when it is false, because a save is refused `403`. The change applies to calls
|
|
42
|
+
started afterwards and is audited against the member. Needs `calls:place`,
|
|
43
|
+
which a key placing calls already holds.
|
|
44
|
+
|
|
45
|
+
### The "Call ended" card shows the minutes left
|
|
46
|
+
|
|
47
|
+
After a call, `<ConnectCallProvider>` re-reads the calling balance and the
|
|
48
|
+
card now says how many minutes are left once that read lands, staying a few
|
|
49
|
+
seconds longer to show it. Nothing to wire.
|
|
50
|
+
|
|
6
51
|
## 0.43.0
|
|
7
52
|
|
|
8
53
|
### Recorded calls: transcript, summary, intent and action items
|
|
@@ -29,6 +74,19 @@ minute to the call - nothing to wire on your side to offer it.
|
|
|
29
74
|
|
|
30
75
|
## 0.42.0
|
|
31
76
|
|
|
77
|
+
### `connect.whatsapp` - your own approved templates, to a workspace's customers
|
|
78
|
+
|
|
79
|
+
- **`whatsapp.send({ workspaceId, template, to, values, consent })`** sends one
|
|
80
|
+
of your application's Meta-approved templates - named by slug, resolved by
|
|
81
|
+
Connect as `app_<yourApplicationKey>_<slug>` - through Connect's WhatsApp
|
|
82
|
+
sender. Needs the `whatsapp:send` scope.
|
|
83
|
+
- **`whatsapp.template(slug)`** answers `live` and the `variables` a send must
|
|
84
|
+
carry. Offer WhatsApp on a screen only when `live` is true.
|
|
85
|
+
- `consent.collectedVia` records an opt-in your application collected, only
|
|
86
|
+
when Connect has none for the number. A number that opted out stays opted
|
|
87
|
+
out: the send fails with `opted_out`.
|
|
88
|
+
|
|
89
|
+
|
|
32
90
|
### Ask AI explains failures instead of printing them
|
|
33
91
|
|
|
34
92
|
Nothing to wire - update and the panel behaves better on a bad day.
|
|
@@ -57,10 +57,15 @@ function useConnectCall() {
|
|
|
57
57
|
}
|
|
58
58
|
/** How long the SDK may take to sign in before the attempt is abandoned. */
|
|
59
59
|
const LOGIN_TIMEOUT_MS = 15_000;
|
|
60
|
-
/** How long the "Call ended" card stays
|
|
60
|
+
/** How long the "Call ended" card stays once it is showing the minutes left. */
|
|
61
61
|
const ENDED_CARD_MS = 4_000;
|
|
62
62
|
/** How long after a call to re-read the balance - the carrier's report settles it. */
|
|
63
63
|
const AVAILABILITY_SETTLE_MS = 3_000;
|
|
64
|
+
/**
|
|
65
|
+
* The longest the card waits for that re-read before going anyway - a read
|
|
66
|
+
* that failed must not leave a finished call on screen.
|
|
67
|
+
*/
|
|
68
|
+
const ENDED_CARD_MAX_MS = AVAILABILITY_SETTLE_MS + 6_000;
|
|
64
69
|
function messageFromCause(cause) {
|
|
65
70
|
if (typeof cause === "string" && cause.trim())
|
|
66
71
|
return cause.trim();
|
|
@@ -86,6 +91,8 @@ function ConnectCallProvider({ children, onCallEnded, onError, className, }) {
|
|
|
86
91
|
const targetRef = (0, react_1.useRef)(null);
|
|
87
92
|
const callIdRef = (0, react_1.useRef)(null);
|
|
88
93
|
const endedRef = (0, react_1.useRef)(false);
|
|
94
|
+
/** Set when a call ends; the next balance read is the one the card shows. */
|
|
95
|
+
const awaitingBalanceRef = (0, react_1.useRef)(false);
|
|
89
96
|
const dismissTimer = (0, react_1.useRef)(null);
|
|
90
97
|
// Read inside SDK event handlers wired once, so they always see the latest.
|
|
91
98
|
const onCallEndedRef = (0, react_1.useRef)(onCallEnded);
|
|
@@ -118,15 +125,32 @@ function ConnectCallProvider({ children, onCallEnded, onError, className, }) {
|
|
|
118
125
|
: current);
|
|
119
126
|
if (dismissTimer.current)
|
|
120
127
|
clearTimeout(dismissTimer.current);
|
|
121
|
-
dismissTimer.current = setTimeout(() => setCall(null),
|
|
128
|
+
dismissTimer.current = setTimeout(() => setCall(null), ENDED_CARD_MAX_MS);
|
|
122
129
|
// The charge lands a moment after the line drops, so the minutes shown
|
|
123
|
-
// are re-read once it has had time to
|
|
130
|
+
// are re-read once it has had time to - and the card waits for that read
|
|
131
|
+
// (see the effect below) rather than showing what was left before the call.
|
|
132
|
+
awaitingBalanceRef.current = true;
|
|
124
133
|
setTimeout(() => refreshRef.current(), AVAILABILITY_SETTLE_MS);
|
|
125
134
|
if (target) {
|
|
126
135
|
target.onEnded?.({ callId });
|
|
127
136
|
onCallEndedRef.current?.({ callId, phone: target.phone });
|
|
128
137
|
}
|
|
129
138
|
}, []);
|
|
139
|
+
/*
|
|
140
|
+
* The re-read after a call has landed: put what is left on the "Call ended"
|
|
141
|
+
* card, and give it its own few seconds from here, so the number is on
|
|
142
|
+
* screen long enough to read.
|
|
143
|
+
*/
|
|
144
|
+
(0, react_1.useEffect)(() => {
|
|
145
|
+
if (!awaitingBalanceRef.current || !availability)
|
|
146
|
+
return;
|
|
147
|
+
awaitingBalanceRef.current = false;
|
|
148
|
+
const minutesLeft = availability.availableMinutes;
|
|
149
|
+
setCall((current) => (current?.phase === "ended" ? { ...current, minutesLeft } : current));
|
|
150
|
+
if (dismissTimer.current)
|
|
151
|
+
clearTimeout(dismissTimer.current);
|
|
152
|
+
dismissTimer.current = setTimeout(() => setCall(null), ENDED_CARD_MS);
|
|
153
|
+
}, [availability]);
|
|
130
154
|
const wire = (0, react_1.useCallback)((client) => {
|
|
131
155
|
const ringing = () => setCall((current) => (current ? { ...current, phase: "ringing" } : current));
|
|
132
156
|
client.on("onCalling", ringing);
|
|
@@ -200,6 +224,7 @@ function ConnectCallProvider({ children, onCallEnded, onError, className, }) {
|
|
|
200
224
|
if (dismissTimer.current)
|
|
201
225
|
clearTimeout(dismissTimer.current);
|
|
202
226
|
targetRef.current = target;
|
|
227
|
+
awaitingBalanceRef.current = false;
|
|
203
228
|
callIdRef.current = null;
|
|
204
229
|
placedRef.current = false;
|
|
205
230
|
endedRef.current = false;
|
|
@@ -212,6 +237,7 @@ function ConnectCallProvider({ children, onCallEnded, onError, className, }) {
|
|
|
212
237
|
endedAfterSeconds: null,
|
|
213
238
|
muted: false,
|
|
214
239
|
notice: null,
|
|
240
|
+
minutesLeft: null,
|
|
215
241
|
});
|
|
216
242
|
try {
|
|
217
243
|
const client = await signIn();
|
|
@@ -383,7 +409,15 @@ function ConnectActiveCallBar({ call, onHangUp, onToggleMute, className, }) {
|
|
|
383
409
|
: call.phase === "ended" && call.endedAfterSeconds !== null
|
|
384
410
|
? `Call ended · ${formatSeconds(call.endedAfterSeconds)}`
|
|
385
411
|
: PHASE_LABEL[call.phase];
|
|
386
|
-
|
|
412
|
+
const remaining = call.phase === "ended" ? minutesLeftLabel(call.minutesLeft) : null;
|
|
413
|
+
return ((0, jsx_runtime_1.jsx)("div", { role: "status", "aria-live": "polite", className: (0, cn_js_1.cn)("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [(0, jsx_runtime_1.jsx)("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent", children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "truncate text-sm font-medium", children: call.label }), (0, jsx_runtime_1.jsxs)("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? (0, jsx_runtime_1.jsx)(lucide_react_1.MicOff, { className: "size-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Mic, { className: "size-4" }) }), (0, jsx_runtime_1.jsx)(react_2.Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: (0, jsx_runtime_1.jsx)(lucide_react_1.PhoneOff, { className: "size-4" }) })] }))] }) }));
|
|
414
|
+
}
|
|
415
|
+
/** "42 min of calling left", once the post-call balance read has landed. */
|
|
416
|
+
function minutesLeftLabel(minutes) {
|
|
417
|
+
if (minutes === null)
|
|
418
|
+
return null;
|
|
419
|
+
const whole = Math.max(0, Math.floor(minutes));
|
|
420
|
+
return whole === 0 ? "No calling minutes left" : `${whole} min of calling left`;
|
|
387
421
|
}
|
|
388
422
|
/** Seconds since `since`, ticking once a second. `0` while there is nothing to time. */
|
|
389
423
|
function useElapsedSeconds(since) {
|
|
@@ -31,6 +31,8 @@ class CallsResource {
|
|
|
31
31
|
path: "/calls",
|
|
32
32
|
query: {
|
|
33
33
|
channelId: input.channelId,
|
|
34
|
+
placedByApplication: input.placedByApplication ? "true" : undefined,
|
|
35
|
+
workspaceId: input.workspaceId,
|
|
34
36
|
cursor: input.cursor,
|
|
35
37
|
limit: input.limit === undefined ? undefined : String(input.limit),
|
|
36
38
|
},
|
|
@@ -78,6 +80,30 @@ class CallsResource {
|
|
|
78
80
|
headers: (0, acting_as_js_1.actingAs)(input.asUser),
|
|
79
81
|
});
|
|
80
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether the workspace records its member calls, and whether this member
|
|
85
|
+
* may change that. The same switch as Connect's Calling screen.
|
|
86
|
+
*/
|
|
87
|
+
getCallSettings(input) {
|
|
88
|
+
return (0, http_js_1.request)(this.transport, {
|
|
89
|
+
method: "GET",
|
|
90
|
+
path: "/calls/settings",
|
|
91
|
+
headers: (0, acting_as_js_1.actingAs)(input.asUser),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Switches recording on or off for calls started from now. Throws
|
|
96
|
+
* `ConnectError` 403 when the member lacks `channel:manage`; the change is
|
|
97
|
+
* audited against them. Answers the settings as they now stand.
|
|
98
|
+
*/
|
|
99
|
+
setCallRecording(input) {
|
|
100
|
+
return (0, http_js_1.request)(this.transport, {
|
|
101
|
+
method: "PUT",
|
|
102
|
+
path: "/calls/settings",
|
|
103
|
+
json: { recordingEnabled: input.recordingEnabled },
|
|
104
|
+
headers: (0, acting_as_js_1.actingAs)(input.asUser),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
81
107
|
/** A carrier token the member's browser signs in to the phone network with. */
|
|
82
108
|
getBrowserCallingToken(input) {
|
|
83
109
|
return (0, http_js_1.request)(this.transport, {
|
|
@@ -17,10 +17,15 @@ export function useConnectCall() {
|
|
|
17
17
|
}
|
|
18
18
|
/** How long the SDK may take to sign in before the attempt is abandoned. */
|
|
19
19
|
const LOGIN_TIMEOUT_MS = 15_000;
|
|
20
|
-
/** How long the "Call ended" card stays
|
|
20
|
+
/** How long the "Call ended" card stays once it is showing the minutes left. */
|
|
21
21
|
const ENDED_CARD_MS = 4_000;
|
|
22
22
|
/** How long after a call to re-read the balance - the carrier's report settles it. */
|
|
23
23
|
const AVAILABILITY_SETTLE_MS = 3_000;
|
|
24
|
+
/**
|
|
25
|
+
* The longest the card waits for that re-read before going anyway - a read
|
|
26
|
+
* that failed must not leave a finished call on screen.
|
|
27
|
+
*/
|
|
28
|
+
const ENDED_CARD_MAX_MS = AVAILABILITY_SETTLE_MS + 6_000;
|
|
24
29
|
function messageFromCause(cause) {
|
|
25
30
|
if (typeof cause === "string" && cause.trim())
|
|
26
31
|
return cause.trim();
|
|
@@ -46,6 +51,8 @@ export function ConnectCallProvider({ children, onCallEnded, onError, className,
|
|
|
46
51
|
const targetRef = useRef(null);
|
|
47
52
|
const callIdRef = useRef(null);
|
|
48
53
|
const endedRef = useRef(false);
|
|
54
|
+
/** Set when a call ends; the next balance read is the one the card shows. */
|
|
55
|
+
const awaitingBalanceRef = useRef(false);
|
|
49
56
|
const dismissTimer = useRef(null);
|
|
50
57
|
// Read inside SDK event handlers wired once, so they always see the latest.
|
|
51
58
|
const onCallEndedRef = useRef(onCallEnded);
|
|
@@ -78,15 +85,32 @@ export function ConnectCallProvider({ children, onCallEnded, onError, className,
|
|
|
78
85
|
: current);
|
|
79
86
|
if (dismissTimer.current)
|
|
80
87
|
clearTimeout(dismissTimer.current);
|
|
81
|
-
dismissTimer.current = setTimeout(() => setCall(null),
|
|
88
|
+
dismissTimer.current = setTimeout(() => setCall(null), ENDED_CARD_MAX_MS);
|
|
82
89
|
// The charge lands a moment after the line drops, so the minutes shown
|
|
83
|
-
// are re-read once it has had time to
|
|
90
|
+
// are re-read once it has had time to - and the card waits for that read
|
|
91
|
+
// (see the effect below) rather than showing what was left before the call.
|
|
92
|
+
awaitingBalanceRef.current = true;
|
|
84
93
|
setTimeout(() => refreshRef.current(), AVAILABILITY_SETTLE_MS);
|
|
85
94
|
if (target) {
|
|
86
95
|
target.onEnded?.({ callId });
|
|
87
96
|
onCallEndedRef.current?.({ callId, phone: target.phone });
|
|
88
97
|
}
|
|
89
98
|
}, []);
|
|
99
|
+
/*
|
|
100
|
+
* The re-read after a call has landed: put what is left on the "Call ended"
|
|
101
|
+
* card, and give it its own few seconds from here, so the number is on
|
|
102
|
+
* screen long enough to read.
|
|
103
|
+
*/
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!awaitingBalanceRef.current || !availability)
|
|
106
|
+
return;
|
|
107
|
+
awaitingBalanceRef.current = false;
|
|
108
|
+
const minutesLeft = availability.availableMinutes;
|
|
109
|
+
setCall((current) => (current?.phase === "ended" ? { ...current, minutesLeft } : current));
|
|
110
|
+
if (dismissTimer.current)
|
|
111
|
+
clearTimeout(dismissTimer.current);
|
|
112
|
+
dismissTimer.current = setTimeout(() => setCall(null), ENDED_CARD_MS);
|
|
113
|
+
}, [availability]);
|
|
90
114
|
const wire = useCallback((client) => {
|
|
91
115
|
const ringing = () => setCall((current) => (current ? { ...current, phase: "ringing" } : current));
|
|
92
116
|
client.on("onCalling", ringing);
|
|
@@ -160,6 +184,7 @@ export function ConnectCallProvider({ children, onCallEnded, onError, className,
|
|
|
160
184
|
if (dismissTimer.current)
|
|
161
185
|
clearTimeout(dismissTimer.current);
|
|
162
186
|
targetRef.current = target;
|
|
187
|
+
awaitingBalanceRef.current = false;
|
|
163
188
|
callIdRef.current = null;
|
|
164
189
|
placedRef.current = false;
|
|
165
190
|
endedRef.current = false;
|
|
@@ -172,6 +197,7 @@ export function ConnectCallProvider({ children, onCallEnded, onError, className,
|
|
|
172
197
|
endedAfterSeconds: null,
|
|
173
198
|
muted: false,
|
|
174
199
|
notice: null,
|
|
200
|
+
minutesLeft: null,
|
|
175
201
|
});
|
|
176
202
|
try {
|
|
177
203
|
const client = await signIn();
|
|
@@ -343,7 +369,15 @@ export function ConnectActiveCallBar({ call, onHangUp, onToggleMute, className,
|
|
|
343
369
|
: call.phase === "ended" && call.endedAfterSeconds !== null
|
|
344
370
|
? `Call ended · ${formatSeconds(call.endedAfterSeconds)}`
|
|
345
371
|
: PHASE_LABEL[call.phase];
|
|
346
|
-
|
|
372
|
+
const remaining = call.phase === "ended" ? minutesLeftLabel(call.minutesLeft) : null;
|
|
373
|
+
return (_jsx("div", { role: "status", "aria-live": "polite", className: cn("fixed inset-x-0 bottom-4 z-50 flex justify-center px-4", className), children: _jsxs("div", { className: "flex w-full max-w-sm items-center gap-3 rounded-2xl border border-separator bg-surface p-3 shadow-lg", children: [_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-accent-soft text-accent", children: _jsx(PhoneOutgoing, { "aria-hidden": true, className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: call.label }), _jsxs("p", { className: "truncate text-xs text-muted tabular-nums", children: [status, call.notice ? ` · ${call.notice}` : call.label === call.number ? "" : ` · ${call.number}`] }), remaining && _jsx("p", { className: "truncate text-xs text-muted tabular-nums", children: remaining })] }), live && (_jsxs(_Fragment, { children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "secondary", "aria-label": call.muted ? "Unmute" : "Mute", isDisabled: call.phase !== "in-call", onPress: onToggleMute, children: call.muted ? _jsx(MicOff, { className: "size-4" }) : _jsx(Mic, { className: "size-4" }) }), _jsx(Button, { isIconOnly: true, size: "sm", variant: "danger", "aria-label": "Hang up", onPress: onHangUp, children: _jsx(PhoneOff, { className: "size-4" }) })] }))] }) }));
|
|
374
|
+
}
|
|
375
|
+
/** "42 min of calling left", once the post-call balance read has landed. */
|
|
376
|
+
function minutesLeftLabel(minutes) {
|
|
377
|
+
if (minutes === null)
|
|
378
|
+
return null;
|
|
379
|
+
const whole = Math.max(0, Math.floor(minutes));
|
|
380
|
+
return whole === 0 ? "No calling minutes left" : `${whole} min of calling left`;
|
|
347
381
|
}
|
|
348
382
|
/** Seconds since `since`, ticking once a second. `0` while there is nothing to time. */
|
|
349
383
|
function useElapsedSeconds(since) {
|
|
@@ -28,6 +28,8 @@ export class CallsResource {
|
|
|
28
28
|
path: "/calls",
|
|
29
29
|
query: {
|
|
30
30
|
channelId: input.channelId,
|
|
31
|
+
placedByApplication: input.placedByApplication ? "true" : undefined,
|
|
32
|
+
workspaceId: input.workspaceId,
|
|
31
33
|
cursor: input.cursor,
|
|
32
34
|
limit: input.limit === undefined ? undefined : String(input.limit),
|
|
33
35
|
},
|
|
@@ -75,6 +77,30 @@ export class CallsResource {
|
|
|
75
77
|
headers: actingAs(input.asUser),
|
|
76
78
|
});
|
|
77
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Whether the workspace records its member calls, and whether this member
|
|
82
|
+
* may change that. The same switch as Connect's Calling screen.
|
|
83
|
+
*/
|
|
84
|
+
getCallSettings(input) {
|
|
85
|
+
return request(this.transport, {
|
|
86
|
+
method: "GET",
|
|
87
|
+
path: "/calls/settings",
|
|
88
|
+
headers: actingAs(input.asUser),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Switches recording on or off for calls started from now. Throws
|
|
93
|
+
* `ConnectError` 403 when the member lacks `channel:manage`; the change is
|
|
94
|
+
* audited against them. Answers the settings as they now stand.
|
|
95
|
+
*/
|
|
96
|
+
setCallRecording(input) {
|
|
97
|
+
return request(this.transport, {
|
|
98
|
+
method: "PUT",
|
|
99
|
+
path: "/calls/settings",
|
|
100
|
+
json: { recordingEnabled: input.recordingEnabled },
|
|
101
|
+
headers: actingAs(input.asUser),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
78
104
|
/** A carrier token the member's browser signs in to the phone network with. */
|
|
79
105
|
getBrowserCallingToken(input) {
|
|
80
106
|
return request(this.transport, {
|
|
@@ -45,6 +45,12 @@ interface ActiveCall {
|
|
|
45
45
|
endedAfterSeconds: number | null;
|
|
46
46
|
muted: boolean;
|
|
47
47
|
notice: string | null;
|
|
48
|
+
/**
|
|
49
|
+
* Calling minutes left once this call has been charged - filled on the
|
|
50
|
+
* "Call ended" card by the balance re-read that follows it, and `null`
|
|
51
|
+
* until that read lands, so the card never shows the pre-call figure.
|
|
52
|
+
*/
|
|
53
|
+
minutesLeft: number | null;
|
|
48
54
|
}
|
|
49
55
|
/** The number to ring, and how to name the person on the in-call bar. */
|
|
50
56
|
export interface ConnectCallTarget {
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { type TransportOptions } from "../http.js";
|
|
2
|
-
import type { BrowserCallingToken, CallingAvailability, CallDetail, CallsPage, StartedBrowserCall } from "../../types.js";
|
|
2
|
+
import type { BrowserCallingToken, CallingAvailability, CallSettings, CallDetail, CallsPage, StartedBrowserCall } from "../../types.js";
|
|
3
3
|
export interface ListCallsInput {
|
|
4
4
|
/** Narrow to one channel. A channel belonging to another application simply matches nothing. */
|
|
5
5
|
channelId?: string;
|
|
6
|
+
/**
|
|
7
|
+
* `true` for the member calls your UI placed (`<ConnectCallButton>`) instead
|
|
8
|
+
* of the calls on your channels. Those leave from the workspace's own
|
|
9
|
+
* number, so the default list never contains them - this is the list to
|
|
10
|
+
* reconcile a timeline from when a webhook went to another deployment.
|
|
11
|
+
*/
|
|
12
|
+
placedByApplication?: boolean;
|
|
13
|
+
/** With `placedByApplication`, one workspace's calls. */
|
|
14
|
+
workspaceId?: string;
|
|
6
15
|
/** From a previous page's `nextCursor`. */
|
|
7
16
|
cursor?: string;
|
|
8
17
|
/** 1-100, default 50. */
|
|
@@ -50,6 +59,22 @@ export declare class CallsResource {
|
|
|
50
59
|
getCallingAvailability(input: {
|
|
51
60
|
asUser: string;
|
|
52
61
|
}): Promise<CallingAvailability>;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the workspace records its member calls, and whether this member
|
|
64
|
+
* may change that. The same switch as Connect's Calling screen.
|
|
65
|
+
*/
|
|
66
|
+
getCallSettings(input: {
|
|
67
|
+
asUser: string;
|
|
68
|
+
}): Promise<CallSettings>;
|
|
69
|
+
/**
|
|
70
|
+
* Switches recording on or off for calls started from now. Throws
|
|
71
|
+
* `ConnectError` 403 when the member lacks `channel:manage`; the change is
|
|
72
|
+
* audited against them. Answers the settings as they now stand.
|
|
73
|
+
*/
|
|
74
|
+
setCallRecording(input: {
|
|
75
|
+
recordingEnabled: boolean;
|
|
76
|
+
asUser: string;
|
|
77
|
+
}): Promise<CallSettings>;
|
|
53
78
|
/** A carrier token the member's browser signs in to the phone network with. */
|
|
54
79
|
getBrowserCallingToken(input: {
|
|
55
80
|
asUser: string;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -2858,6 +2858,14 @@ export interface ConnectCall {
|
|
|
2858
2858
|
* nobody answered.
|
|
2859
2859
|
*/
|
|
2860
2860
|
talkSeconds: number | null;
|
|
2861
|
+
/**
|
|
2862
|
+
* When Connect asked the carrier to ring the person called, on a `member`
|
|
2863
|
+
* call. Tells apart the two calls `unansweredReason: "cancelled"` covers:
|
|
2864
|
+
* set when the member hung up while it rang (the person sees a missed call -
|
|
2865
|
+
* Connect sends `call.missed`), `null` when they gave up before anyone was
|
|
2866
|
+
* rung (no call happened, and no event is sent).
|
|
2867
|
+
*/
|
|
2868
|
+
dialledAt: string | null;
|
|
2861
2869
|
/** Why nobody answered - set only when `status` is `unanswered`. */
|
|
2862
2870
|
unansweredReason: CallUnansweredReason | (string & {}) | null;
|
|
2863
2871
|
/**
|
|
@@ -2902,6 +2910,18 @@ export interface StartedBrowserCall {
|
|
|
2902
2910
|
* - `available`.
|
|
2903
2911
|
*/
|
|
2904
2912
|
export type CallingAvailabilityStatus = "available" | "no_permission" | "no_number" | "number_pending" | "no_balance";
|
|
2913
|
+
/**
|
|
2914
|
+
* The workspace's calling settings, as the signed-in member sees them -
|
|
2915
|
+
* `connect.calls.getCallSettings()`.
|
|
2916
|
+
*/
|
|
2917
|
+
export interface CallSettings {
|
|
2918
|
+
/** Whether member calls are recorded, transcribed and read (`call.analyzed`). */
|
|
2919
|
+
recordingEnabled: boolean;
|
|
2920
|
+
/** Paise, pre-tax, recording adds to every billed minute - show it beside the switch. */
|
|
2921
|
+
recordingRatePaisePerMinute: number;
|
|
2922
|
+
/** Whether this member may change it - their `channel:manage`. Draw the switch disabled when not. */
|
|
2923
|
+
canManage: boolean;
|
|
2924
|
+
}
|
|
2905
2925
|
export interface CallingAvailability {
|
|
2906
2926
|
status: CallingAvailabilityStatus;
|
|
2907
2927
|
/** The workspace's business number, E.164, when it holds one. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmoops/connect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "Official server-side SDK for the CosmoOps Connect platform API - Login with Connect, entitlements, billing and metering.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "commonjs",
|