@16x/webphone-sdk 3.8.1 → 4.0.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.
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../SharedWorkerProtocol-39dch6xL.cjs"),t=require("../CCBarClient-BJryiSYT.cjs");require("../createJsSipFactory-Dr8HbLl-.cjs");var n=class extends Error{code=`LEGACY_METHOD_DEPRECATED`;constructor(e,t){super(`${e} 已废弃:${t}`),this.name=`DeprecatedError`}},r=class{client;warnedMethods=new Set;unsubscribeIncoming;currentCall;constructor(e){this.client=new t.t(e),this.unsubscribeIncoming=this.client.on(`call.incoming`,({callId:e})=>{this.currentCall=this.client.getCalls().find(t=>t.id===e)})}getToken(...e){return this.warn(`getToken`,`请改用 client.connect()`),this.client.connect()}getAccount(...e){return this.warn(`getAccount`,`请改用 client.connect()`),this.client.connect()}login(...e){return this.warn(`login`,`请改用 client.connect()`),this.client.connect()}async call(e,...t){this.warn(`call`,`请改用 client.dial({ destination })`);let n=await this.client.dial({destination:String(e)});return this.currentCall=n,n}async insideCall(e,...t){this.warn(`insideCall`,`请改用 client.dial({ destination, type: 'extension' })`);let n=await this.client.dial({destination:String(e),type:`extension`});return this.currentCall=n,n}answer(...e){this.warn(`answer`,`请改用 call.answer()`);let t=this.requireCurrentCall();return this.client.answer(t.id)}async hangup(...e){this.warn(`hangup`,`请改用 call.hangup()`);let t=this.requireCurrentCall();try{await t.hangup()}finally{this.currentCall===t&&(this.currentCall=void 0)}}holdCall(...e){return this.warn(`holdCall`,`请改用 call.hold()`),this.requireCurrentCall().hold()}unholdCall(...e){return this.warn(`unholdCall`,`请改用 call.resume()`),this.requireCurrentCall().resume()}transSo(e,...t){return this.warn(`transSo`,`请改用 call.transfer({ target, type: 'blind' })`),this.requireCurrentCall().transfer({target:String(e),type:`blind`})}async signOut(...e){this.warn(`signOut`,`请改用 client.disconnect() 和 client.dispose()`);let t;try{await this.client.disconnect()}catch(e){t=e}try{await this.client.dispose()}catch(e){t??=e}if(this.unsubscribeIncoming(),this.currentCall=void 0,t!==void 0)throw t}setId(...e){return this.warn(`setId`,`请改用 client.setAgentStatus('available')`),this.client.setAgentStatus(`available`)}setBu(...e){throw this.warn(`setBu`,`busy由通话状态或服务端状态策略控制,浏览器不得伪造`),new n(`setBu()`,`busy由通话状态或服务端状态策略控制,浏览器不得伪造`)}setRe(...e){return this.warn(`setRe`,`请改用 client.setAgentStatus('break')`),this.client.setAgentStatus(`break`)}requireCurrentCall(){let t=this.client.getActiveCall();if(t!==void 0&&(this.currentCall=t),this.currentCall===void 0)throw new e.i(`CALL_NOT_CONNECTED`,`call`,!1);return this.currentCall}warn(e,t){i()&&!this.warnedMethods.has(e)&&(this.warnedMethods.add(e),console.warn(`[CCBarSDK] ${e}() 已废弃。${t}`))}};function i(){return!1}var a=`q7X4p6MvK1z8Lb3A`,o=`W9e2T4mN0aQ7Ru6C`;async function s(t,n={}){let r=String(t??``).replace(/\s+/g,``);if(!r)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`解密的密文为空:坐席账号里没有 password/registerPassword`});let i=n.subtle??globalThis.crypto?.subtle;if(i===void 0)throw new e.i(`CONFIG_INVALID`,`configuration`,!1,{cause:`需要 WebCrypto 才能解 SIP 密码:请在安全上下文(https / localhost)或 Node 18+ 里运行`});let s;try{let e=atob(r);s=new Uint8Array(new ArrayBuffer(e.length));for(let t=0;t<e.length;t+=1)s[t]=e.charCodeAt(t)}catch{throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`密文不是合法的 Base64:确认取的是 password/registerPassword 原值,没有被截断或转义`})}let l=await i.importKey(`raw`,c(n.key??a),`AES-CBC`,!1,[`decrypt`]);try{let e=await i.decrypt({name:`AES-CBC`,iv:c(n.iv??o)},l,s);return new TextDecoder(`utf-8`).decode(e).trim()}catch(t){throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`SIP 密码解密失败(检查 key/iv 与该环境是否一致):${t instanceof Error?t.message:String(t)}`})}}function c(e){let t=new TextEncoder().encode(e),n=new Uint8Array(new ArrayBuffer(t.byteLength));return n.set(t),n}var l=`/openapi/token/v1`,u=`/seat/account/get`,d=`/seats/set-status`,f=`/api/fs/sip-ws`,p=7443,m=3478,h=600,g=600,_=180,v=60,y=5e3,b=2e4,x=[`outbound`,`inbound`,`mute`,`dtmf`,`hold`,`blind_transfer`],S={available:{status:`Available`,reason:`空闲`},break:{status:`On Break`,reason:`休息`},offline:{status:`Logged Out`,reason:``}};function C(t){let n=w(t.host),r=T(t.pathPrefix??l),i=`${n}${r}${E(t.accountPath??u)}`,a=`${n}${r}${E(t.statusPath??d)}`,o=E(t.sipWsPath??f),c={...t.aesKey?{key:t.aesKey}:{},...t.aesIv?{iv:t.aesIv}:{}},b=Math.max(0,t.refreshBufferSeconds??_),C=t.fetchImpl??globalThis.fetch,j,M,N=``;async function P(){if(t.getToken!==void 0)return t.getToken();if(t.tokenUrl!==void 0){let n=t.tokenMethod??`POST`,r={method:n,headers:{Accept:`application/json`,...t.tokenHeaders??{}},credentials:`include`};n===`POST`&&(r.headers={"Content-Type":`application/json`,...r.headers},r.body=JSON.stringify(t.tokenBody??{}));let i=await A(C,t.tokenUrl,r,`取 token`),a=await i.text();try{return JSON.parse(a)}catch{throw new e.i(`AUTH_TOKEN_UNAVAILABLE`,`auth`,!0,{status:i.status,cause:`取 token 的接口返回了非 JSON 响应(HTTP ${i.status}):${a.trim().slice(0,120)}`})}}throw new e.i(`CONFIG_INVALID`,`configuration`,!1,{cause:`createLegacySessionProvider 需要 getToken 或 tokenUrl;坐席状态也可以直接用会话软电话地址里的 token(setAgentStatus 的 context.wssUrl),但这次调用没有会话可用`})}async function F(){if(j&&j.expiresAtMs-Date.now()>y)return j.token;let t;try{t=await P()}catch(t){throw t instanceof e.i?t:new e.i(`AUTH_TOKEN_UNAVAILABLE`,`auth`,!0,{cause:t})}let n=k(t);if(!n.token)throw new e.i(`AUTH_TOKEN_UNAVAILABLE`,`auth`,!0,{cause:`取 token 的返回里没有 token(支持 {token}、{accessToken}、{data:{token}} 或裸字符串)`});let r=O(n.expires,3600);return j={token:n.token,expiresAtMs:Date.now()+r*1e3},j.token}function I(e){if(!e)return``;try{return String(new URL(e).searchParams.get(`token`)??``).trim()}catch{return``}}async function L(e){let t=I(e?.wssUrl);return t?(N=t,t):N||F()}async function R(t){let n=t||await F(),r=await A(C,i,{method:`POST`,headers:{Authorization:n,"Content-Type":`application/json`,Accept:`application/json`},body:`{}`},`取坐席账号`),a=await r.text(),o;try{o=JSON.parse(a)}catch{throw new e.i(`LEGACY_PLATFORM_UNREACHABLE`,`network`,!0,{status:r.status,cause:`${i} 返回了非 JSON 响应(HTTP ${r.status});请确认 host 填的是接口网关(不是文档站)`})}let s=Number(o.code??NaN);if(!r.ok||s!==0)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,s===50,{status:r.status,serverCode:Number.isFinite(s)?String(s):void 0,cause:o.message||`取坐席账号失败(HTTP ${r.status})`});if(!o.data)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`坐席账号接口没有返回 data`});return M=o.data,M}let z=e=>s(e,c);function B(r,i){let a=Number(r.wssPort)||p,s=String(r.domain??``).trim(),c=String(t.sipWsUrl??``).trim(),l;if(c)l=new URL(c,n),l.protocol===`http:`&&(l.protocol=`ws:`),l.protocol===`https:`&&(l.protocol=`wss:`);else{if(!s)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`坐席账号里没有 domain,无法拼软电话地址;可用 sipWsUrl 显式指定`});let t=a===443?``:`:${a}`;l=new URL(`wss://${s}${t}${o}`)}return(t.attachTokenToWss??!0)&&i&&l.searchParams.set(`token`,i),l.toString()}function V(e){let t=(Array.isArray(e.iceServers)?e.iceServers:[]).map(e=>{let t=(Array.isArray(e?.urls)?e.urls:typeof e?.urls==`string`?[e.urls]:[]).map(e=>String(e??``).trim()).filter(Boolean);if(t.length!==0)return{urls:t,...e?.username?{username:String(e.username)}:{},...e?.credential?{credential:String(e.credential)}:{}}}).filter(e=>e!==void 0);if(t.length>0)return t;let n=String(e.turnIp??``).trim();return n?[{urls:[`stun:${n}:${Number(e.turnPort)||m}`]}]:[]}async function H(){let r=await F(),i=await R(),a=String(i.username??i.account??``).trim();if(!a)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`坐席账号里没有 username`});let o=String(i.registerPassword??i.password??``),s=o?await z(o):``;if(!s)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`坐席账号里没有 password/registerPassword,或解密后为空`});let c=O(i.expiresIn??i.expires_in,g),l=Math.max(0,Math.round((j.expiresAtMs-Date.now())/1e3)),u=Math.max(v,Math.min(l||c,c)-b),d=String(i.domain??``).trim()||D(n),f=B(i,r);return r&&(N=r),{sessionId:`legacy-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,expiresAt:Math.floor(Date.now()/1e3)+u,agent:{id:a,externalUserId:a,displayName:a,extension:a,status:`Available`},sip:{uri:`sip:${a}@${d}`,registrar:d,registerTicket:s,registerExpires:Number(t.registerExpires)>0?Number(t.registerExpires):h,ticketExpiresIn:u},transport:{wssUrl:f,ticket:``,ticketExpiresIn:u},iceServers:V(i),policy:{maxConcurrentCalls:Number(t.maxConcurrentCalls)>0?Number(t.maxConcurrentCalls):2,incomingEnabled:t.incomingEnabled??!0,mobileIncomingEnabled:t.mobileIncomingEnabled??!0,backgroundCallingSupported:!1},capabilities:[...t.capabilities??x]}}return{createSession:H,refreshSession:H,setAgentStatus:async({status:t,reason:n},r)=>{let i=await L(r),o=M??await R(i),s=String(o.username??o.account??``).trim();if(!s)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,!1,{cause:`坐席账号里没有 username`});let c=S[t],l=await A(C,a,{method:`POST`,headers:{Authorization:i,"Content-Type":`application/json`,Accept:`application/json`},body:JSON.stringify({extension:s,status:c.status,reason:n||c.reason})},`设置坐席状态`),u=await l.text(),d;try{d=JSON.parse(u)}catch{throw new e.i(`LEGACY_PLATFORM_UNREACHABLE`,`network`,!0,{status:l.status,cause:`${a} 返回了非 JSON 响应(HTTP ${l.status})`})}let f=Number(d.code??NaN);if(!l.ok||f!==0)throw new e.i(`LEGACY_PLATFORM_REJECTED`,`auth`,f===50,{status:l.status,serverCode:Number.isFinite(f)?String(f):void 0,cause:d.message||`设置坐席状态失败(HTTP ${l.status})`})},invalidateToken:()=>{j=void 0,N=``},getSeatAccount:async()=>M??R()}}function w(t){let n=String(t??``).trim().replace(/\/+$/,``);if(!n)throw new e.i(`CONFIG_INVALID`,`configuration`,!1,{cause:`createLegacySessionProvider 需要 host`});return/^https?:\/\//i.test(n)?n:`https://${n}`}function T(e){let t=String(e??``).trim();return!t||t===`/`?``:`/${t.replace(/^\/+|\/+$/g,``)}`}function E(e){let t=String(e??``).trim();return t.startsWith(`/`)?t:`/${t}`}function D(e){try{return new URL(e).hostname}catch{return``}}function O(e,t){let n=Number(e);return!Number.isFinite(n)||n<=0?t:(n>86400&&(n/=1e3),Math.min(Math.max(Math.floor(n),v),86400))}function k(e){if(typeof e==`string`)return{token:e.trim()};let t=e??{},n=typeof t.data==`object`&&t.data!==null?t.data:void 0;for(let e of[t,n]){if(e===void 0)continue;let t=e.token??e.accessToken;if(typeof t==`string`&&t.trim()){let n=Number(e.expires??e.expiresIn??e.expires_at);return{token:t.trim(),...Number.isFinite(n)&&n>0?{expires:n}:{}}}}return{token:``}}async function A(t,n,r,i){let a=new AbortController,o=setTimeout(()=>a.abort(),b);try{return await t(n,{...r,signal:a.signal})}catch(t){let r=a.signal.aborted;throw new e.i(r?`NETWORK_TIMEOUT`:`LEGACY_PLATFORM_UNREACHABLE`,`network`,!0,{cause:r?`${i}超时(${b/1e3} 秒):POST ${n}`:`${i}失败:无法请求 ${n}(${t instanceof Error?t.message:String(t)})`})}finally{clearTimeout(o)}}var j=`0.1.0`;typeof window<`u`&&(window.CCBarSDK=r),exports.CCBarSDK=r,exports.DeprecatedError=n,exports.createLegacySessionProvider=C,exports.decryptSipPassword=s,exports.legacyVersion=j;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../SharedWorkerProtocol-39dch6xL.cjs"),t=require("../CCBarClient-BFq7695C.cjs");require("../createJsSipFactory-Dr8HbLl-.cjs");var n=class extends Error{code=`LEGACY_METHOD_DEPRECATED`;constructor(e,t){super(`${e} 已废弃:${t}`),this.name=`DeprecatedError`}},r=class{client;warnedMethods=new Set;unsubscribeIncoming;currentCall;constructor(e){this.client=new t.t(e),this.unsubscribeIncoming=this.client.on(`call.incoming`,({callId:e})=>{this.currentCall=this.client.getCalls().find(t=>t.id===e)})}getToken(...e){return this.warn(`getToken`,`请改用 client.connect()`),this.client.connect()}getAccount(...e){return this.warn(`getAccount`,`请改用 client.connect()`),this.client.connect()}login(...e){return this.warn(`login`,`请改用 client.connect()`),this.client.connect()}async call(e,...t){this.warn(`call`,`请改用 client.dial({ destination })`);let n=await this.client.dial({destination:String(e)});return this.currentCall=n,n}async insideCall(e,...t){this.warn(`insideCall`,`请改用 client.dial({ destination, type: 'extension' })`);let n=await this.client.dial({destination:String(e),type:`extension`});return this.currentCall=n,n}answer(...e){this.warn(`answer`,`请改用 call.answer()`);let t=this.requireCurrentCall();return this.client.answer(t.id)}async hangup(...e){this.warn(`hangup`,`请改用 call.hangup()`);let t=this.requireCurrentCall();try{await t.hangup()}finally{this.currentCall===t&&(this.currentCall=void 0)}}holdCall(...e){return this.warn(`holdCall`,`请改用 call.hold()`),this.requireCurrentCall().hold()}unholdCall(...e){return this.warn(`unholdCall`,`请改用 call.resume()`),this.requireCurrentCall().resume()}transSo(e,...t){return this.warn(`transSo`,`请改用 call.transfer({ target, type: 'blind' })`),this.requireCurrentCall().transfer({target:String(e),type:`blind`})}async signOut(...e){this.warn(`signOut`,`请改用 client.disconnect() 和 client.dispose()`);let t;try{await this.client.disconnect()}catch(e){t=e}try{await this.client.dispose()}catch(e){t??=e}if(this.unsubscribeIncoming(),this.currentCall=void 0,t!==void 0)throw t}setId(...e){return this.warn(`setId`,`请改用 client.setAgentStatus('available')`),this.client.setAgentStatus(`available`)}setBu(...e){throw this.warn(`setBu`,`busy由通话状态或服务端状态策略控制,浏览器不得伪造`),new n(`setBu()`,`busy由通话状态或服务端状态策略控制,浏览器不得伪造`)}setRe(...e){return this.warn(`setRe`,`请改用 client.setAgentStatus('break')`),this.client.setAgentStatus(`break`)}requireCurrentCall(){let t=this.client.getActiveCall();if(t!==void 0&&(this.currentCall=t),this.currentCall===void 0)throw new e.i(`CALL_NOT_CONNECTED`,`call`,!1);return this.currentCall}warn(e,t){i()&&!this.warnedMethods.has(e)&&(this.warnedMethods.add(e),console.warn(`[CCBarSDK] ${e}() 已废弃。${t}`))}};function i(){return!1}var a=`0.1.0`;typeof window<`u`&&(window.CCBarSDK=r),exports.CCBarSDK=r,exports.DeprecatedError=n,exports.legacyVersion=a;
@@ -1,7 +1,5 @@
1
1
  import { CCBarSDK } from './CCBarSDK.js';
