@16x/webphone-sdk 3.1.8 → 3.1.10
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 +67 -0
- package/README.md +64 -17
- package/dist/{CCBarClient-DdxjJrdT.js → CCBarClient-Sr_zlDBu.js} +232 -380
- package/dist/CCBarClient-z-TFUjTB.cjs +1 -0
- package/dist/{Diagnostics-BaGHYBAX.js → Diagnostics-WkQagE6_.js} +1 -1
- package/dist/Diagnostics-x4G0ho6W.cjs +1 -0
- package/dist/api/contracts.d.ts +11 -6
- package/dist/core/CCBarClient.d.ts +11 -14
- package/dist/diagnostics/index.cjs +1 -1
- package/dist/diagnostics/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +7 -7
- package/dist/index.umd.js +16 -16
- package/dist/legacy/index.cjs +1 -1
- package/dist/legacy/index.d.ts +2 -0
- package/dist/legacy/index.js +276 -10
- package/dist/legacy/legacyAes.d.ts +7 -0
- package/dist/legacy/legacySessionProvider.d.ts +79 -0
- package/dist/types.d.ts +5 -1
- package/dist/ui/index.cjs +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -4
- package/dist/CCBarClient-BqMNSIu-.cjs +0 -1
- package/dist/Diagnostics-LQAtyhLu.cjs +0 -1
- package/dist/api/WebPhoneApi.d.ts +0 -28
- package/dist/auth/TokenManager.d.ts +0 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.1.10 - 2026-09-24
|
|
4
|
+
|
|
5
|
+
- **会话入口自动认密文**:接入方把老平台 `seat/account/get` 返回的 `password` 密文**直接**塞进
|
|
6
|
+
`session.sip.registerTicket` 时,SDK 现在会在会话进入的那一处(`createSessionProviderApi`)
|
|
7
|
+
先用老平台内置 key/iv 解一次,解不出来就原样放行 —— 服务端因此可以只做转发,
|
|
8
|
+
不必再自己调 `decryptSipPassword`。
|
|
9
|
+
- 判定条件收得很紧,避免把明文密码当密文解坏:合法 Base64、长度是 4 的倍数、解码后是 16 字节的整数倍、
|
|
10
|
+
AES 解出的文本没有替换符与控制字符。固定种子采样(字母数字 8~40 位、32 位十六进制、
|
|
11
|
+
以及「刚好是 16 字节 Base64 带 `=` 填充」这种最危险的形状)实测没有误判。
|
|
12
|
+
- 只认老平台内置的 key/iv(即 `decryptSipPassword` 的默认值):环境不同、key/iv 被改过时,
|
|
13
|
+
自动判定会失败并退回原值,这种环境仍需接入方自己用 `decryptSipPassword(cipher, { key, iv })`
|
|
14
|
+
解好再放进会话。
|
|
15
|
+
- 非破坏性:已经自己解好的接入方(含 `createLegacySessionProvider`)拿到的会话对象原样不变;
|
|
16
|
+
`decryptSipPassword` 的实现搬到 `src/crypto/legacySipTicket.ts`,公开导出与行为保持不变。
|
|
17
|
+
|
|
18
|
+
## 3.1.9 - 2026-09-24
|
|
19
|
+
|
|
20
|
+
> 同日先发过 `3.8.1`(内容与本文完全相同),按项目版本线改成 3.1.9(3.1.8 的补丁位)重新发布;3.8.1 不再维护。
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
- **坐席状态接口直接用会话里的那张票**:老平台把 fs token 拼在软电话地址上(`wss://…/api/fs/sip-ws?token=…`),
|
|
24
|
+
同一个 token 也用于 HTTP 接口的 `Authorization`。`client.setAgentStatus()` 现在把这个地址带给会话来源
|
|
25
|
+
(`SessionProvider.setAgentStatus(request, context?)`,新增可选参数 `AgentStatusContext.wssUrl`),
|
|
26
|
+
订阅来源可以直接拿它去打 `seat/account/get` 与 `seats/set-status`,**不再单独换一次票**。
|
|
27
|
+
- `createLegacySessionProvider` 的 `setAgentStatus` 按这个顺序取票:
|
|
28
|
+
会话地址里的 token → 最近见过的那个(退签时会话已经断开)→ `getToken`/`tokenUrl` 重新换票(与旧行为一致)。
|
|
29
|
+
它自己在 `createSession` 里拼过 WSS 地址时也会记住那张票;`invalidateToken()` 会把它一起清掉。
|
|
30
|
+
- 非破坏性:`setAgentStatus` 的第二个参数是可选的,旧接入方(不传 context、只用 `getToken`/`tokenUrl`)行为不变。
|
|
31
|
+
|
|
32
|
+
## 4.1.0 - 2026-09-24
|
|
33
|
+
|
|
34
|
+
- 新增 `createLegacySessionProvider`(只在 `@16x/webphone-sdk/legacy` 子入口导出):
|
|
35
|
+
把老平台「取 token → `POST {host}/openapi/token/v1/seat/account/get` → AES-128-CBC/Pkcs7 解 SIP 密码
|
|
36
|
+
→ 拼 WebPhoneSession」以及坐席状态 `POST {host}/openapi/token/v1/seats/set-status` 这一整段交给 SDK,
|
|
37
|
+
页面只需提供换票口子:`getToken()` 函数或 `tokenUrl` 地址(**两者都不会让 SDK 接触 API SECRET**)。
|
|
38
|
+
AES key/iv 默认用老平台固定值(与旧版 `ccbar.js` 一致),可用 `aesKey`/`aesIv` 覆盖;
|
|
39
|
+
平台返回的 `iceServers` 原样透传(含 TURN),缺失时才按 `turnIp` 拼 STUN;
|
|
40
|
+
`refreshSession` 等于整条链路重跑(换一次 SIP 密码)。
|
|
41
|
+
- 新增 `decryptSipPassword(cipherText, { key?, iv? })`(同样只在 `/legacy` 导出):
|
|
42
|
+
解 `seat/account/get` 返回的 `password`/`registerPassword`(AES-128-CBC/Pkcs7、Base64 密文)。
|
|
43
|
+
服务端(Node 18+)与页面都能用;默认平台内置 key/iv,可覆盖;失败原因在 `error.cause`。
|
|
44
|
+
`createLegacySessionProvider` 内部复用同一个实现。
|
|
45
|
+
- 新增错误码 `LEGACY_PLATFORM_UNREACHABLE`(网络/超时/非 JSON)与 `LEGACY_PLATFORM_REJECTED`
|
|
46
|
+
(平台业务错误,平台的 code 在 `error.serverCode` 上)。
|
|
47
|
+
- 非破坏性:`sessionProvider` 契约与服务端拼会话的用法完全不变。
|
|
48
|
+
|
|
49
|
+
## 4.0.0 - 2026-09-24
|
|
50
|
+
|
|
51
|
+
**破坏性变更:移除「SDK 自己换会话」这条能力,会话只能由接入方服务端下发。**
|
|
52
|
+
|
|
53
|
+
- 移除 `tokenProvider` 选项与 `baseUrl` 上的会话职责:SDK 不再请求平台会话接口
|
|
54
|
+
(`POST /webphone/v1/sessions`、`/sessions/{id}/refresh`、`DELETE /sessions/{id}`、
|
|
55
|
+
`/capabilities`、`/agents/me/status`)。
|
|
56
|
+
- 删除 `WebPhoneApi`、`TokenManager` 及其公开导出(`TokenProvider`、`TokenRequest`、
|
|
57
|
+
`TokenProviderResult`、`WebPhoneApiOptions`、`RefreshSessionRequest`、`CapabilitiesResponse`)。
|
|
58
|
+
- `sessionProvider` 成为**唯一**会话来源;未提供时构造抛 `CONFIG_INVALID`。
|
|
59
|
+
- `connect({ extension })` 的 `extension` 参数删除(它只用于向平台换取 token):
|
|
60
|
+
改成 `connect()`,坐席身份由接入方服务端按登录态决定。
|
|
61
|
+
- `sessionProvider.invalidateToken()` 保留:接入方可以在终态失败时清掉自己的服务端缓存。
|
|
62
|
+
- **迁移**:原来用 `tokenProvider` 的接入方,改成在自己服务端拼好 `WebPhoneSession`
|
|
63
|
+
(拿坐席账号 → 解出 SIP 密码 → 拼 WSS 地址),再用 `sessionProvider.createSession` 交给 SDK。
|
|
64
|
+
参考实现:客户 demo 仓库的 `server/get-session.js`(fs token → 坐席账号 → AES-128-CBC 解密 → 拼 WSS)。
|
|
65
|
+
`docs/api/client.md` 与 `docs-site` 的「会话来源」章节已按新契约重写。
|
|
66
|
+
- 仓库结构:`examples/` 下不再提供前端 demo 与 Node/Go 签发服务(它们只服务于旧流程),
|
|
67
|
+
只保留 `examples/network-check`(网络预检页);客户包相应缩减为
|
|
68
|
+
`ccbar-sdk-network-check` + `ccbar-sdk-technical-integration-package`。
|
|
69
|
+
|
|
3
70
|
## 3.1.8 - 2026-09-23
|
|
4
71
|
|
|
5
72
|
- **修 3.1.7 引入的回归:来电会被立刻拒掉(对端收到 480)。**
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @16x/webphone-sdk
|
|
|
10
10
|
|
|
11
11
|
公开包地址:[https://www.npmjs.com/package/@16x/webphone-sdk](https://www.npmjs.com/package/@16x/webphone-sdk)。任何人都可以直接安装,不需要 token。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
浏览器只应接收客户服务端下发的会话(已含短期 WSS ticket 与 SIP 凭证)。不要把 `appSecret`、AES 密钥、固定 SIP 密码或 TURN 密码写入前端代码、示例、日志或构建产物。客户从安装到首通的完整路径见 `docs/quick-start.md`,PC/H5 选择见 `docs/guides/mobile-h5.md`。
|
|
14
14
|
|
|
15
15
|
## Core 快速开始
|
|
16
16
|
|
|
@@ -19,11 +19,13 @@ import { CCBarClient } from '@16x/webphone-sdk';
|
|
|
19
19
|
|
|
20
20
|
const client = new CCBarClient({
|
|
21
21
|
platform: 'web',
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
+
},
|
|
27
29
|
},
|
|
28
30
|
});
|
|
29
31
|
|
|
@@ -51,7 +53,7 @@ await client.dispose();
|
|
|
51
53
|
- `CCBarClient.getDiagnostics` 脱敏诊断报告
|
|
52
54
|
- `CCBarCall.mute/unmute/hold/resume/sendDtmf/transfer/hangup`
|
|
53
55
|
|
|
54
|
-
构造参数里几个按需用的:`sessionProvider
|
|
56
|
+
构造参数里几个按需用的:`sessionProvider`(**必填**,4.0.0 起唯一会话来源,见下一节)、
|
|
55
57
|
`sipKeepaliveSeconds`(3.1.1+,默认 25 秒重发 REGISTER 撑住 NAT/代理上的长连接,`0` 关闭)、
|
|
56
58
|
`sharedWorker`(多标签页入口)、`baseUrl`、`maxConcurrentCalls`。
|
|
57
59
|
|
|
@@ -60,10 +62,10 @@ INVITE,预收集候选才能做到「点完就出局」。
|
|
|
60
62
|
|
|
61
63
|
PC Web 支持完整 Core 通话能力。移动 H5 首期仅承诺受支持系统浏览器中的前台单路外呼;来电、保持、转接、后台和锁屏通话不在承诺范围内。
|
|
62
64
|
|
|
63
|
-
##
|
|
65
|
+
## 会话来源(sessionProvider)
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
会话由接入方在自己的服务端拼好后交给 SDK:**4.0.0 起这是唯一的会话来源**(3.x 的 `tokenProvider`
|
|
68
|
+
以及 SDK 自己请求平台会话接口的能力已移除,迁移方式见 `CHANGELOG.md` 的 4.0.0 条目)。
|
|
67
69
|
|
|
68
70
|
```ts
|
|
69
71
|
const client = new CCBarClient({
|
|
@@ -108,10 +110,12 @@ import { CCBarSDK } from '@16x/webphone-sdk/legacy';
|
|
|
108
110
|
|
|
109
111
|
const legacy = new CCBarSDK({
|
|
110
112
|
platform: 'web',
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
sessionProvider: {
|
|
114
|
+
createSession: async () => fetch('/get-session', {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
credentials: 'include',
|
|
117
|
+
}).then(response => response.json()),
|
|
118
|
+
},
|
|
115
119
|
});
|
|
116
120
|
|
|
117
121
|
await legacy.login();
|
|
@@ -120,6 +124,49 @@ await legacy.hangup();
|
|
|
120
124
|
await legacy.signOut();
|
|
121
125
|
```
|
|
122
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
|
+
// 取 token 二选一:给函数,或给地址让 SDK 去 POST(都是你们自己的接口)
|
|
139
|
+
getToken: () => fetch('/get-token', { method: 'POST' }).then(response => response.json()),
|
|
140
|
+
// tokenUrl: '/get-token',
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- **SDK 不碰 API SECRET**:`/openapi/v1/token/fs` 的加签始终在你们服务端,SDK 只拿结果;
|
|
146
|
+
- **AES key 会随包下发**:老平台那把 16 字节 key/iv 是固定值(旧版 `ccbar.js` 里同样是硬编码),
|
|
147
|
+
可用 `aesKey` / `aesIv` 覆盖;介意的话就走服务端拼会话(见下一节);
|
|
148
|
+
- 坐席状态:`client.setAgentStatus(...)` 打到 `{host}/openapi/token/v1/seats/set-status`,
|
|
149
|
+
其中 `extension` 传的是**坐席账号**(`username`,可能带企业前缀);
|
|
150
|
+
- **浏览器直连网关**:需要网关放行 CORS 与调用方 IP(`seat/account/get` 有 IP 白名单),部署前先确认;
|
|
151
|
+
- 取票与 SIP 密码都按有效期提前换(`refreshBufferSeconds`,默认 180 秒)。
|
|
152
|
+
|
|
153
|
+
### `decryptSipPassword`:解 `seat/account/get` 返回的 `password`
|
|
154
|
+
|
|
155
|
+
`password`(或 `registerPassword`)是 **AES-128-CBC/Pkcs7 的密文、Base64 编码**,
|
|
156
|
+
SDK 把解密单独导出,服务端(Node 18+)与页面都能用:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { decryptSipPassword } from '@16x/webphone-sdk/legacy';
|
|
160
|
+
|
|
161
|
+
const password = await decryptSipPassword(data.password); // 明文 SIP 密码
|
|
162
|
+
// 环境用的是别的 key/iv 时:await decryptSipPassword(data.password, { key, iv })
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- key/iv 各 16 字节,**默认是平台内置固定值**(与旧版 `ccbar.js` 一致),可用 `key`/`iv` 覆盖;
|
|
166
|
+
- 失败抛 `CCBarError`,可读原因在 `error.cause`(密文为空 / 不是合法 Base64 / 解密失败);
|
|
167
|
+
- 依赖 WebCrypto:浏览器需安全上下文(https / localhost),Node 需 18+;
|
|
168
|
+
- `createLegacySessionProvider` 内部用的就是它。
|
|
169
|
+
|
|
123
170
|
旧方法映射如下:
|
|
124
171
|
|
|
125
172
|
| Legacy | Core |
|
|
@@ -137,8 +184,8 @@ await legacy.signOut();
|
|
|
137
184
|
`insideCall(number)` 会使用 `type: 'extension'` 标记 SIP 呼叫(INVITE 上带 `X-XCall-Destination-Type: extension`),
|
|
138
185
|
浏览器不拼接客户前缀;后端按当前会话租户解析分机并路由到同租户坐席。普通 `dial()` 不传 type,继续按外呼处理。
|
|
139
186
|
|
|
140
|
-
|
|
141
|
-
|
|
187
|
+
注意:如果你们的网关只认「企业前缀 + 分机号」,`type: 'extension'` 这个头它不解析,
|
|
188
|
+
就需要自己拼好前缀再拨(`dial({ destination: \`${prefix}${number}\` })`),
|
|
142
189
|
具体见客户示例仓库的 `prefixExtension()`。
|
|
143
190
|
|
|
144
191
|
`setBu()` 不会伪造 `busy`。该方法抛出 `DeprecatedError`,因为忙碌状态应由通话状态或服务端状态策略驱动。所有 Legacy 方法仅在开发构建中各警告一次,生产构建不输出弃用日志。`signOut()` 是终态操作;如需保留实例以便重新连接,请直接使用 Core 的 `disconnect()`。
|
|
@@ -146,7 +193,7 @@ await legacy.signOut();
|
|
|
146
193
|
## 版本与更新记录
|
|
147
194
|
|
|
148
195
|
当前版本见 `package.json`,每个版本改了什么见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
149
|
-
最近几个版本:3.1.0 新增 `sessionProvider`;3.1.1 新增 SIP 保活;3.1.2 拨号带 ICE 候选池。
|
|
196
|
+
最近几个版本:4.0.0 移除 `tokenProvider`(只保留 `sessionProvider`,破坏性变更);3.1.0 新增 `sessionProvider`;3.1.1 新增 SIP 保活;3.1.2 拨号带 ICE 候选池。
|
|
150
197
|
|
|
151
198
|
## 开发与验收
|
|
152
199
|
|