@16x/webphone-sdk 3.1.11 → 3.1.13

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +37 -215
  2. package/LICENSE +1 -1
  3. package/README.md +161 -219
  4. package/dist/{CCBarClient-Dmw3T0lF.js → CCBarClient-BJO4qjN2.js} +93 -64
  5. package/dist/CCBarClient-_sG61iiu.cjs +1 -0
  6. package/dist/{Diagnostics-Dmhx4niF.cjs → Diagnostics-B9EQ0Y8m.cjs} +1 -1
  7. package/dist/{Diagnostics-BLHANihC.js → Diagnostics-DdbpMSMr.js} +1 -1
  8. package/dist/JsSipAdapter-BB-SusrS.cjs +1 -0
  9. package/dist/{JsSipAdapter-CwDotPN2.js → JsSipAdapter-BHtCsy_S.js} +206 -35
  10. package/dist/SharedWorkerProtocol-C-4KpECM.cjs +1 -0
  11. package/dist/SharedWorkerProtocol-CffZrUXg.js +119 -0
  12. package/dist/core/CCBarCall.d.ts +3 -0
  13. package/dist/createJsSipFactory-B1ORbJiP.cjs +22 -0
  14. package/dist/createJsSipFactory-CoWZN-BC.js +7410 -0
  15. package/dist/diagnostics/Diagnostics.d.ts +8 -0
  16. package/dist/diagnostics/index.cjs +1 -1
  17. package/dist/diagnostics/index.js +1 -1
  18. package/dist/errors/CCBarError.d.ts +3 -0
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/index.umd.js +1 -75
  22. package/dist/legacy/index.cjs +1 -1
  23. package/dist/legacy/index.js +67 -69
  24. package/dist/legacy/legacySessionProvider.d.ts +4 -8
  25. package/dist/shared/SharedWorkerProtocol.d.ts +1 -0
  26. package/dist/shared-worker.cjs +1 -1
  27. package/dist/shared-worker.js +1 -1
  28. package/dist/styles.css +7 -7
  29. package/dist/types.d.ts +1 -1
  30. package/dist/ui/i18n/types.d.ts +1 -0
  31. package/dist/ui/index.cjs +1 -1
  32. package/dist/ui/index.js +16 -7
  33. package/dist/version.d.ts +1 -1
  34. package/package.json +4 -3
  35. package/dist/CCBarClient-B7SRICz8.cjs +0 -1
  36. package/dist/JsSipAdapter-BN9BAFHr.cjs +0 -1
  37. package/dist/SharedWorkerProtocol-39dch6xL.cjs +0 -1
  38. package/dist/SharedWorkerProtocol-DKVQ5-po.js +0 -87
  39. package/dist/createJsSipFactory-9CPsweDR.js +0 -9665
  40. package/dist/createJsSipFactory-Dr8HbLl-.cjs +0 -75