2
2
  export { CCBarSDK, DeprecatedError } from './CCBarSDK.js';
3
- export { decryptSipPassword, type DecryptSipPasswordOptions } from './legacyAes.js';
4
- export { createLegacySessionProvider, type LegacyPlatformSessionProviderOptions, type LegacySeatAccount, type LegacyTokenResult, } from './legacySessionProvider.js';
5
3
  export declare const legacyVersion = "0.1.0";
6
4
  declare global {
7
5
  interface Window {
@@ -1,5 +1,5 @@
1
1
  import { i as e } from "../SharedWorkerProtocol-DKVQ5-po.js";
2
- import { t } from "../CCBarClient-CbkMKzEw.js";
2
+ import { t } from "../CCBarClient-CAGBUxil.js";
3
3
  //#region src/legacy/CCBarSDK.ts
4
4
  var n = class extends Error {
5
5
  code = "LEGACY_METHOD_DEPRECATED";
@@ -101,305 +101,8 @@ function i() {
101
101
  return !1;
102
102
  }
103
103
  //#endregion
104
- //#region src/legacy/legacyAes.ts
105
- var a = "q7X4p6MvK1z8Lb3A", o = "W9e2T4mN0aQ7Ru6C";
106
- async function s(t, n = {}) {
107
- let r = String(t ?? "").replace(/\s+/g, "");
108
- if (!r) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "解密的密文为空:坐席账号里没有 password/registerPassword" });
109
- let i = n.subtle ?? globalThis.crypto?.subtle;
110
- if (i === void 0) throw new e("CONFIG_INVALID", "configuration", !1, { cause: "需要 WebCrypto 才能解 SIP 密码:请在安全上下文(https / localhost)或 Node 18+ 里运行" });
111
- let s;
112
- try {
113
- let e = atob(r);
114
- s = new Uint8Array(new ArrayBuffer(e.length));
115
- for (let t = 0; t < e.length; t += 1) s[t] = e.charCodeAt(t);
116
- } catch {
117
- throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "密文不是合法的 Base64:确认取的是 password/registerPassword 原值,没有被截断或转义" });
118
- }
119
- let l = await i.importKey("raw", c(n.key ?? a), "AES-CBC", !1, ["decrypt"]);
120
- try {
121
- let e = await i.decrypt({
122
- name: "AES-CBC",
123
- iv: c(n.iv ?? o)
124
- }, l, s);
125
- return new TextDecoder("utf-8").decode(e).trim();
126
- } catch (t) {
127
- throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: `SIP 密码解密失败(检查 key/iv 与该环境是否一致):${t instanceof Error ? t.message : String(t)}` });
128
- }
129
- }
130
- function c(e) {
131
- let t = new TextEncoder().encode(e), n = new Uint8Array(new ArrayBuffer(t.byteLength));
132
- return n.set(t), n;
133
- }
134
- //#endregion
135
- //#region src/legacy/legacySessionProvider.ts
136
- var l = "/openapi/token/v1", u = "/seat/account/get", d = "/seats/set-status", f = "/api/fs/sip-ws", p = 7443, m = 3478, h = 600, g = 600, _ = 180, v = 60, y = 5e3, b = 2e4, x = [
137
- "outbound",
138
- "inbound",
139
- "mute",
140
- "dtmf",
141
- "hold",
142
- "blind_transfer"
143
- ], S = {
144
- available: {
145
- status: "Available",
146
- reason: "空闲"
147
- },
148
- break: {
149
- status: "On Break",
150
- reason: "休息"
151
- },
152
- offline: {
153
- status: "Logged Out",
154
- reason: ""
155
- }
156
- };
157
- function C(t) {
158
- let n = w(t.host), r = T(t.pathPrefix ?? l), i = `${n}${r}${E(t.accountPath ?? u)}`, a = `${n}${r}${E(t.statusPath ?? d)}`, o = E(t.sipWsPath ?? f), c = {
159
- ...t.aesKey ? { key: t.aesKey } : {},
160
- ...t.aesIv ? { iv: t.aesIv } : {}
161
- }, b = Math.max(0, t.refreshBufferSeconds ?? _), C = t.fetchImpl ?? globalThis.fetch, j, M, N = "";
162
- async function P() {
163
- if (t.getToken !== void 0) return t.getToken();
164
- if (t.tokenUrl !== void 0) {
165
- let n = t.tokenMethod ?? "POST", r = {
166
- method: n,
167
- headers: {
168
- Accept: "application/json",
169
- ...t.tokenHeaders ?? {}
170
- },
171
- credentials: "include"
172
- };
173
- n === "POST" && (r.headers = {
174
- "Content-Type": "application/json",
175
- ...r.headers
176
- }, r.body = JSON.stringify(t.tokenBody ?? {}));
177
- let i = await A(C, t.tokenUrl, r, "取 token"), a = await i.text();
178
- try {
179
- return JSON.parse(a);
180
- } catch {
181
- throw new e("AUTH_TOKEN_UNAVAILABLE", "auth", !0, {
182
- status: i.status,
183
- cause: `取 token 的接口返回了非 JSON 响应(HTTP ${i.status}):${a.trim().slice(0, 120)}`
184
- });
185
- }
186
- }
187
- throw new e("CONFIG_INVALID", "configuration", !1, { cause: "createLegacySessionProvider 需要 getToken 或 tokenUrl;坐席状态也可以直接用会话软电话地址里的 token(setAgentStatus 的 context.wssUrl),但这次调用没有会话可用" });
188
- }
189
- async function F() {
190
- if (j && j.expiresAtMs - Date.now() > y) return j.token;
191
- let t;
192
- try {
193
- t = await P();
194
- } catch (t) {
195
- throw t instanceof e ? t : new e("AUTH_TOKEN_UNAVAILABLE", "auth", !0, { cause: t });
196
- }
197
- let n = k(t);
198
- if (!n.token) throw new e("AUTH_TOKEN_UNAVAILABLE", "auth", !0, { cause: "取 token 的返回里没有 token(支持 {token}、{accessToken}、{data:{token}} 或裸字符串)" });
199
- let r = O(n.expires, 3600);
200
- return j = {
201
- token: n.token,
202
- expiresAtMs: Date.now() + r * 1e3
203
- }, j.token;
204
- }
205
- function I(e) {
206
- if (!e) return "";
207
- try {
208
- return String(new URL(e).searchParams.get("token") ?? "").trim();
209
- } catch {
210
- return "";
211
- }
212
- }
213
- async function L(e) {
214
- let t = I(e?.wssUrl);
215
- return t ? (N = t, t) : N || F();
216
- }
217
- async function R(t) {
218
- let n = t || await F(), r = await A(C, i, {
219
- method: "POST",
220
- headers: {
221
- Authorization: n,
222
- "Content-Type": "application/json",
223
- Accept: "application/json"
224
- },
225
- body: "{}"
226
- }, "取坐席账号"), a = await r.text(), o;
227
- try {
228
- o = JSON.parse(a);
229
- } catch {
230
- throw new e("LEGACY_PLATFORM_UNREACHABLE", "network", !0, {
231
- status: r.status,
232
- cause: `${i} 返回了非 JSON 响应(HTTP ${r.status});请确认 host 填的是接口网关(不是文档站)`
233
- });
234
- }
235
- let s = Number(o.code ?? NaN);
236
- if (!r.ok || s !== 0) throw new e("LEGACY_PLATFORM_REJECTED", "auth", s === 50, {
237
- status: r.status,
238
- serverCode: Number.isFinite(s) ? String(s) : void 0,
239
- cause: o.message || `取坐席账号失败(HTTP ${r.status})`
240
- });
241
- if (!o.data) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "坐席账号接口没有返回 data" });
242
- return M = o.data, M;
243
- }
244
- let z = (e) => s(e, c);
245
- function B(r, i) {
246
- let a = Number(r.wssPort) || p, s = String(r.domain ?? "").trim(), c = String(t.sipWsUrl ?? "").trim(), l;
247
- if (c) l = new URL(c, n), l.protocol === "http:" && (l.protocol = "ws:"), l.protocol === "https:" && (l.protocol = "wss:");
248
- else {
249
- if (!s) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "坐席账号里没有 domain,无法拼软电话地址;可用 sipWsUrl 显式指定" });
250
- let t = a === 443 ? "" : `:${a}`;
251
- l = new URL(`wss://${s}${t}${o}`);
252
- }
253
- return (t.attachTokenToWss ?? !0) && i && l.searchParams.set("token", i), l.toString();
254
- }
255
- function V(e) {
256
- let t = (Array.isArray(e.iceServers) ? e.iceServers : []).map((e) => {
257
- let t = (Array.isArray(e?.urls) ? e.urls : typeof e?.urls == "string" ? [e.urls] : []).map((e) => String(e ?? "").trim()).filter(Boolean);
258
- if (t.length !== 0) return {
259
- urls: t,
260
- ...e?.username ? { username: String(e.username) } : {},
261
- ...e?.credential ? { credential: String(e.credential) } : {}
262
- };
263
- }).filter((e) => e !== void 0);
264
- if (t.length > 0) return t;
265
- let n = String(e.turnIp ?? "").trim();
266
- return n ? [{ urls: [`stun:${n}:${Number(e.turnPort) || m}`] }] : [];
267
- }
268
- async function H() {
269
- let r = await F(), i = await R(), a = String(i.username ?? i.account ?? "").trim();
270
- if (!a) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "坐席账号里没有 username" });
271
- let o = String(i.registerPassword ?? i.password ?? ""), s = o ? await z(o) : "";
272
- if (!s) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "坐席账号里没有 password/registerPassword,或解密后为空" });
273
- let c = O(i.expiresIn ?? i.expires_in, g), l = Math.max(0, Math.round((j.expiresAtMs - Date.now()) / 1e3)), u = Math.max(v, Math.min(l || c, c) - b), d = String(i.domain ?? "").trim() || D(n), f = B(i, r);
274
- return r && (N = r), {
275
- sessionId: `legacy-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
276
- expiresAt: Math.floor(Date.now() / 1e3) + u,
277
- agent: {
278
- id: a,
279
- externalUserId: a,
280
- displayName: a,
281
- extension: a,
282
- status: "Available"
283
- },
284
- sip: {
285
- uri: `sip:${a}@${d}`,
286
- registrar: d,
287
- registerTicket: s,
288
- registerExpires: Number(t.registerExpires) > 0 ? Number(t.registerExpires) : h,
289
- ticketExpiresIn: u
290
- },
291
- transport: {
292
- wssUrl: f,
293
- ticket: "",
294
- ticketExpiresIn: u
295
- },
296
- iceServers: V(i),
297
- policy: {
298
- maxConcurrentCalls: Number(t.maxConcurrentCalls) > 0 ? Number(t.maxConcurrentCalls) : 2,
299
- incomingEnabled: t.incomingEnabled ?? !0,
300
- mobileIncomingEnabled: t.mobileIncomingEnabled ?? !0,
301
- backgroundCallingSupported: !1
302
- },
303
- capabilities: [...t.capabilities ?? x]
304
- };
305
- }
306
- return {
307
- createSession: H,
308
- refreshSession: H,
309
- setAgentStatus: async ({ status: t, reason: n }, r) => {
310
- let i = await L(r), o = M ?? await R(i), s = String(o.username ?? o.account ?? "").trim();
311
- if (!s) throw new e("LEGACY_PLATFORM_REJECTED", "auth", !1, { cause: "坐席账号里没有 username" });
312
- let c = S[t], l = await A(C, a, {
313
- method: "POST",
314
- headers: {
315
- Authorization: i,
316
- "Content-Type": "application/json",
317
- Accept: "application/json"
318
- },
319
- body: JSON.stringify({
320
- extension: s,
321
- status: c.status,
322
- reason: n || c.reason
323
- })
324
- }, "设置坐席状态"), u = await l.text(), d;
325
- try {
326
- d = JSON.parse(u);
327
- } catch {
328
- throw new e("LEGACY_PLATFORM_UNREACHABLE", "network", !0, {
329
- status: l.status,
330
- cause: `${a} 返回了非 JSON 响应(HTTP ${l.status})`
331
- });
332
- }
333
- let f = Number(d.code ?? NaN);
334
- if (!l.ok || f !== 0) throw new e("LEGACY_PLATFORM_REJECTED", "auth", f === 50, {
335
- status: l.status,
336
- serverCode: Number.isFinite(f) ? String(f) : void 0,
337
- cause: d.message || `设置坐席状态失败(HTTP ${l.status})`
338
- });
339
- },
340
- invalidateToken: () => {
341
- j = void 0, N = "";
342
- },
343
- getSeatAccount: async () => M ?? R()
344
- };
345
- }
346
- function w(t) {
347
- let n = String(t ?? "").trim().replace(/\/+$/, "");
348
- if (!n) throw new e("CONFIG_INVALID", "configuration", !1, { cause: "createLegacySessionProvider 需要 host" });
349
- return /^https?:\/\//i.test(n) ? n : `https://${n}`;
350
- }
351
- function T(e) {
352
- let t = String(e ?? "").trim();
353
- return !t || t === "/" ? "" : `/${t.replace(/^\/+|\/+$/g, "")}`;
354
- }
355
- function E(e) {
356
- let t = String(e ?? "").trim();
357
- return t.startsWith("/") ? t : `/${t}`;
358
- }
359
- function D(e) {
360
- try {
361
- return new URL(e).hostname;
362
- } catch {
363
- return "";
364
- }
365
- }
366
- function O(e, t) {
367
- let n = Number(e);
368
- return !Number.isFinite(n) || n <= 0 ? t : (n > 86400 && (n /= 1e3), Math.min(Math.max(Math.floor(n), v), 86400));
369
- }
370
- function k(e) {
371
- if (typeof e == "string") return { token: e.trim() };
372
- let t = e ?? {}, n = typeof t.data == "object" && t.data !== null ? t.data : void 0;
373
- for (let e of [t, n]) {
374
- if (e === void 0) continue;
375
- let t = e.token ?? e.accessToken;
376
- if (typeof t == "string" && t.trim()) {
377
- let n = Number(e.expires ?? e.expiresIn ?? e.expires_at);
378
- return {
379
- token: t.trim(),
380
- ...Number.isFinite(n) && n > 0 ? { expires: n } : {}
381
- };
382
- }
383
- }
384
- return { token: "" };
385
- }
386
- async function A(t, n, r, i) {
387
- let a = new AbortController(), o = setTimeout(() => a.abort(), b);
388
- try {
389
- return await t(n, {
390
- ...r,
391
- signal: a.signal
392
- });
393
- } catch (t) {
394
- let r = a.signal.aborted;
395
- throw new e(r ? "NETWORK_TIMEOUT" : "LEGACY_PLATFORM_UNREACHABLE", "network", !0, { cause: r ? `${i}超时(${b / 1e3} 秒):POST ${n}` : `${i}失败:无法请求 ${n}(${t instanceof Error ? t.message : String(t)})` });
396
- } finally {
397
- clearTimeout(o);
398
- }
399
- }
400
- //#endregion
401
104
  //#region src/legacy/index.ts
