@mrrisega/dsh-remote 0.6.7-beta.3 → 0.6.7

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.
@@ -973,8 +973,24 @@ async function resolveToken(refresh = false) {
973
973
  const d = await r.json();
974
974
  if (r.status === 200 && d.token) {
975
975
  console.log("[bridge] 登录成功,已获取 JWT");
976
+ // 登录成功 = 限流窗口已结束(服务端成功即清零),把退避提示删掉让 watcher 恢复正常节奏
977
+ try { fs.rmSync(path.join(path.dirname(CONFIG_PATH), ".dsh-login-ratelimited"), { force: true }); } catch { /* 非关键 */ }
976
978
  return d.token;
977
979
  }
980
+ if (r.status === 429) {
981
+ // 服务端登录限流(每 IP 5 次失败 / 15 分钟)。**这不是密码错**:
982
+ // 旧文案一律提示"请检查 DSH_BRIDGE_EMAIL / DSH_BRIDGE_PASSWORD",把限流误报成凭据问题,
983
+ // 用户会去反复改密码、反而把窗口拖长(2026-09-19 实测)。
984
+ const waitMs = Number(d.retry_after_ms) > 0 ? Number(d.retry_after_ms) : 0;
985
+ console.error(`[bridge] 登录被服务端限流(429)${waitMs ? `,约 ${Math.ceil(waitMs / 1000)} 秒后自动重试` : ",请稍后自动重试"}`);
986
+ console.error("[bridge] 凭据没错,是这台机器的出口 IP 短时间登录失败过多被临时限制;无需改密码。");
987
+ // 把"最早可重试时刻"落盘:watcher 据此退避,避免 10 秒一次空撞限流
988
+ try {
989
+ const until = Date.now() + (waitMs || 5 * 60 * 1000);
990
+ fs.writeFileSync(path.join(path.dirname(CONFIG_PATH), ".dsh-login-ratelimited"), String(until));
991
+ } catch { /* 非关键 */ }
992
+ process.exit(3);
993
+ }
978
994
  console.error(`[bridge] 登录失败(${r.status}): ${d.error?.message || "未知错误"}`);
979
995
  console.error("[bridge] 请检查 DSH_BRIDGE_EMAIL / DSH_BRIDGE_PASSWORD,或直接设 DSH_BRIDGE_TOKEN");
980
996
  process.exit(1);
@@ -599,6 +599,14 @@
599
599
 
600
600
  /* ---- 状态徽标 ---- */
601
601
  var _badgeCollapseTimer = null; // 自动收缩(仅 ok 态):展示几秒后缩成仅 🔒,不挡界面
602
+ var _badgeState = null; // 最近一次状态 {mode,text,note} —— 供合并后的悬浮菜单读取
603
+ /** 对外只读接口:让统一的「远程控制」悬浮按钮显示加密状态(不暴露任何密钥/会话材料)。 */
604
+ try {
605
+ window.__dshE2eeBadge = {
606
+ state: function () { return _badgeState; },
607
+ open: function () { var el = document.getElementById("dsh-e2ee-badge"); if (el && el.click) el.click(); return !!el; }
608
+ };
609
+ } catch (e) { /* ignore */ }
602
610
  function _badgeClearCollapse() {
603
611
  try { if (_badgeCollapseTimer) { clearTimeout(_badgeCollapseTimer); _badgeCollapseTimer = null; } } catch (e) {}
604
612
  }
@@ -643,8 +651,17 @@
643
651
  }
644
652
  function _badge(mode, text, note) {
645
653
  try {
646
- // 桌面宽屏(>820)明文/告警态不加徽标 —— 与 mobile-adapter 的“桌面零打扰”契约一致;
654
+ // 合并给「远程控制悬浮菜单」(2026-09-19):状态挂到 window.__dshE2eeBadge 并广播事件,
655
+ // 让 mobile-adapter 的统一悬浮按钮显示加密状态、并把独立药丸收起来(只读、不碰加解密逻辑)。
656
+ _badgeState = { mode: String(mode), text: String(text == null ? "" : text), note: String(note == null ? "" : note) };
657
+ try {
658
+ var ev = new CustomEvent("dsh-e2ee-badge", { detail: _badgeState });
659
+ document.dispatchEvent(ev);
660
+ if (window !== document) window.dispatchEvent(ev);
661
+ } catch (e0) { /* 老浏览器:忽略 */ }
662
+ // 桌面宽屏(>820)明文/告警态不加**独立**徽标 —— 与 mobile-adapter 的“桌面零打扰”契约一致;
647
663
  // 加密态(ok)或窄屏始终给出明确状态(协议 §2.4“不静默”)。
664
+ // 状态本身照常发布,合并后的悬浮菜单无论宽窄都能看到它。
648
665
  if (mode !== "ok" && window.innerWidth > 820) return;
649
666
  var el = _badgeEl();
650
667
  if (!el) return;