mitra-interactions-sdk 1.0.60-beta.27 → 1.0.60-beta.29

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/index.js CHANGED
@@ -279,7 +279,15 @@ async function refreshTokenSilently(authUrl, _projectId) {
279
279
  if (!refreshToken) {
280
280
  throw new Error("refreshToken n\xE3o dispon\xEDvel para renova\xE7\xE3o. Fa\xE7a login novamente.");
281
281
  }
282
+ if (!hasAppScope(refreshToken)) {
283
+ clearRefreshToken();
284
+ throw new Error("refreshToken sem escopo de app (sess\xE3o do console) \u2014 descartado. Fa\xE7a loginMitra({ projectId }) novamente.");
285
+ }
282
286
  const data = await postBffAuth(buildBffAuthEndpoint(authUrl, "/refresh-token"), { refreshToken });
287
+ if (!hasAppScope(data.token)) {
288
+ clearRefreshToken();
289
+ throw new Error("refresh retornou token sem escopo de app \u2014 rejeitado. Fa\xE7a loginMitra({ projectId }) novamente.");
290
+ }
283
291
  return {
284
292
  token: data.token,
285
293
  // Refresh não muda a base — mantém o authUrl (gateway).
@@ -346,20 +354,23 @@ async function handleResponse(response) {
346
354
  }
347
355
  return data;
348
356
  }
357
+ function withFreshAuth(init) {
358
+ const config = getConfig();
359
+ const headers = { ...init.headers };
360
+ if (config.token) headers["Authorization"] = formatToken(config.token);
361
+ return { ...init, headers };
362
+ }
349
363
  async function fetchWithRefresh(url, init) {
350
364
  const fetchFn = getFetch2();
365
+ if (isTokenExpiring()) {
366
+ const refreshed = await tryRefreshToken();
367
+ if (refreshed) init = withFreshAuth(init);
368
+ }
351
369
  const response = await fetchFn(url, init);
352
370
  if (response.status === 401) {
353
371
  const refreshed = await tryRefreshToken();
354
372
  if (refreshed) {
355
- const newHeaders = { ...buildHeaders() };
356
- const oldHeaders = init.headers;
357
- if (oldHeaders) {
358
- for (const [k, v] of Object.entries(oldHeaders)) {
359
- if (k !== "Authorization") newHeaders[k] = v;
360
- }
361
- }
362
- const retryResponse = await fetchFn(url, { ...init, headers: newHeaders });
373
+ const retryResponse = await fetchFn(url, withFreshAuth(init));
363
374
  return handleResponse(retryResponse);
364
375
  }
365
376
  }
@@ -650,8 +661,25 @@ var AgentTaskSession = class {
650
661
  if (!this._taskId) {
651
662
  return Promise.reject(new Error("AgentTaskSession: sem taskId \u2014 task ainda n\xE3o criada."));
652
663
  }
653
- const url = getTaskWsUrl(this._taskId);
654
- this._wsConnectPromise = new Promise((resolve, reject) => {
664
+ this._wsConnectPromise = (async () => {
665
+ try {
666
+ if (isTokenExpiring()) await tryRefreshToken();
667
+ try {
668
+ return await this._connectWs(this._taskId);
669
+ } catch (err) {
670
+ const refreshed = await tryRefreshToken();
671
+ if (!refreshed) throw err;
672
+ return await this._connectWs(this._taskId);
673
+ }
674
+ } finally {
675
+ this._wsConnectPromise = null;
676
+ }
677
+ })();
678
+ return this._wsConnectPromise;
679
+ }
680
+ _connectWs(taskId) {
681
+ const url = getTaskWsUrl(taskId);
682
+ return new Promise((resolve, reject) => {
655
683
  const socket = new WebSocket(url);
656
684
  let opened = false;
657
685
  const timer = setTimeout(() => {
@@ -660,7 +688,6 @@ var AgentTaskSession = class {
660
688
  socket.close();
661
689
  } catch (e) {
662
690
  }
663
- this._wsConnectPromise = null;
664
691
  reject(new Error("Agent Chat: timeout ao conectar no WebSocket da task"));
665
692
  }
666
693
  }, 15e3);
@@ -668,13 +695,11 @@ var AgentTaskSession = class {
668
695
  opened = true;
669
696
  clearTimeout(timer);
670
697
  this._ws = socket;
671
- this._wsConnectPromise = null;
672
698
  resolve(socket);
673
699
  };
674
700
  socket.onerror = () => {
675
701
  if (!opened) {
676
702
  clearTimeout(timer);
677
- this._wsConnectPromise = null;
678
703
  reject(new Error("Agent Chat: erro ao conectar no WebSocket da task"));
679
704
  }
680
705
  };
@@ -691,7 +716,6 @@ var AgentTaskSession = class {
691
716
  this._handleWsEvent(msg);
692
717
  };
693
718
  });
694
- return this._wsConnectPromise;
695
719
  }
696
720
  // ── Eventos do stream (WsOutboundMessage { type, payload, timestamp }) ──
697
721
  _handleWsEvent(msg) {
@@ -810,8 +834,12 @@ function getGatewayOrigin() {
810
834
  return new URL(config.baseURL).origin;
811
835
  }
812
836
  function getTaskWsUrl(taskId) {
837
+ const token = getRawToken();
838
+ if (!hasAppScope(token)) {
839
+ throw new Error("Agent Chat: o token configurado \xE9 uma sess\xE3o do console (sem app_id) \u2014 o WebSocket do copilot exige token de app. Fa\xE7a loginMitra({ projectId }).");
840
+ }
813
841
  const origin = getGatewayOrigin().replace(/^http/, "ws");
814
- return `${origin}/copilot/ws/tasks/${encodeURIComponent(taskId)}?token=${encodeURIComponent(getRawToken())}`;
842
+ return `${origin}/copilot/ws/tasks/${encodeURIComponent(taskId)}?token=${encodeURIComponent(token)}`;
815
843
  }
816
844
  function getRawToken() {
817
845
  const config = getConfig();
@@ -1051,20 +1079,22 @@ function createMitraInstance(initialConfig) {
1051
1079
  return false;
1052
1080
  }
1053
1081
  }
1082
+ function withFreshAuth2(init) {
1083
+ const headers = { ...init.headers };
1084
+ if (_config.token) headers["Authorization"] = formatToken2(_config.token);
1085
+ return { ...init, headers };
1086
+ }
1054
1087
  async function fetchWithRefresh2(url, init) {
1055
1088
  const fetchFn = getFetch3();
1089
+ if (isTokenExpiring(_config.token)) {
1090
+ const refreshed = await tryRefreshToken2();
1091
+ if (refreshed) init = withFreshAuth2(init);
1092
+ }
1056
1093
  const response = await fetchFn(url, init);
1057
1094
  if (response.status === 401) {
1058
1095
  const refreshed = await tryRefreshToken2();
1059
1096
  if (refreshed) {
1060
- const newHeaders = { ...authHeaders() };
1061
- const oldHeaders = init.headers;
1062
- if (oldHeaders) {
1063
- for (const [k, v] of Object.entries(oldHeaders)) {
1064
- if (k !== "Authorization") newHeaders[k] = v;
1065
- }
1066
- }
1067
- const retryResponse = await fetchFn(url, { ...init, headers: newHeaders });
1097
+ const retryResponse = await fetchFn(url, withFreshAuth2(init));
1068
1098
  return handleResponse2(retryResponse);
1069
1099
  }
1070
1100
  }
@@ -1251,6 +1281,50 @@ function persistRefreshToken(refreshToken) {
1251
1281
  } catch (e) {
1252
1282
  }
1253
1283
  }
1284
+ function removePersistedRefreshToken() {
1285
+ const ls = safeLocalStorage();
1286
+ if (!ls) return;
1287
+ try {
1288
+ const raw = ls.getItem(SESSION_STORE_KEY);
1289
+ if (!raw) return;
1290
+ const obj = JSON.parse(raw);
1291
+ delete obj.refreshToken;
1292
+ ls.setItem(SESSION_STORE_KEY, JSON.stringify(obj));
1293
+ } catch (e) {
1294
+ }
1295
+ }
1296
+ function decodeJwtPayload(token) {
1297
+ try {
1298
+ const part = stripBearer(token).split(".")[1];
1299
+ if (!part) return null;
1300
+ const b64 = part.replace(/-/g, "+").replace(/_/g, "/");
1301
+ const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
1302
+ const json = typeof atob === "function" ? atob(padded) : Buffer.from(padded, "base64").toString("utf8");
1303
+ return JSON.parse(json);
1304
+ } catch (e) {
1305
+ return null;
1306
+ }
1307
+ }
1308
+ function hasAppScope(token) {
1309
+ const payload = decodeJwtPayload(token);
1310
+ if (!payload) return true;
1311
+ return typeof payload.app_id === "string" && payload.app_id.length > 0;
1312
+ }
1313
+ function isTokenExpiring(token, skewMs = 3e4) {
1314
+ const t = token != null ? token : globalConfig == null ? void 0 : globalConfig.token;
1315
+ if (!t) return false;
1316
+ const payload = decodeJwtPayload(t);
1317
+ const exp = payload == null ? void 0 : payload.exp;
1318
+ if (typeof exp !== "number") return false;
1319
+ return exp * 1e3 - Date.now() < skewMs;
1320
+ }
1321
+ function clearRefreshToken() {
1322
+ if (globalConfig) {
1323
+ const { refreshToken: _drop, ...rest } = globalConfig;
1324
+ globalConfig = rest;
1325
+ }
1326
+ removePersistedRefreshToken();
1327
+ }
1254
1328
  function readPersistedRefreshToken() {
1255
1329
  const ls = safeLocalStorage();
1256
1330
  if (!ls) return void 0;
@@ -1283,7 +1357,16 @@ function configureSdkMitra(config = {}) {
1283
1357
  if (merged.token != null) merged.token = stripBearer(merged.token);
1284
1358
  if (!merged.refreshToken) {
1285
1359
  const persisted = readPersistedRefreshToken();
1286
- if (persisted) merged.refreshToken = persisted;
1360
+ if (persisted) {
1361
+ if (hasAppScope(persisted)) {
1362
+ merged.refreshToken = persisted;
1363
+ } else {
1364
+ removePersistedRefreshToken();
1365
+ }
1366
+ }
1367
+ } else if (!hasAppScope(merged.refreshToken)) {
1368
+ delete merged.refreshToken;
1369
+ removePersistedRefreshToken();
1287
1370
  } else {
1288
1371
  persistRefreshToken(merged.refreshToken);
1289
1372
  }