402
- var j = "0.1.0";
105
+ var a = "0.1.0";
403
106
  typeof window < "u" && (window.CCBarSDK = r);
404
107
  //#endregion
405
- export { r as CCBarSDK, n as DeprecatedError, C as createLegacySessionProvider, s as decryptSipPassword, j as legacyVersion };
108
+ export { r as CCBarSDK, n as DeprecatedError, a as legacyVersion };
package/dist/types.d.ts CHANGED
@@ -58,11 +58,7 @@ export interface MediaManager {
58
58
  releaseAll(): Promise<void>;
59
59
  }
60
60
  export type CCBarErrorCategory = 'auth' | 'configuration' | 'network' | 'registration' | 'media' | 'call' | 'capability' | 'internal';
61
- export type CCBarErrorCode = 'AUTH_TOKEN_UNAVAILABLE' | 'AUTH_TOKEN_EXPIRED' | 'AUTH_PERMISSION_DENIED' | 'CONFIG_INVALID' | 'CONFIG_ORIGIN_NOT_ALLOWED' | 'NETWORK_OFFLINE' | 'NETWORK_TIMEOUT' | 'SIP_WS_UNAVAILABLE' | 'REGISTRATION_FAILED' | 'REGISTRATION_REJECTED' | 'MEDIA_PERMISSION_DENIED' | 'MEDIA_DEVICE_NOT_FOUND' | 'MEDIA_DEVICE_IN_USE' | 'MEDIA_PLAYBACK_BLOCKED' | 'CALL_INVALID_DESTINATION' | 'CALL_INVALID_USERDATA' | 'CALL_ALREADY_EXISTS' | 'CALL_REJECTED' | 'CALL_BUSY' | 'CALL_NOT_CONNECTED' | 'CALL_OPERATION_NOT_ALLOWED' | 'CAPABILITY_NOT_SUPPORTED' | 'H5_BACKGROUND_NOT_SUPPORTED' | 'SDK_ALREADY_DISPOSED' | 'SDK_INTERNAL_ERROR'
62
- /** 老平台接口(seat/account/get、seats/set-status)不可达:网络失败、超时或返回非 JSON */
63
- | 'LEGACY_PLATFORM_UNREACHABLE'
64
- /** 老平台接口返回了业务错误(code != 0)或缺少必需字段;平台的 code 在 error.serverCode 上 */
65
- | 'LEGACY_PLATFORM_REJECTED';
61
+ export type CCBarErrorCode = 'AUTH_TOKEN_UNAVAILABLE' | 'AUTH_TOKEN_EXPIRED' | 'AUTH_PERMISSION_DENIED' | 'CONFIG_INVALID' | 'CONFIG_ORIGIN_NOT_ALLOWED' | 'NETWORK_OFFLINE' | 'NETWORK_TIMEOUT' | 'SIP_WS_UNAVAILABLE' | 'REGISTRATION_FAILED' | 'REGISTRATION_REJECTED' | 'MEDIA_PERMISSION_DENIED' | 'MEDIA_DEVICE_NOT_FOUND' | 'MEDIA_DEVICE_IN_USE' | 'MEDIA_PLAYBACK_BLOCKED' | 'CALL_INVALID_DESTINATION' | 'CALL_INVALID_USERDATA' | 'CALL_ALREADY_EXISTS' | 'CALL_REJECTED' | 'CALL_BUSY' | 'CALL_NOT_CONNECTED' | 'CALL_OPERATION_NOT_ALLOWED' | 'CAPABILITY_NOT_SUPPORTED' | 'H5_BACKGROUND_NOT_SUPPORTED' | 'SDK_ALREADY_DISPOSED' | 'SDK_INTERNAL_ERROR';
66
62
  export interface ConnectionStateEvent {
67
63
  state: 'offline' | 'connecting' | 'connected' | 'reconnecting' | 'failed';
68
64
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "3.8.1";
1
+ export declare const SDK_VERSION = "4.0.0";
package/package.json CHANGED
@@ -1,109 +1,109 @@
1
- {
2
- "name": "@16x/webphone-sdk",
3
- "version": "3.8.1",
4
- "homepage": "https://www.npmjs.com/package/@16x/webphone-sdk",
5
- "private": false,
6
- "license": "UNLICENSED",
7
- "publishConfig": {
8
- "access": "public",
9
- "registry": "https://registry.npmjs.org"
10
- },
11
- "type": "module",
12
- "sideEffects": [
13
- "./dist/styles.css"
14
- ],
15
- "files": [
16
- "dist/index.js",
17
- "dist/index.cjs",
18
- "dist/index.umd.js",
19
- "dist/*.js",
20
- "dist/*.cjs",
21
- "dist/index.d.ts",
22
- "dist/types.d.ts",
23
- "dist/version.d.ts",
24
- "dist/errors",
25
- "dist/events",
26
- "dist/state",
27
- "dist/api",
28
- "dist/core",
29
- "dist/shared",
30
- "dist/ui",
31
- "dist/legacy",
32
- "dist/diagnostics",
33
- "dist/shared-worker.js",
34
- "dist/shared-worker.d.ts",
35
- "dist/styles.css",
36
- "README.md",
37
- "LICENSE",
38
- "CHANGELOG.md"
39
- ],
40
- "exports": {
41
- ".": {
42
- "types": "./dist/index.d.ts",
43
- "import": "./dist/index.js",
44
- "require": "./dist/index.cjs"
45
- },
46
- "./ui": {
47
- "types": "./dist/ui/index.d.ts",
48
- "import": "./dist/ui/index.js",
49
- "require": "./dist/ui/index.cjs"
50
- },
51
- "./legacy": {
52
- "types": "./dist/legacy/index.d.ts",
53
- "import": "./dist/legacy/index.js",
54
- "require": "./dist/legacy/index.cjs"
55
- },
56
- "./diagnostics": {
57
- "types": "./dist/diagnostics/index.d.ts",
58
- "import": "./dist/diagnostics/index.js",
59
- "require": "./dist/diagnostics/index.cjs"
60
- },
61
- "./shared-worker": {
62
- "types": "./dist/shared-worker.d.ts",
63
- "import": "./dist/shared-worker.js"
64
- },
65
- "./styles.css": "./dist/styles.css"
66
- },
67
- "scripts": {
68
- "build": "vite build && vite build -c vite.umd.config.ts",
69
- "typecheck": "tsc -p tsconfig.json --noEmit",
70
- "test": "npm run build && npm run docs:build && vitest run",
71
- "lint": "eslint .",
72
- "pack:check": "node scripts/check-package.mjs",
73
- "sbom": "node scripts/generate-sbom.mjs",
74
- "release:artifacts": "node scripts/generate-release-artifacts.mjs",
75
- "check:node-next": "npm run build && node scripts/check-node-next-consumer.mjs",
76
- "verify:bundle": "node scripts/check-bundle-size.mjs",
77
- "verify:memory": "node scripts/run-memory-soak.mjs",
78
- "verify:call-soak": "node scripts/run-call-soak.mjs",
79
- "verify:hour-soak": "node scripts/run-browser-call-soak.mjs",
80
- "verify:staged": "npm run verify:memory && npm run verify:call-soak",
81
- "docs:build": "node scripts/build-docs-site.mjs",
82
- "docs:check": "node scripts/check-docs-site.mjs",
83
- "docs:test": "npm run docs:build && npx vitest run tests/docs-site/static-site.test.ts tests/docs-site/navigation.test.ts tests/docs-site/check-docs-site.test.ts",
84
- "docs:release": "npm run customer:packages && npm run docs:build && node scripts/create-integration-package.mjs && npm run docs:build && npm run docs:check",
85
- "customer:packages": "node scripts/create-customer-packages.mjs",
86
- "publish:npm": "bash scripts/publish-npm.sh",
87
- "verify:release": "npm run lint && npm run typecheck && npm test && npm run build && npm run verify:bundle && npm run pack:check && npm run sbom"
88
- },
89
- "dependencies": {
90
- "jssip": "3.10.1"
91
- },
92
- "devDependencies": {
93
- "@eslint/js": "^10.0.1",
94
- "@types/react": "^19.3.0",
95
- "@types/react-dom": "^19.3.0",
96
- "@vitejs/plugin-react": "^6.1.1",
97
- "@vitejs/plugin-vue": "^6.0.9",
98
- "eslint": "^10.10.0",
99
- "happy-dom": "^20.14.5",
100
- "react": "^19.3.0",
101
- "react-dom": "^19.3.0",
102
- "typescript": "^6.0.3",
103
- "typescript-eslint": "^8.70.0",
104
- "vite": "^8.3.0",
105
- "vite-plugin-dts": "^5.1.0",
106
- "vitest": "^5.0.1",
107
- "vue": "^3.5.43"
108
- }
109
- }
1
+ {
2
+ "name": "@16x/webphone-sdk",
3
+ "version": "4.0.0",
4
+ "homepage": "https://www.npmjs.com/package/@16x/webphone-sdk",
5
+ "private": false,
6
+ "license": "UNLICENSED",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "registry": "https://registry.npmjs.org"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "./dist/styles.css"
14
+ ],
15
+ "files": [
16
+ "dist/index.js",
17
+ "dist/index.cjs",
18
+ "dist/index.umd.js",
19
+ "dist/*.js",
20
+ "dist/*.cjs",
21
+ "dist/index.d.ts",
22
+ "dist/types.d.ts",
23
+ "dist/version.d.ts",
24
+ "dist/errors",
25
+ "dist/events",
26
+ "dist/state",
27
+ "dist/api",
28
+ "dist/core",
29
+ "dist/shared",
30
+ "dist/ui",
31
+ "dist/legacy",
32
+ "dist/diagnostics",
33
+ "dist/shared-worker.js",
34
+ "dist/shared-worker.d.ts",
35
+ "dist/styles.css",
36
+ "README.md",
37
+ "LICENSE",
38
+ "CHANGELOG.md"
39
+ ],
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "import": "./dist/index.js",
44
+ "require": "./dist/index.cjs"
45
+ },
46
+ "./ui": {
47
+ "types": "./dist/ui/index.d.ts",
48
+ "import": "./dist/ui/index.js",
49
+ "require": "./dist/ui/index.cjs"
50
+ },
51
+ "./legacy": {
52
+ "types": "./dist/legacy/index.d.ts",
53
+ "import": "./dist/legacy/index.js",
54
+ "require": "./dist/legacy/index.cjs"
55
+ },
56
+ "./diagnostics": {
57
+ "types": "./dist/diagnostics/index.d.ts",
58
+ "import": "./dist/diagnostics/index.js",
59
+ "require": "./dist/diagnostics/index.cjs"
60
+ },
61
+ "./shared-worker": {
62
+ "types": "./dist/shared-worker.d.ts",
63
+ "import": "./dist/shared-worker.js"
64
+ },
65
+ "./styles.css": "./dist/styles.css"
66
+ },
67
+ "scripts": {
68
+ "build": "vite build && vite build -c vite.umd.config.ts",
69
+ "typecheck": "tsc -p tsconfig.json --noEmit",
70
+ "test": "npm run build && npm run docs:build && vitest run",
71
+ "lint": "eslint .",
72
+ "pack:check": "node scripts/check-package.mjs",
73
+ "sbom": "node scripts/generate-sbom.mjs",
74
+ "release:artifacts": "node scripts/generate-release-artifacts.mjs",
75
+ "check:node-next": "npm run build && node scripts/check-node-next-consumer.mjs",
76
+ "verify:bundle": "node scripts/check-bundle-size.mjs",
77
+ "verify:memory": "node scripts/run-memory-soak.mjs",
78
+ "verify:call-soak": "node scripts/run-call-soak.mjs",
79
+ "verify:hour-soak": "node scripts/run-browser-call-soak.mjs",
80
+ "verify:staged": "npm run verify:memory && npm run verify:call-soak",
81
+ "docs:build": "node scripts/build-docs-site.mjs",
82
+ "docs:check": "node scripts/check-docs-site.mjs",
83
+ "docs:test": "npm run docs:build && npx vitest run tests/docs-site/static-site.test.ts tests/docs-site/navigation.test.ts tests/docs-site/check-docs-site.test.ts",
84
+ "docs:release": "npm run customer:packages && npm run docs:build && node scripts/create-integration-package.mjs && npm run docs:build && npm run docs:check",
85
+ "customer:packages": "node scripts/create-customer-packages.mjs",
86
+ "publish:npm": "bash scripts/publish-npm.sh",
87
+ "verify:release": "npm run lint && npm run typecheck && npm test && npm run build && npm run verify:bundle && npm run pack:check && npm run sbom"
88
+ },
89
+ "dependencies": {
90
+ "jssip": "3.10.1"
91
+ },
92
+ "devDependencies": {
93
+ "@eslint/js": "^10.0.1",
94
+ "@types/react": "^19.3.0",
95
+ "@types/react-dom": "^19.3.0",
96
+ "@vitejs/plugin-react": "^6.1.1",
97
+ "@vitejs/plugin-vue": "^6.0.9",
98
+ "eslint": "^10.10.0",
99
+ "happy-dom": "^20.14.5",
100
+ "react": "^19.3.0",
101
+ "react-dom": "^19.3.0",
102
+ "typescript": "^6.0.3",
103
+ "typescript-eslint": "^8.70.0",
104
+ "vite": "^8.3.0",
105
+ "vite-plugin-dts": "^5.1.0",
106
+ "vitest": "^5.0.1",
107
+ "vue": "^3.5.43"
108
+ }
109
+ }
@@ -1,29 +0,0 @@
1
- /**
2
- * 老平台坐席密码的解密:`seat/account/get` 返回的 `password`(或 `registerPassword`)
3
- * 是 **AES-128-CBC / Pkcs7 的密文,Base64 编码**,需要解密才有明文 SIP 密码。
4
- *
5
- * key/iv 各 16 字节,老平台是固定值(旧版 `ccbar.js` 里同样是硬编码),可用 options 覆盖。
6
- * 在浏览器与 Node 里都能用(走 WebCrypto,无需额外依赖;需要安全上下文或 Node 18+)。
7
- */
8
- export interface DecryptSipPasswordOptions {
9
- /** AES key(16 字节,UTF-8),默认用老平台内置值 */
10
- key?: string;
11
- /** AES iv(16 字节,UTF-8),默认用老平台内置值 */
12
- iv?: string;
13
- /** 解密实现所用的 WebCrypto(测试可注入),默认全局 crypto.subtle */
14
- subtle?: SubtleCrypto;
15
- }
16
- /**
17
- * 解出 SIP 密码明文。
18
- *
19
- * ```ts
20
- * import { decryptSipPassword } from '@16x/webphone-sdk/legacy';
21
- *
22
- * const res = await fetch(`${host}/openapi/token/v1/seat/account/get`, { method: 'POST', headers: { Authorization: token }, body: '{}' });
23
- * const { data } = await res.json();
24
- * const password = await decryptSipPassword(data.password); // 明文 SIP 密码
25
- * ```
26
- *
27
- * 失败时抛 `CCBarError`:可读原因在 `error.cause` 上(密文为空 / 不是合法 Base64 / 解密失败)。
28
- */
29
- export declare function decryptSipPassword(cipherText: string, options?: DecryptSipPasswordOptions): Promise<string>;