package/README.md CHANGED
@@ -1,219 +1,161 @@
1
- # CCBar WebPhone SDK Core
2
-
3
- `@16x/webphone-sdk` 是 CCBar WebPhone 的 TypeScript SDK。Core 提供无 UI 的连接、通话、媒体设备、重连和脱敏诊断能力,可用于 Vue、React、原生 Web 与受支持的移动浏览器。
4
-
5
- ## 安装
6
-
7
- ```bash
8
- npm install @16x/webphone-sdk
9
- ```
10
-
11
- 公开包地址:[https://www.npmjs.com/package/@16x/webphone-sdk](https://www.npmjs.com/package/@16x/webphone-sdk)。任何人都可以直接安装,不需要 token。
12
-
13
- 浏览器只应接收客户服务端下发的会话(已含短期 WSS ticket 与 SIP 凭证)。不要把 `appSecret`、AES 密钥、固定 SIP 密码或 TURN 密码写入前端代码、示例、日志或构建产物。客户从安装到首通的完整路径见 `docs/quick-start.md`,PC/H5 选择见 `docs/guides/mobile-h5.md`。
14
-
15
- ## Core 快速开始
16
-
17
- ```ts
18
- import { CCBarClient } from '@16x/webphone-sdk';
19
-
20
- const client = new CCBarClient({
21
- platform: 'web',
22
- sessionProvider: {
23
- // 你们自己的会话接口:返回一份 WebPhoneSession,签名与解密都留在服务端
24
- createSession: async () => {
25
- const response = await fetch('/get-session', { method: 'POST', credentials: 'include' });
26
- if (!response.ok) throw new Error('获取会话失败');
27
- return response.json();
28
- },
29
- },
30
- });
31
-
32
- await client.initialize();
33
- await client.connect();
34
-
35
- const call = await client.dial({ destination: '13800138000' });
36
- await call.hangup();
37
-
38
- await client.disconnect();
39
- await client.dispose();
40
- ```
41
-
42
- 在 SPA 中应由应用级容器持有一个 `CCBarClient`。`connect()`、`disconnect()` 和 `dispose()` 支持幂等调用;组件卸载或应用退出时必须调用 `dispose()` 释放 Timer、Listener、WebSocket、PeerConnection、Track 和 Audio 资源。
43
-
44
- 根入口与 `/diagnostics` 可在 SSR/Node 导入阶段安全加载,不会访问 `window`、`document` 或 `navigator`。浏览器媒体能力只在初始化或后续操作时检测。
45
-
46
- ## 主要 API
47
-
48
- - `CCBarClient.initialize/connect/disconnect/dispose`
49
- - `CCBarClient.dial/getCalls/getActiveCall/setActiveCall`
50
- - `CCBarClient.setAgentStatus`
51
- - `CCBarClient.media` 设备与音频控制
52
- - `CCBarClient.on/off` 类型化事件
53
- - `CCBarClient.getDiagnostics` 脱敏诊断报告
54
- - `CCBarCall.mute/unmute/hold/resume/sendDtmf/transfer/hangup`
55
-
56
- 构造参数里几个按需用的:`sessionProvider`(**必填**,4.0.0 起唯一会话来源,见下一节)、
57
- `sipKeepaliveSeconds`(3.1.1+,默认 25 秒重发 REGISTER 撑住 NAT/代理上的长连接,`0` 关闭)、
58
- `sharedWorker`(多标签页入口)、`baseUrl`、`maxConcurrentCalls`。
59
-
60
- 拨号时 SDK 会在 `pcConfig` 里带上 `iceCandidatePoolSize: 2`(3.1.2+):JsSIP 要等 ICE 收集完成才发
61
- INVITE,预收集候选才能做到「点完就出局」。
62
-
63
- PC Web 支持完整 Core 通话能力。移动 H5 首期仅承诺受支持系统浏览器中的前台单路外呼;来电、保持、转接、后台和锁屏通话不在承诺范围内。
64
-
65
- ## 会话来源(sessionProvider)
66
-
67
- 会话由接入方在自己的服务端拼好后交给 SDK:**4.0.0 起这是唯一的会话来源**(3.x 的 `tokenProvider`
68
- 以及 SDK 自己请求平台会话接口的能力已移除,迁移方式见 `CHANGELOG.md` 的 4.0.0 条目)。
69
-
70
- ```ts
71
- const client = new CCBarClient({
72
- platform: 'web',
73
- sessionProvider: {
74
- // 必填:返回一份 WebPhoneSession(sip.uri / sip.registerTicket / transport.wssUrl / iceServers / capabilities…)
75
- createSession: async () => {
76
- const response = await fetch('/get-session', { method: 'POST', credentials: 'include' });
77
- if (!response.ok) throw new Error('获取坐席账号失败');
78
- return response.json();
79
- },
80
- // 可选:缺省时 SDK 用 createSession 重建会话(会话到期前会调用一次)
81
- // refreshSession: async (sessionId) => { ... },
82
- // 可选:缺省时 setAgentStatus 抛 CAPABILITY_NOT_SUPPORTED,不静默失败
83
- // setAgentStatus: async ({ status, reason }) => { ... },
84
- },
85
- });
86
-
87
- await client.connect({ extension: '8001' });
88
- ```
89
-
90
- SDK 从会话里读的字段:
91
-
92
- | 字段 | 用途 |
93
- |---|---|
94
- | `sip.uri` / `sip.registrar` | REGISTER 的账号与 registrar |
95
- | `sip.registerTicket` | SIP 密码(旧平台就是服务端解出来的那个) |
96
- | `sip.registerExpires` | REGISTER 有效期(秒),没给或非正数按 300 |
97
- | `transport.wssUrl` | 软电话地址;只剥离 `ticket` 参数,`?token=` 原样保留 |
98
- | `transport.ticket` | 走 `xcall-ticket.<ticket>` 子协议;为空则只声明 `sip` |
99
- | `iceServers` / `capabilities` | ICE 配置 / 能力开关(outbound、inbound、mute、dtmf、hold、blind_transfer) |
100
-
101
- **密钥与密码都留在服务端**:签名的 appSecret、坐席密码的解密密钥都不要下发浏览器;
102
- 会话(含 SIP 密码)在你们自己的服务端拼好,页面只拿结果。
103
-
104
- ## Legacy Adapter
105
-
106
- 只有显式导入 Legacy 子路径才会在浏览器附加 `window.CCBarSDK`;根入口没有该副作用。Legacy 子路径同样可以安全地在 SSR 中导入。
107
-
108
- ```ts
109
- import { CCBarSDK } from '@16x/webphone-sdk/legacy';
110
-
111
- const legacy = new CCBarSDK({
112
- platform: 'web',
113
- sessionProvider: {
114
- createSession: async () => fetch('/get-session', {
115
- method: 'POST',
116
- credentials: 'include',
117
- }).then(response => response.json()),
118
- },
119
- });
120
-
121
- await legacy.login();
122
- const call = await legacy.call('13800138000');
123
- await legacy.hangup();
124
- await legacy.signOut();
125
- ```
126
-
127
- ### `createLegacySessionProvider`:老平台的会话来源
128
-
129
- 老平台(`token/fs` + `seat/account/get` 这一套)可以把「取 token → 取坐席账号 → 解 SIP 密码 → 组装会话」
130
- 整段交给 SDK,页面只提供换票口子:
131
-
132
- ```ts
133
- import { createLegacySessionProvider } from '@16x/webphone-sdk/legacy';
134
-
135
- const client = new CCBarClient({
136
- sessionProvider: createLegacySessionProvider({
137
- host: 'https://vxapi.yundianlab.com',
138
- // 软电话 WSS 地址(必填):wss:// 或 ws:// 开头的完整地址,SDK 只往上挂 ?token=
139
- sipWsUrl: 'wss://your-sip-host/api/fs/sip-ws',
140
- // 取 token 二选一:给函数,或给地址让 SDK 去 POST(都是你们自己的接口)
141
- getToken: () => fetch('/get-token', { method: 'POST' }).then(response => response.json()),
142
- // tokenUrl: '/get-token',
143
- }),
144
- });
145
- ```
146
-
147
- - **软电话地址由你们传**:`sipWsUrl` 必填(完整地址),SDK **不再**按坐席账号的 `domain` / `wssPort`
148
- 拼装,也不接受相对路径;缺了或不是 `wss://` / `ws://` 地址时,建会话会抛 `CONFIG_INVALID`。
149
- SDK 只把 fs token 挂成 `?token=`(`attachTokenToWss: false` 可关掉)。
150
- - **SDK 不碰 API SECRET**:`/openapi/v1/token/fs` 的加签始终在你们服务端,SDK 只拿结果;
151
- - **AES key 会随包下发**:老平台那把 16 字节 key/iv 是固定值(旧版 `ccbar.js` 里同样是硬编码),
152
- 可用 `aesKey` / `aesIv` 覆盖;介意的话就走服务端拼会话(见下一节);
153
- - 坐席状态:`client.setAgentStatus(...)` 打到 `{host}/openapi/token/v1/seats/set-status`,
154
- 其中 `extension` 传的是**坐席账号**(`username`,可能带企业前缀);
155
- - **浏览器直连网关**:需要网关放行 CORS 与调用方 IP(`seat/account/get` 有 IP 白名单),部署前先确认;
156
- - 取票与 SIP 密码都按有效期提前换(`refreshBufferSeconds`,默认 180 秒)。
157
-
158
- ### `decryptSipPassword`:解 `seat/account/get` 返回的 `password`
159
-
160
- `password`(或 `registerPassword`)是 **AES-128-CBC/Pkcs7 的密文、Base64 编码**,
161
- SDK 把解密单独导出,服务端(Node 18+)与页面都能用:
162
-
163
- ```ts
164
- import { decryptSipPassword } from '@16x/webphone-sdk/legacy';
165
-
166
- const password = await decryptSipPassword(data.password); // 明文 SIP 密码
167
- // 环境用的是别的 key/iv 时:await decryptSipPassword(data.password, { key, iv })
168
- ```
169
-
170
- - key/iv 各 16 字节,**默认是平台内置固定值**(与旧版 `ccbar.js` 一致),可用 `key`/`iv` 覆盖;
171
- - 失败抛 `CCBarError`,可读原因在 `error.cause`(密文为空 / 不是合法 Base64 / 解密失败);
172
- - 依赖 WebCrypto:浏览器需安全上下文(https / localhost),Node 需 18+;
173
- - `createLegacySessionProvider` 内部用的就是它。
174
-
175
- 旧方法映射如下:
176
-
177
- | Legacy | Core |
178
- |---|---|
179
- | `getToken()` / `getAccount()` / `login()` | `client.connect()` |
180
- | `call(number)` | `client.dial({ destination: number })` |
181
- | `insideCall(number)` | `client.dial({ destination: number, type: 'extension' })` |
182
- | `answer()` | 当前 `call.answer()` |
183
- | `hangup()` | 当前 `call.hangup()` |
184
- | `holdCall()` / `unholdCall()` | 当前 `call.hold()` / `call.resume()` |
185
- | `transSo(number)` | 当前 `call.transfer({ target: number, type: 'blind' })` |
186
- | `setId()` / `setRe()` | `client.setAgentStatus('available' / 'break')` |
187
- | `signOut()` | `client.disconnect()` 后 `client.dispose()` |
188
-
189
- `insideCall(number)` 会使用 `type: 'extension'` 标记 SIP 呼叫(INVITE 上带 `X-XCall-Destination-Type: extension`),
190
- 浏览器不拼接客户前缀;后端按当前会话租户解析分机并路由到同租户坐席。普通 `dial()` 不传 type,继续按外呼处理。
191
-
192
- 注意:如果你们的网关只认「企业前缀 + 分机号」,`type: 'extension'` 这个头它不解析,
193
- 就需要自己拼好前缀再拨(`dial({ destination: \`${prefix}${number}\` })`),
194
- 具体见客户示例仓库的 `prefixExtension()`。
195
-
196
- `setBu()` 不会伪造 `busy`。该方法抛出 `DeprecatedError`,因为忙碌状态应由通话状态或服务端状态策略驱动。所有 Legacy 方法仅在开发构建中各警告一次,生产构建不输出弃用日志。`signOut()` 是终态操作;如需保留实例以便重新连接,请直接使用 Core 的 `disconnect()`。
197
-
198
- ## 版本与更新记录
199
-
200
- 当前版本见 `package.json`,每个版本改了什么见 [CHANGELOG.md](./CHANGELOG.md)。
201
- 最近几个版本:4.0.0 移除 `tokenProvider`(只保留 `sessionProvider`,破坏性变更);3.1.0 新增 `sessionProvider`;3.1.1 新增 SIP 保活;3.1.2 拨号带 ICE 候选池。
202
-
203
- ## 开发与验收
204
-
205
- ```bash
206
- npm run lint
207
- npm run typecheck
208
- npm test
209
- npm run build
210
- npm run pack:check
211
- ```
212
-
213
- 正式包通过 `exports` 提供根入口、`/ui`、`/legacy`、`/diagnostics` 和 `/styles.css`,不包含测试、示例、Source Map 或凭证文件。
214
-
215
- ## License
216
-
217
- Copyright 16X. All rights reserved.
218
-
219
- Private customer distribution only. No redistribution or sublicensing without written permission.
1
+ # CCBar WebPhone SDK
2
+
3
+ `@16x/webphone-sdk` 是浏览器软电话 SDK,提供 SIP 注册、呼入呼出、通话控制、坐席状态、媒体设备和生命周期管理。
4
+
5
+ ```bash
6
+ npm install @16x/webphone-sdk@3.1.12
7
+ ```
8
+
9
+ 包信息:[https://www.npmjs.com/package/@16x/webphone-sdk](https://www.npmjs.com/package/@16x/webphone-sdk)。
10
+
11
+ ## 接入流程
12
+
13
+ 本接入方式由客户服务端负责取得平台 token、查询坐席账号并组装完整的 `WebPhoneSession`。浏览器只请求客户自己的会话接口;`APP Key`、`security` 和 SIP 密码处理都留在服务端。
14
+
15
+ 1. 向商务经理或对接人获取 HTTP `host`、SIP `sipWsUrl`、`APP Key`、`security` 和分机号码。
16
+ 2. 客户服务端使用 `APP Key`、`security` 和分机号取得 token,再查询坐席账号,按 SDK 会话契约组装 `WebPhoneSession`。
17
+ 3. 浏览器通过 `sessionProvider.createSession()` 请求客户服务端的会话接口,然后调用 `client.connect()`。
18
+ 4. 登录页面或进入拨打页面时调用 `connect()`;退出登录或离开拨打页面时调用 `disconnect()`。销毁应用级客户端时再调用 `dispose()`。
19
+
20
+ 服务端会话接口示意:
21
+
22
+ ```http
23
+ POST /get-session
24
+ Content-Type: application/json
25
+ ```
26
+
27
+ 接口返回完整的 `WebPhoneSession`,至少包括:
28
+
29
+ | 字段 | 用途 |
30
+ |---|---|
31
+ | `sessionId`、`expiresAt` | 会话标识与到期时间 |
32
+ | `agent` | 坐席 ID、分机号和当前坐席状态 |
33
+ | `sip.uri`、`sip.registrar`、`sip.registerTicket` | SIP 注册账号、服务器和注册凭证 |
34
+ | `sip.registerExpires` | SIP 注册有效期,秒 |
35
+ | `transport.wssUrl`、`transport.ticket` | SIP WebSocket 地址与连接凭证 |
36
+ | `iceServers` | ICE/STUN/TURN 配置 |
37
+ | `policy`、`capabilities` | 呼入策略和当前会话支持的能力 |
38
+
39
+ ## 最简浏览器接入
40
+
41
+ ```ts
42
+ import { CCBarClient } from '@16x/webphone-sdk';
43
+
44
+ const client = new CCBarClient({
45
+ platform: 'web',
46
+ sessionProvider: {
47
+ createSession: async () => {
48
+ const response = await fetch('/get-session', {
49
+ method: 'POST',
50
+ credentials: 'include',
51
+ });
52
+ if (!response.ok) throw new Error(`获取软电话会话失败:HTTP ${response.status}`);
53
+ return response.json();
54
+ },
55
+ },
56
+ });
57
+
58
+ await client.initialize();
59
+ await client.connect();
60
+
61
+ const call = await client.dial({ destination: '9196' });
62
+ await call.hangup();
63
+
64
+ await client.disconnect();
65
+ await client.dispose();
66
+ ```
67
+
68
+ 同一个 `CCBarClient` 应由应用级容器持有。重复调用进行中的 `connect()` 会复用同一个连接操作;已连接时再次调用不会重新创建 SIP 资源。
69
+
70
+ ## 状态、事件与通话控制
71
+
72
+ 通话状态通过 `call.state` 读取,也可监听 `call.stateChanged`:
73
+
74
+ | 状态 | 含义 |
75
+ |---|---|
76
+ | `new` | 通话对象已创建 |
77
+ | `dialing` | 已发起呼叫,等待对端响应 |
78
+ | `ringing` | 对端正在振铃 |
79
+ | `connecting` | 对端已接听,媒体连接建立中 |
80
+ | `active` | 通话已接通 |
81
+ | `held` | 通话保持中 |
82
+ | `ended` | 通话正常结束 |
83
+ | `failed` | 通话建立或进行失败 |
84
+
85
+ ```ts
86
+ client.on('call.stateChanged', ({ callId, from, to }) => {
87
+ console.log(callId, from, to);
88
+ });
89
+
90
+ client.on('call.failed', ({ callId, error }) => {
91
+ console.error(callId, error.code, error.message);
92
+ });
93
+
94
+ await call.hold();
95
+ await call.resume();
96
+ await call.mute();
97
+ await call.unmute();
98
+ await call.hangup();
99
+ ```
100
+
101
+ 拨号附加信息可以通过 `userdata` 传递,SDK 会随呼叫发送,后续话单是否展示或支持查询取决于平台侧的话单接口:
102
+
103
+ ```ts
104
+ await client.dial({
105
+ destination: '9196',
106
+ userdata: { orderId: 'order-20261006-001', source: 'web' },
107
+ });
108
+ ```
109
+
110
+ 坐席状态使用 `available`、`break`、`offline`:
111
+
112
+ ```ts
113
+ await client.setAgentStatus('available');
114
+ await client.setAgentStatus('break');
115
+ await client.setAgentStatus('offline');
116
+ ```
117
+
118
+ 登录、掉线和坐席状态也可通过 `client.on()` 监听:
119
+
120
+ ```ts
121
+ client.on('connection.stateChanged', ({ state }) => console.log('SIP:', state));
122
+ client.on('agent.statusChanged', ({ status }) => console.log('坐席:', status));
123
+ client.on('error', ({ error }) => console.error(error.code, error.message));
124
+ ```
125
+
126
+ ## 错误处理
127
+
128
+ SDK 错误提供稳定字符串 `code` 和可读 `message`;如果服务端返回平台数字错误码,可通过 `platformCode` 读取。业务逻辑应按 `code` 分支,向用户展示 `message`:
129
+
130
+ ```ts
131
+ try {
132
+ await client.connect();
133
+ } catch (error) {
134
+ if (error instanceof Error && 'code' in error) {
135
+ const sdkError = error as Error & { code: string; platformCode?: number };
136
+ console.error(sdkError.code, sdkError.message, sdkError.platformCode);
137
+ }
138
+ }
139
+ ```
140
+
141
+ 常见错误包括 `CONFIG_INVALID`(会话或配置无效)、`AUTH_TOKEN_UNAVAILABLE`(服务端未取得登录凭证)、`REGISTRATION_FAILED`(SIP 注册失败)、`CALL_INVALID_DESTINATION`(被叫格式错误)、`CALL_NOT_CONNECTED`(当前没有可操作的通话)和 `CALL_OPERATION_NOT_ALLOWED`(当前通话状态不支持该操作)。
142
+
143
+ ## 资源释放
144
+
145
+ 退出坐席登录或离开拨打页面时调用 `disconnect()`。应用永久销毁客户端时调用 `dispose()`,释放定时器、事件监听、WebSocket、PeerConnection、音轨和音频资源。
146
+
147
+ ## 运行检查
148
+
149
+ ```bash
150
+ npm run lint
151
+ npm run typecheck
152
+ npm test
153
+ npm run build
154
+ npm run pack:check
155
+ ```
156
+
157
+ ## License
158
+
159
+ Copyright 16X. All rights reserved.
160
+
161
+ Private customer distribution only. No redistribution or sublicensing without written permission.