@babelforce/babelconnect-sdk 0.9.1 → 0.11.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 +5 -0
- package/dist/client.js +33 -5
- package/dist/gen/babelconnect/v1/babelconnect_pb.d.ts +120 -0
- package/dist/gen/babelconnect/v1/babelconnect_pb.js +153 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/media.d.ts +27 -0
- package/dist/media.js +65 -2
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/package.json +2 -1
package/dist/client.d.ts
CHANGED
|
@@ -150,3 +150,8 @@ export declare class BabelconnectClient {
|
|
|
150
150
|
private rawSend;
|
|
151
151
|
private closeAllMedia;
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Stamp the SDK identity (name + build version) onto a request's headers — sent
|
|
155
|
+
* on every unary + streaming call so the server can identify the client (APP-A5).
|
|
156
|
+
*/
|
|
157
|
+
export declare function applySdkHeaders(header: Headers): void;
|
package/dist/client.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createClient, } from "@connectrpc/connect";
|
|
2
2
|
import { createGrpcWebTransport } from "@connectrpc/connect-web";
|
|
3
|
+
import { SDK_VERSION } from "./version.js";
|
|
3
4
|
import { Agent } from "./gen/babelconnect/v1/babelconnect_connect.js";
|
|
4
|
-
import { AddConferenceMember, AnswerCall, CallDirection, CallLifecycle, CallSource, Command, EndConference, Error as BcError, FlagRecording, Hangup, HistoryRequest, SmsThreadRequest, PhonebookRequest, HoldConferenceMember, Hold, KickConferenceMember, LeaveConference, MarkConversationRead, Mute, MuteConferenceMember, PlaceCall, Register, ResetLineStatus, SendDigits, SendSmsRequest, SetAgentNumber, SetConversationOpen, SetDisplayAs, SetPresence, SetWebrtc, StartConference, StartRecording, StopRecording, SetRecordingTags, SubscribeRequest, Transfer, WrapUpCancel, WrapUpExtend, } from "./gen/babelconnect/v1/babelconnect_pb.js";
|
|
5
|
-
import { browserMediaFactory, toRTCIceServers, } from "./media.js";
|
|
5
|
+
import { AddConferenceMember, AnswerCall, CallDirection, CallLifecycle, CallSource, Command, EndConference, Error as BcError, FlagRecording, Hangup, HistoryRequest, SmsThreadRequest, PhonebookRequest, HoldConferenceMember, Hold, KickConferenceMember, LeaveConference, MarkConversationRead, Mute, MuteConferenceMember, PlaceCall, Pong, Register, ResetLineStatus, SendDigits, SendSmsRequest, SetAgentNumber, SetConversationOpen, SetDisplayAs, SetPresence, SetWebrtc, StartConference, StartRecording, StopRecording, SetRecordingTags, SubscribeRequest, Transfer, WrapUpCancel, WrapUpExtend, } from "./gen/babelconnect/v1/babelconnect_pb.js";
|
|
6
|
+
import { browserMediaFactory, MediaError, toRTCIceServers, } from "./media.js";
|
|
6
7
|
import { StateCache } from "./state-cache.js";
|
|
7
8
|
/**
|
|
8
9
|
* The TypeScript "dumb renderer" client: opens the `Subscribe`/`Send` gRPC-web
|
|
@@ -346,6 +347,15 @@ export class BabelconnectClient {
|
|
|
346
347
|
// pass so a 15s heartbeat doesn't trigger a no-op reconcile every tick.
|
|
347
348
|
if (u.update.case === "keepalive")
|
|
348
349
|
return;
|
|
350
|
+
// Ping: reply with a Pong echoing the seq so the server can measure RTT +
|
|
351
|
+
// liveness (CALL-M6). Control-only, like keepalive — short-circuited before the
|
|
352
|
+
// cache and the ready/flush logic so it is never mistaken for the snapshot.
|
|
353
|
+
if (u.update.case === "ping") {
|
|
354
|
+
void this.rawSend(new Command({
|
|
355
|
+
command: { case: "pong", value: new Pong({ seq: u.update.value.seq }) },
|
|
356
|
+
}));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
349
359
|
// First message = the snapshot ⇒ the session is registered server-side, so
|
|
350
360
|
// flush intents queued during connect() (register(), an early dial, …).
|
|
351
361
|
if (!this.ready) {
|
|
@@ -420,9 +430,15 @@ export class BabelconnectClient {
|
|
|
420
430
|
}));
|
|
421
431
|
}
|
|
422
432
|
catch (e) {
|
|
433
|
+
// CALL-M5: a Media impl classifies a mic-acquisition failure (no mic,
|
|
434
|
+
// permission denied, device busy) into a distinct code via MediaError;
|
|
435
|
+
// anything else stays the opaque catch-all. The distinct code lets the
|
|
436
|
+
// UI/host explain the cause instead of showing a raw WebRTC string.
|
|
437
|
+
const code = e instanceof MediaError ? e.code : "media_answer_failed";
|
|
438
|
+
const message = e instanceof MediaError ? e.message : String(e);
|
|
423
439
|
this.opts.onError?.(new BcError({
|
|
424
|
-
code
|
|
425
|
-
message
|
|
440
|
+
code,
|
|
441
|
+
message,
|
|
426
442
|
callId: call.id,
|
|
427
443
|
}));
|
|
428
444
|
}
|
|
@@ -458,10 +474,22 @@ export class BabelconnectClient {
|
|
|
458
474
|
await Promise.all(all.map((m) => m.close()));
|
|
459
475
|
}
|
|
460
476
|
}
|
|
461
|
-
/**
|
|
477
|
+
/**
|
|
478
|
+
* Stamp the SDK identity (name + build version) onto a request's headers — sent
|
|
479
|
+
* on every unary + streaming call so the server can identify the client (APP-A5).
|
|
480
|
+
*/
|
|
481
|
+
export function applySdkHeaders(header) {
|
|
482
|
+
header.set("x-babelconnect-sdk", "ts");
|
|
483
|
+
header.set("x-babelconnect-sdk-version", SDK_VERSION);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* An interceptor that attaches the bearer token and the SDK identity headers to
|
|
487
|
+
* every gRPC-web call (unary + the long-lived Subscribe stream).
|
|
488
|
+
*/
|
|
462
489
|
function authInterceptor(token) {
|
|
463
490
|
return (next) => (req) => {
|
|
464
491
|
req.header.set("Authorization", `Bearer ${token}`);
|
|
492
|
+
applySdkHeaders(req.header);
|
|
465
493
|
return next(req);
|
|
466
494
|
};
|
|
467
495
|
}
|
|
@@ -332,6 +332,18 @@ export declare class Identity extends Message<Identity> {
|
|
|
332
332
|
* @generated from field: babelconnect.v1.AgentState state = 5;
|
|
333
333
|
*/
|
|
334
334
|
state: AgentState;
|
|
335
|
+
/**
|
|
336
|
+
* account / company display name, when known (ACC-E1)
|
|
337
|
+
*
|
|
338
|
+
* @generated from field: string account_name = 6;
|
|
339
|
+
*/
|
|
340
|
+
accountName: string;
|
|
341
|
+
/**
|
|
342
|
+
* accounts this agent may switch to (ACC-E2)
|
|
343
|
+
*
|
|
344
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 7;
|
|
345
|
+
*/
|
|
346
|
+
accounts: Account[];
|
|
335
347
|
constructor(data?: PartialMessage<Identity>);
|
|
336
348
|
static readonly runtime: typeof proto3;
|
|
337
349
|
static readonly typeName = "babelconnect.v1.Identity";
|
|
@@ -341,6 +353,41 @@ export declare class Identity extends Message<Identity> {
|
|
|
341
353
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Identity;
|
|
342
354
|
static equals(a: Identity | PlainMessage<Identity> | undefined, b: Identity | PlainMessage<Identity> | undefined): boolean;
|
|
343
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Account is one tenant the signed-in agent can operate in — the switchable set
|
|
358
|
+
* (agent-role only) surfaced for the account picker (ACC-E2). Sourced from
|
|
359
|
+
* GET /api/v2/user/accounts.
|
|
360
|
+
*
|
|
361
|
+
* @generated from message babelconnect.v1.Account
|
|
362
|
+
*/
|
|
363
|
+
export declare class Account extends Message<Account> {
|
|
364
|
+
/**
|
|
365
|
+
* customer / account id
|
|
366
|
+
*
|
|
367
|
+
* @generated from field: string id = 1;
|
|
368
|
+
*/
|
|
369
|
+
id: string;
|
|
370
|
+
/**
|
|
371
|
+
* company / display name
|
|
372
|
+
*
|
|
373
|
+
* @generated from field: string name = 2;
|
|
374
|
+
*/
|
|
375
|
+
name: string;
|
|
376
|
+
/**
|
|
377
|
+
* the account this session is currently scoped to
|
|
378
|
+
*
|
|
379
|
+
* @generated from field: bool current = 3;
|
|
380
|
+
*/
|
|
381
|
+
current: boolean;
|
|
382
|
+
constructor(data?: PartialMessage<Account>);
|
|
383
|
+
static readonly runtime: typeof proto3;
|
|
384
|
+
static readonly typeName = "babelconnect.v1.Account";
|
|
385
|
+
static readonly fields: FieldList;
|
|
386
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Account;
|
|
387
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Account;
|
|
388
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Account;
|
|
389
|
+
static equals(a: Account | PlainMessage<Account> | undefined, b: Account | PlainMessage<Account> | undefined): boolean;
|
|
390
|
+
}
|
|
344
391
|
/**
|
|
345
392
|
* AgentView is the complete per-agent state the server owns and renders to. A
|
|
346
393
|
* snapshot carries the whole view; patches carry entity-level deltas.
|
|
@@ -532,6 +579,12 @@ export declare class FeatureAccount extends Message<FeatureAccount> {
|
|
|
532
579
|
* @generated from field: bool show_status = 3;
|
|
533
580
|
*/
|
|
534
581
|
showStatus: boolean;
|
|
582
|
+
/**
|
|
583
|
+
* the multi-account switcher (ACC-E2)
|
|
584
|
+
*
|
|
585
|
+
* @generated from field: bool allow_account_switch = 4;
|
|
586
|
+
*/
|
|
587
|
+
allowAccountSwitch: boolean;
|
|
535
588
|
constructor(data?: PartialMessage<FeatureAccount>);
|
|
536
589
|
static readonly runtime: typeof proto3;
|
|
537
590
|
static readonly typeName = "babelconnect.v1.FeatureAccount";
|
|
@@ -716,6 +769,14 @@ export declare class AgentInfo extends Message<AgentInfo> {
|
|
|
716
769
|
* @generated from field: bool line_blocked = 18;
|
|
717
770
|
*/
|
|
718
771
|
lineBlocked: boolean;
|
|
772
|
+
/**
|
|
773
|
+
* Accounts this agent may switch to without re-logging-in (agent-role only,
|
|
774
|
+
* resolved at auth from GET /api/v2/user/accounts). One is marked `current`.
|
|
775
|
+
* Drives the Account tab's switcher; empty/single ⇒ no picker (ACC-E2).
|
|
776
|
+
*
|
|
777
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 19;
|
|
778
|
+
*/
|
|
779
|
+
accounts: Account[];
|
|
719
780
|
constructor(data?: PartialMessage<AgentInfo>);
|
|
720
781
|
static readonly runtime: typeof proto3;
|
|
721
782
|
static readonly typeName = "babelconnect.v1.AgentInfo";
|
|
@@ -1037,6 +1098,12 @@ export declare class StateUpdate extends Message<StateUpdate> {
|
|
|
1037
1098
|
*/
|
|
1038
1099
|
value: Keepalive;
|
|
1039
1100
|
case: "keepalive";
|
|
1101
|
+
} | {
|
|
1102
|
+
/**
|
|
1103
|
+
* @generated from field: babelconnect.v1.Ping ping = 6;
|
|
1104
|
+
*/
|
|
1105
|
+
value: Ping;
|
|
1106
|
+
case: "ping";
|
|
1040
1107
|
} | {
|
|
1041
1108
|
case: undefined;
|
|
1042
1109
|
value?: undefined;
|
|
@@ -1070,6 +1137,30 @@ export declare class Keepalive extends Message<Keepalive> {
|
|
|
1070
1137
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Keepalive;
|
|
1071
1138
|
static equals(a: Keepalive | PlainMessage<Keepalive> | undefined, b: Keepalive | PlainMessage<Keepalive> | undefined): boolean;
|
|
1072
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Ping is a liveness probe the server sends on the heartbeat tick (replacing the
|
|
1142
|
+
* bare Keepalive frame). The client echoes `seq` back in a Pong command; the server
|
|
1143
|
+
* measures round-trip time (RTT) from the reply and tracks per-session liveness.
|
|
1144
|
+
* Like Keepalive it keeps the connection warm, carries no state, and does NOT
|
|
1145
|
+
* advance `seq` — caches ignore it, and the reply is handled in the client's stream
|
|
1146
|
+
* loop, not the state reducer. (APP-A5/CALL-M6.)
|
|
1147
|
+
*
|
|
1148
|
+
* @generated from message babelconnect.v1.Ping
|
|
1149
|
+
*/
|
|
1150
|
+
export declare class Ping extends Message<Ping> {
|
|
1151
|
+
/**
|
|
1152
|
+
* @generated from field: uint64 seq = 1;
|
|
1153
|
+
*/
|
|
1154
|
+
seq: bigint;
|
|
1155
|
+
constructor(data?: PartialMessage<Ping>);
|
|
1156
|
+
static readonly runtime: typeof proto3;
|
|
1157
|
+
static readonly typeName = "babelconnect.v1.Ping";
|
|
1158
|
+
static readonly fields: FieldList;
|
|
1159
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Ping;
|
|
1160
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Ping;
|
|
1161
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Ping;
|
|
1162
|
+
static equals(a: Ping | PlainMessage<Ping> | undefined, b: Ping | PlainMessage<Ping> | undefined): boolean;
|
|
1163
|
+
}
|
|
1073
1164
|
/**
|
|
1074
1165
|
* Patch is an entity-level delta applied mechanically by the client cache:
|
|
1075
1166
|
* replace the agent block, upsert/remove a call by id, or set wrap-up.
|
|
@@ -1455,6 +1546,14 @@ export declare class Command extends Message<Command> {
|
|
|
1455
1546
|
*/
|
|
1456
1547
|
value: MarkConversationRead;
|
|
1457
1548
|
case: "markConversationRead";
|
|
1549
|
+
} | {
|
|
1550
|
+
/**
|
|
1551
|
+
* reply to a server Ping (liveness + RTT); echoes Ping.seq
|
|
1552
|
+
*
|
|
1553
|
+
* @generated from field: babelconnect.v1.Pong pong = 30;
|
|
1554
|
+
*/
|
|
1555
|
+
value: Pong;
|
|
1556
|
+
case: "pong";
|
|
1458
1557
|
} | {
|
|
1459
1558
|
case: undefined;
|
|
1460
1559
|
value?: undefined;
|
|
@@ -1468,6 +1567,27 @@ export declare class Command extends Message<Command> {
|
|
|
1468
1567
|
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Command;
|
|
1469
1568
|
static equals(a: Command | PlainMessage<Command> | undefined, b: Command | PlainMessage<Command> | undefined): boolean;
|
|
1470
1569
|
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Pong replies to a server Ping (StateUpdate.ping), echoing its `seq` so the server
|
|
1572
|
+
* can match it to the outstanding ping and compute RTT. Sent automatically by the
|
|
1573
|
+
* SDK from its stream-receive loop — not a user intent. (APP-A5/CALL-M6.)
|
|
1574
|
+
*
|
|
1575
|
+
* @generated from message babelconnect.v1.Pong
|
|
1576
|
+
*/
|
|
1577
|
+
export declare class Pong extends Message<Pong> {
|
|
1578
|
+
/**
|
|
1579
|
+
* @generated from field: uint64 seq = 1;
|
|
1580
|
+
*/
|
|
1581
|
+
seq: bigint;
|
|
1582
|
+
constructor(data?: PartialMessage<Pong>);
|
|
1583
|
+
static readonly runtime: typeof proto3;
|
|
1584
|
+
static readonly typeName = "babelconnect.v1.Pong";
|
|
1585
|
+
static readonly fields: FieldList;
|
|
1586
|
+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Pong;
|
|
1587
|
+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Pong;
|
|
1588
|
+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Pong;
|
|
1589
|
+
static equals(a: Pong | PlainMessage<Pong> | undefined, b: Pong | PlainMessage<Pong> | undefined): boolean;
|
|
1590
|
+
}
|
|
1471
1591
|
/**
|
|
1472
1592
|
* @generated from message babelconnect.v1.Register
|
|
1473
1593
|
*/
|
|
@@ -471,6 +471,18 @@ export class Identity extends Message {
|
|
|
471
471
|
* @generated from field: babelconnect.v1.AgentState state = 5;
|
|
472
472
|
*/
|
|
473
473
|
state = AgentState.UNSPECIFIED;
|
|
474
|
+
/**
|
|
475
|
+
* account / company display name, when known (ACC-E1)
|
|
476
|
+
*
|
|
477
|
+
* @generated from field: string account_name = 6;
|
|
478
|
+
*/
|
|
479
|
+
accountName = "";
|
|
480
|
+
/**
|
|
481
|
+
* accounts this agent may switch to (ACC-E2)
|
|
482
|
+
*
|
|
483
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 7;
|
|
484
|
+
*/
|
|
485
|
+
accounts = [];
|
|
474
486
|
constructor(data) {
|
|
475
487
|
super();
|
|
476
488
|
proto3.util.initPartial(data, this);
|
|
@@ -483,6 +495,8 @@ export class Identity extends Message {
|
|
|
483
495
|
{ no: 3, name: "number", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
484
496
|
{ no: 4, name: "username", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
485
497
|
{ no: 5, name: "state", kind: "enum", T: proto3.getEnumType(AgentState) },
|
|
498
|
+
{ no: 6, name: "account_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
499
|
+
{ no: 7, name: "accounts", kind: "message", T: Account, repeated: true },
|
|
486
500
|
]);
|
|
487
501
|
static fromBinary(bytes, options) {
|
|
488
502
|
return new Identity().fromBinary(bytes, options);
|
|
@@ -497,6 +511,56 @@ export class Identity extends Message {
|
|
|
497
511
|
return proto3.util.equals(Identity, a, b);
|
|
498
512
|
}
|
|
499
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Account is one tenant the signed-in agent can operate in — the switchable set
|
|
516
|
+
* (agent-role only) surfaced for the account picker (ACC-E2). Sourced from
|
|
517
|
+
* GET /api/v2/user/accounts.
|
|
518
|
+
*
|
|
519
|
+
* @generated from message babelconnect.v1.Account
|
|
520
|
+
*/
|
|
521
|
+
export class Account extends Message {
|
|
522
|
+
/**
|
|
523
|
+
* customer / account id
|
|
524
|
+
*
|
|
525
|
+
* @generated from field: string id = 1;
|
|
526
|
+
*/
|
|
527
|
+
id = "";
|
|
528
|
+
/**
|
|
529
|
+
* company / display name
|
|
530
|
+
*
|
|
531
|
+
* @generated from field: string name = 2;
|
|
532
|
+
*/
|
|
533
|
+
name = "";
|
|
534
|
+
/**
|
|
535
|
+
* the account this session is currently scoped to
|
|
536
|
+
*
|
|
537
|
+
* @generated from field: bool current = 3;
|
|
538
|
+
*/
|
|
539
|
+
current = false;
|
|
540
|
+
constructor(data) {
|
|
541
|
+
super();
|
|
542
|
+
proto3.util.initPartial(data, this);
|
|
543
|
+
}
|
|
544
|
+
static runtime = proto3;
|
|
545
|
+
static typeName = "babelconnect.v1.Account";
|
|
546
|
+
static fields = proto3.util.newFieldList(() => [
|
|
547
|
+
{ no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
548
|
+
{ no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
549
|
+
{ no: 3, name: "current", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
550
|
+
]);
|
|
551
|
+
static fromBinary(bytes, options) {
|
|
552
|
+
return new Account().fromBinary(bytes, options);
|
|
553
|
+
}
|
|
554
|
+
static fromJson(jsonValue, options) {
|
|
555
|
+
return new Account().fromJson(jsonValue, options);
|
|
556
|
+
}
|
|
557
|
+
static fromJsonString(jsonString, options) {
|
|
558
|
+
return new Account().fromJsonString(jsonString, options);
|
|
559
|
+
}
|
|
560
|
+
static equals(a, b) {
|
|
561
|
+
return proto3.util.equals(Account, a, b);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
500
564
|
/**
|
|
501
565
|
* AgentView is the complete per-agent state the server owns and renders to. A
|
|
502
566
|
* snapshot carries the whole view; patches carry entity-level deltas.
|
|
@@ -767,6 +831,12 @@ export class FeatureAccount extends Message {
|
|
|
767
831
|
* @generated from field: bool show_status = 3;
|
|
768
832
|
*/
|
|
769
833
|
showStatus = false;
|
|
834
|
+
/**
|
|
835
|
+
* the multi-account switcher (ACC-E2)
|
|
836
|
+
*
|
|
837
|
+
* @generated from field: bool allow_account_switch = 4;
|
|
838
|
+
*/
|
|
839
|
+
allowAccountSwitch = false;
|
|
770
840
|
constructor(data) {
|
|
771
841
|
super();
|
|
772
842
|
proto3.util.initPartial(data, this);
|
|
@@ -777,6 +847,7 @@ export class FeatureAccount extends Message {
|
|
|
777
847
|
{ no: 1, name: "enabled", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
778
848
|
{ no: 2, name: "allow_device_switch", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
779
849
|
{ no: 3, name: "show_status", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
850
|
+
{ no: 4, name: "allow_account_switch", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
780
851
|
]);
|
|
781
852
|
static fromBinary(bytes, options) {
|
|
782
853
|
return new FeatureAccount().fromBinary(bytes, options);
|
|
@@ -1007,6 +1078,14 @@ export class AgentInfo extends Message {
|
|
|
1007
1078
|
* @generated from field: bool line_blocked = 18;
|
|
1008
1079
|
*/
|
|
1009
1080
|
lineBlocked = false;
|
|
1081
|
+
/**
|
|
1082
|
+
* Accounts this agent may switch to without re-logging-in (agent-role only,
|
|
1083
|
+
* resolved at auth from GET /api/v2/user/accounts). One is marked `current`.
|
|
1084
|
+
* Drives the Account tab's switcher; empty/single ⇒ no picker (ACC-E2).
|
|
1085
|
+
*
|
|
1086
|
+
* @generated from field: repeated babelconnect.v1.Account accounts = 19;
|
|
1087
|
+
*/
|
|
1088
|
+
accounts = [];
|
|
1010
1089
|
constructor(data) {
|
|
1011
1090
|
super();
|
|
1012
1091
|
proto3.util.initPartial(data, this);
|
|
@@ -1032,6 +1111,7 @@ export class AgentInfo extends Message {
|
|
|
1032
1111
|
{ no: 16, name: "account_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
1033
1112
|
{ no: 17, name: "account_name", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
|
1034
1113
|
{ no: 18, name: "line_blocked", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
|
|
1114
|
+
{ no: 19, name: "accounts", kind: "message", T: Account, repeated: true },
|
|
1035
1115
|
]);
|
|
1036
1116
|
static fromBinary(bytes, options) {
|
|
1037
1117
|
return new AgentInfo().fromBinary(bytes, options);
|
|
@@ -1465,6 +1545,7 @@ export class StateUpdate extends Message {
|
|
|
1465
1545
|
{ no: 3, name: "patch", kind: "message", T: Patch, oneof: "update" },
|
|
1466
1546
|
{ no: 4, name: "error", kind: "message", T: Error, oneof: "update" },
|
|
1467
1547
|
{ no: 5, name: "keepalive", kind: "message", T: Keepalive, oneof: "update" },
|
|
1548
|
+
{ no: 6, name: "ping", kind: "message", T: Ping, oneof: "update" },
|
|
1468
1549
|
]);
|
|
1469
1550
|
static fromBinary(bytes, options) {
|
|
1470
1551
|
return new StateUpdate().fromBinary(bytes, options);
|
|
@@ -1510,6 +1591,43 @@ export class Keepalive extends Message {
|
|
|
1510
1591
|
return proto3.util.equals(Keepalive, a, b);
|
|
1511
1592
|
}
|
|
1512
1593
|
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Ping is a liveness probe the server sends on the heartbeat tick (replacing the
|
|
1596
|
+
* bare Keepalive frame). The client echoes `seq` back in a Pong command; the server
|
|
1597
|
+
* measures round-trip time (RTT) from the reply and tracks per-session liveness.
|
|
1598
|
+
* Like Keepalive it keeps the connection warm, carries no state, and does NOT
|
|
1599
|
+
* advance `seq` — caches ignore it, and the reply is handled in the client's stream
|
|
1600
|
+
* loop, not the state reducer. (APP-A5/CALL-M6.)
|
|
1601
|
+
*
|
|
1602
|
+
* @generated from message babelconnect.v1.Ping
|
|
1603
|
+
*/
|
|
1604
|
+
export class Ping extends Message {
|
|
1605
|
+
/**
|
|
1606
|
+
* @generated from field: uint64 seq = 1;
|
|
1607
|
+
*/
|
|
1608
|
+
seq = protoInt64.zero;
|
|
1609
|
+
constructor(data) {
|
|
1610
|
+
super();
|
|
1611
|
+
proto3.util.initPartial(data, this);
|
|
1612
|
+
}
|
|
1613
|
+
static runtime = proto3;
|
|
1614
|
+
static typeName = "babelconnect.v1.Ping";
|
|
1615
|
+
static fields = proto3.util.newFieldList(() => [
|
|
1616
|
+
{ no: 1, name: "seq", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
|
|
1617
|
+
]);
|
|
1618
|
+
static fromBinary(bytes, options) {
|
|
1619
|
+
return new Ping().fromBinary(bytes, options);
|
|
1620
|
+
}
|
|
1621
|
+
static fromJson(jsonValue, options) {
|
|
1622
|
+
return new Ping().fromJson(jsonValue, options);
|
|
1623
|
+
}
|
|
1624
|
+
static fromJsonString(jsonString, options) {
|
|
1625
|
+
return new Ping().fromJsonString(jsonString, options);
|
|
1626
|
+
}
|
|
1627
|
+
static equals(a, b) {
|
|
1628
|
+
return proto3.util.equals(Ping, a, b);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1513
1631
|
/**
|
|
1514
1632
|
* Patch is an entity-level delta applied mechanically by the client cache:
|
|
1515
1633
|
* replace the agent block, upsert/remove a call by id, or set wrap-up.
|
|
@@ -1695,6 +1813,7 @@ export class Command extends Message {
|
|
|
1695
1813
|
{ no: 27, name: "mute_conference_member", kind: "message", T: MuteConferenceMember, oneof: "command" },
|
|
1696
1814
|
{ no: 28, name: "set_conversation_open", kind: "message", T: SetConversationOpen, oneof: "command" },
|
|
1697
1815
|
{ no: 29, name: "mark_conversation_read", kind: "message", T: MarkConversationRead, oneof: "command" },
|
|
1816
|
+
{ no: 30, name: "pong", kind: "message", T: Pong, oneof: "command" },
|
|
1698
1817
|
]);
|
|
1699
1818
|
static fromBinary(bytes, options) {
|
|
1700
1819
|
return new Command().fromBinary(bytes, options);
|
|
@@ -1709,6 +1828,40 @@ export class Command extends Message {
|
|
|
1709
1828
|
return proto3.util.equals(Command, a, b);
|
|
1710
1829
|
}
|
|
1711
1830
|
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Pong replies to a server Ping (StateUpdate.ping), echoing its `seq` so the server
|
|
1833
|
+
* can match it to the outstanding ping and compute RTT. Sent automatically by the
|
|
1834
|
+
* SDK from its stream-receive loop — not a user intent. (APP-A5/CALL-M6.)
|
|
1835
|
+
*
|
|
1836
|
+
* @generated from message babelconnect.v1.Pong
|
|
1837
|
+
*/
|
|
1838
|
+
export class Pong extends Message {
|
|
1839
|
+
/**
|
|
1840
|
+
* @generated from field: uint64 seq = 1;
|
|
1841
|
+
*/
|
|
1842
|
+
seq = protoInt64.zero;
|
|
1843
|
+
constructor(data) {
|
|
1844
|
+
super();
|
|
1845
|
+
proto3.util.initPartial(data, this);
|
|
1846
|
+
}
|
|
1847
|
+
static runtime = proto3;
|
|
1848
|
+
static typeName = "babelconnect.v1.Pong";
|
|
1849
|
+
static fields = proto3.util.newFieldList(() => [
|
|
1850
|
+
{ no: 1, name: "seq", kind: "scalar", T: 4 /* ScalarType.UINT64 */ },
|
|
1851
|
+
]);
|
|
1852
|
+
static fromBinary(bytes, options) {
|
|
1853
|
+
return new Pong().fromBinary(bytes, options);
|
|
1854
|
+
}
|
|
1855
|
+
static fromJson(jsonValue, options) {
|
|
1856
|
+
return new Pong().fromJson(jsonValue, options);
|
|
1857
|
+
}
|
|
1858
|
+
static fromJsonString(jsonString, options) {
|
|
1859
|
+
return new Pong().fromJsonString(jsonString, options);
|
|
1860
|
+
}
|
|
1861
|
+
static equals(a, b) {
|
|
1862
|
+
return proto3.util.equals(Pong, a, b);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1712
1865
|
/**
|
|
1713
1866
|
* @generated from message babelconnect.v1.Register
|
|
1714
1867
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
export { BabelconnectClient, type ConnectOptions } from "./client.js";
|
|
13
13
|
export { StateCache } from "./state-cache.js";
|
|
14
14
|
export { passwordGrant, revokeToken, pkceChallenge, buildAuthorizeUrl, authorizationCodeGrant, DEFAULT_CLIENT_ID, type TokenResponse, type PkceChallenge, type AuthorizeUrlOptions, } from "./auth.js";
|
|
15
|
-
export { BrowserWebrtcMedia, browserMediaFactory, type Media, type MediaFactory } from "./media.js";
|
|
15
|
+
export { BrowserWebrtcMedia, browserMediaFactory, MediaError, micErrorCode, MIC_NOT_FOUND, MIC_PERMISSION_DENIED, MIC_IN_USE, MEDIA_ANSWER_FAILED, type Media, type MediaFactory, } from "./media.js";
|
|
16
16
|
export * from "./gen/babelconnect/v1/babelconnect_pb.js";
|
|
17
17
|
export { Agent } from "./gen/babelconnect/v1/babelconnect_connect.js";
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { BabelconnectClient } from "./client.js";
|
|
13
13
|
export { StateCache } from "./state-cache.js";
|
|
14
14
|
export { passwordGrant, revokeToken, pkceChallenge, buildAuthorizeUrl, authorizationCodeGrant, DEFAULT_CLIENT_ID, } from "./auth.js";
|
|
15
|
-
export { BrowserWebrtcMedia, browserMediaFactory } from "./media.js";
|
|
15
|
+
export { BrowserWebrtcMedia, browserMediaFactory, MediaError, micErrorCode, MIC_NOT_FOUND, MIC_PERMISSION_DENIED, MIC_IN_USE, MEDIA_ANSWER_FAILED, } from "./media.js";
|
|
16
16
|
// The generated babelconnect.v1 messages + enums (AgentView, CallState, Command, …)
|
|
17
17
|
// and the Agent service descriptor.
|
|
18
18
|
export * from "./gen/babelconnect/v1/babelconnect_pb.js";
|
package/dist/media.d.ts
CHANGED
|
@@ -19,6 +19,33 @@ export interface Media {
|
|
|
19
19
|
}
|
|
20
20
|
/** Builds the {@link Media} for a given call id. */
|
|
21
21
|
export type MediaFactory = (callId: string) => Media;
|
|
22
|
+
/** No microphone hardware is available (`NotFoundError`). */
|
|
23
|
+
export declare const MIC_NOT_FOUND = "mic_not_found";
|
|
24
|
+
/** Microphone access was denied (`NotAllowedError` / `SecurityError`). */
|
|
25
|
+
export declare const MIC_PERMISSION_DENIED = "mic_permission_denied";
|
|
26
|
+
/** The microphone is held by another application (`NotReadableError` / `AbortError`). */
|
|
27
|
+
export declare const MIC_IN_USE = "mic_in_use";
|
|
28
|
+
/** The pre-existing catch-all, kept as the fallback when the cause is unclear. */
|
|
29
|
+
export declare const MEDIA_ANSWER_FAILED = "media_answer_failed";
|
|
30
|
+
/**
|
|
31
|
+
* Thrown by a {@link Media} implementation when it cannot start audio for a
|
|
32
|
+
* call — e.g. the microphone is missing, permission was denied, or the device
|
|
33
|
+
* is held by another app (CALL-M5). {@link code} is a stable classification the
|
|
34
|
+
* SDK surfaces on `onError` (and hosts receive as `cti.error`) so the UI can
|
|
35
|
+
* show cause-specific help instead of a raw WebRTC string.
|
|
36
|
+
*/
|
|
37
|
+
export declare class MediaError extends Error {
|
|
38
|
+
readonly code: string;
|
|
39
|
+
constructor(code: string, message: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Classify a caught `getUserMedia` failure into one of the mic error codes,
|
|
43
|
+
* falling back to {@link MEDIA_ANSWER_FAILED} when the cause can't be told
|
|
44
|
+
* apart. On the web the real `DOMException` is available so we read its `.name`;
|
|
45
|
+
* the string form is also scanned as a fallback (and to stay in step with the
|
|
46
|
+
* Dart classifier, which only has the collapsed `'Unable to getUserMedia: …'`).
|
|
47
|
+
*/
|
|
48
|
+
export declare function micErrorCode(error: unknown): string;
|
|
22
49
|
/**
|
|
23
50
|
* The default browser WebRTC backend: capture the mic, answer the server's offer
|
|
24
51
|
* (non-trickle ICE), and play the remote audio through a hidden `<audio>` element.
|
package/dist/media.js
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
/** No microphone hardware is available (`NotFoundError`). */
|
|
2
|
+
export const MIC_NOT_FOUND = "mic_not_found";
|
|
3
|
+
/** Microphone access was denied (`NotAllowedError` / `SecurityError`). */
|
|
4
|
+
export const MIC_PERMISSION_DENIED = "mic_permission_denied";
|
|
5
|
+
/** The microphone is held by another application (`NotReadableError` / `AbortError`). */
|
|
6
|
+
export const MIC_IN_USE = "mic_in_use";
|
|
7
|
+
/** The pre-existing catch-all, kept as the fallback when the cause is unclear. */
|
|
8
|
+
export const MEDIA_ANSWER_FAILED = "media_answer_failed";
|
|
9
|
+
/**
|
|
10
|
+
* Thrown by a {@link Media} implementation when it cannot start audio for a
|
|
11
|
+
* call — e.g. the microphone is missing, permission was denied, or the device
|
|
12
|
+
* is held by another app (CALL-M5). {@link code} is a stable classification the
|
|
13
|
+
* SDK surfaces on `onError` (and hosts receive as `cti.error`) so the UI can
|
|
14
|
+
* show cause-specific help instead of a raw WebRTC string.
|
|
15
|
+
*/
|
|
16
|
+
export class MediaError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(code, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.name = "MediaError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Classify a caught `getUserMedia` failure into one of the mic error codes,
|
|
26
|
+
* falling back to {@link MEDIA_ANSWER_FAILED} when the cause can't be told
|
|
27
|
+
* apart. On the web the real `DOMException` is available so we read its `.name`;
|
|
28
|
+
* the string form is also scanned as a fallback (and to stay in step with the
|
|
29
|
+
* Dart classifier, which only has the collapsed `'Unable to getUserMedia: …'`).
|
|
30
|
+
*/
|
|
31
|
+
export function micErrorCode(error) {
|
|
32
|
+
const name = typeof DOMException !== "undefined" && error instanceof DOMException
|
|
33
|
+
? error.name
|
|
34
|
+
: "";
|
|
35
|
+
const s = `${name} ${String(error)}`.toLowerCase();
|
|
36
|
+
if (s.includes("notfound") || s.includes("not found") || s.includes("no device")) {
|
|
37
|
+
return MIC_NOT_FOUND;
|
|
38
|
+
}
|
|
39
|
+
if (s.includes("notallowed") ||
|
|
40
|
+
s.includes("permissiondenied") ||
|
|
41
|
+
s.includes("permission denied") ||
|
|
42
|
+
s.includes("security") ||
|
|
43
|
+
s.includes("denied")) {
|
|
44
|
+
return MIC_PERMISSION_DENIED;
|
|
45
|
+
}
|
|
46
|
+
if (s.includes("notreadable") ||
|
|
47
|
+
s.includes("trackstart") ||
|
|
48
|
+
s.includes("abort") ||
|
|
49
|
+
s.includes("in use") ||
|
|
50
|
+
s.includes("busy")) {
|
|
51
|
+
return MIC_IN_USE;
|
|
52
|
+
}
|
|
53
|
+
return MEDIA_ANSWER_FAILED;
|
|
54
|
+
}
|
|
1
55
|
/**
|
|
2
56
|
* The default browser WebRTC backend: capture the mic, answer the server's offer
|
|
3
57
|
* (non-trickle ICE), and play the remote audio through a hidden `<audio>` element.
|
|
@@ -35,8 +89,17 @@ export class BrowserWebrtcMedia {
|
|
|
35
89
|
el.srcObject = stream;
|
|
36
90
|
void el.play().catch(() => { });
|
|
37
91
|
};
|
|
38
|
-
// Capture the microphone and add it to the peer.
|
|
39
|
-
|
|
92
|
+
// Capture the microphone and add it to the peer. Classify a failure (no mic
|
|
93
|
+
// / permission denied / device busy) into a MediaError the SDK surfaces with
|
|
94
|
+
// a distinct code (CALL-M5) so the UI/host can explain it, instead of the
|
|
95
|
+
// opaque `media_answer_failed`.
|
|
96
|
+
let local;
|
|
97
|
+
try {
|
|
98
|
+
local = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
throw new MediaError(micErrorCode(e), String(e));
|
|
102
|
+
}
|
|
40
103
|
this.local = local;
|
|
41
104
|
for (const track of local.getAudioTracks())
|
|
42
105
|
pc.addTrack(track, local);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SDK_VERSION = "0.11.0";
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babelforce/babelconnect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "TypeScript SDK for babelconnect — server-authoritative agent state over gRPC-web, native WebRTC audio, and an embeddable widget (iframe + postMessage) for the Flutter web app.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://babelforce.github.io/babelconnect-sdk/",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"generate": "echo 'run `task gen` at the repo root (buf generate emits src/gen)'",
|
|
27
|
+
"prebuild": "node scripts/gen-version.mjs",
|
|
27
28
|
"build": "tsc -p tsconfig.json",
|
|
28
29
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
29
30
|
"test": "npm run build && node --test test/*.test.mjs",
|