dsh-deepseek-web-login 0.6.14 → 0.6.15

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  本项目遵循大致语义化版本;日期为本地时间。
4
4
 
5
+ ## 0.6.15 — 2026-10-01
6
+
7
+ **界面两处修正(用户反馈):「一键重登」搬到账号库;删掉一堆误导性的"注释"。**
8
+
9
+ 用户原话:「首先我没有看到『一键重登』的按钮,其次有很多注释完全不需要,什么 cookie 剩余多少天
10
+ 过期,完全没必要显示,不照样还是几个小时就需要重登嘛!这些注释写的真多余,导致界面不美观。」
11
+
12
+ 1. **「一键重登」位置错了**:0.6.14 把它 append 到了 `gateCard`(防风控页的设置卡),
13
+ 而用户是在「账号库」子标签里找 —— 自然找不到。现在挪到**账号库的按钮行**,紧跟
14
+ 「登录新账号(添加)」(两者是同一类动作:弄到可用凭证),按钮文案统一成「一键重登」,
15
+ 结果走**全局提示条**(常驻标签栏之上,切子标签也看得见)。
16
+ 2. **删掉 cookie 的"还剩多少天"**:原来报的是持久级 cookie 的到期日(实测
17
+ `thumbcache_*` 还剩 399 天),而真正鉴权的 token 只有 **87~370 分钟**寿命 ——
18
+ 两个数量级摆在一起只会让人读成"这号还能用 399 天",然后发现几小时就得重登。
19
+ `describeCookieLife` 现在只报构成(`5 项 · 3 会话级 · 2 持久级`),名字与天数都不再出现。
20
+ 3. **合并/精简状态行**:「Cookie 过期」并进「Cookie」一行;「凭证来源」去掉长括号;
21
+ 「指纹头」/「Cookie 未捕获」的长解释改成短句;删掉信息量为零的「PoW WASM」行。
22
+ 4. **账号行元信息瘦身**:从
23
+ `davi***+w5@gmail.com · 9/30 17:34 捕获 · 最近校验 21 小时前 · cookie:5 项 · 3 会话级 · 2 持久级 · .thumbcache_… 还剩 399 天`
24
+ 缩到 `9/30 17:34 捕获 · 最近校验 21 小时前`(账号名标题里已经有了,不再重复)。
25
+
26
+ 用例:`check-cookie-meta` 改成守新意图(**并且新增一条"不许再出现天数/cookie 名"的牙齿**);
27
+ `check-ui-copy` 新增两条点名守卫 ——「一键重登必须在账号库按钮行里」与「cookie 摘要不许出现天数」,
28
+ 两条都做过反向验证(把按钮挪回去 / 把天数加回来 ⇒ 立刻变红)。
29
+
5
30
  ## 0.6.14 — 2026-09-30
6
31
 
7
32
  **邮箱密码自动重登:「重登」一键点下去就上,另加「一键重登全部」+ 到期前自动续(开关,默认关)。**
package/lib/client.js CHANGED
@@ -148,53 +148,18 @@ window.__ModuleLoader__.load({
148
148
  //#endregion
149
149
  //#region src/cookies.ts
150
150
  /**
151
- * 汇总结论。**没有记录时返回 `undefined`**(而不是全 0 的对象)——
152
- * 调用方要区分"没采到过期信息"(老记录 / 手动粘 token)和"采到了、全是会话级",
153
- * 这两种情况的界面文案完全不同。
154
- */
155
- function summarizeCookieLife(metas, now = Date.now()) {
156
- const list = metas ?? [];
157
- if (list.length === 0) return void 0;
158
- const persistent = list.filter((item) => !item.session && Number.isFinite(item.expiresAt));
159
- let latest;
160
- for (const item of persistent) {
161
- const expiresAt = item.expiresAt;
162
- if (!latest || expiresAt > latest.expiresAt) latest = {
163
- name: item.name,
164
- expiresAt,
165
- daysLeft: (expiresAt - now) / 864e5
166
- };
167
- }
168
- return {
169
- total: list.length,
170
- sessionCount: list.length - persistent.length,
171
- persistentCount: persistent.length,
172
- ...latest ? { latest } : {}
173
- };
174
- }
175
- /** 剩余时间的口语化写法。 */
176
- function describeRemaining(daysLeft) {
177
- if (daysLeft <= 0) return "已过期";
178
- if (daysLeft >= 1) return `还剩 ${Math.floor(daysLeft)} 天`;
179
- return `还剩 ${Math.max(1, Math.round(daysLeft * 24))} 小时`;
180
- }
181
- /**
182
151
  * 一句话说清 cookie 的寿命构成(界面直接展示)。
183
152
  *
184
- * 三种情况分开写,因为它们的含义完全不同:
185
- * - 没记录 → 老记录 / 手动粘 token,下次重新登录会补上;
186
- * - 全是会话级 → 浏览器侧本来就没有到期时间,看它撑多久没意义;
187
- * - 有持久级 → 给出最晚那个的剩余时间(**只是浏览器侧的上界,不是登录态寿命**)。
153
+ * ⚠️ **刻意不报"还剩多少天"**(2026-10-01 由用户反馈删掉):那个数字来自持久级 cookie
154
+ * (实测 `thumbcache_*` 这类还剩 399 天),而**真正鉴权的是 token,寿命只有几小时** ——
155
+ * 两个数量级放在一起只会让人读成"这号还能用 399 天",然后发现几小时就得重登,反而更困惑。
156
+ * 现在只报构成(共几项 / 会话级几个 / 持久级几个),需要更细的信息去看 `/status` 原始字段。
188
157
  */
189
- function describeCookieLife(summary, now = Date.now()) {
190
- if (!summary) return "⚠️ 未记录(重新登录后会补上)";
158
+ function describeCookieLife(summary, _now = Date.now()) {
159
+ if (!summary) return "⚠️ 未记录";
191
160
  const parts = [`${summary.total} 项`];
192
- parts.push(summary.sessionCount > 0 ? `${summary.sessionCount} 会话级` : "无会话级");
161
+ if (summary.sessionCount > 0) parts.push(`${summary.sessionCount} 会话级`);
193
162
  if (summary.persistentCount > 0) parts.push(`${summary.persistentCount} 持久级`);
194
- if (summary.latest) {
195
- const daysLeft = (summary.latest.expiresAt - now) / 864e5;
196
- parts.push(`${summary.latest.name} ${describeRemaining(daysLeft)}`);
197
- }
198
163
  return parts.join(" · ");
199
164
  }
200
165
  //#endregion
@@ -814,10 +779,30 @@ background:var(--bg2);white-space:pre-wrap;font-size:12px}
814
779
  const importBtn = el("button", "dsw-btn ghost", "导入备份…");
815
780
  const refreshAccountsBtn = el("button", "dsw-btn ghost", "校验全部");
816
781
  refreshAccountsBtn.title = "对每个账号做一次只读校验(零额度):刷新登录态、补上账号名、清掉已恢复的失败标记";
782
+ const reloginAllBtn = el("button", "dsw-btn", "一键重登");
783
+ reloginAllBtn.title = "用本机保存的邮箱密码,把所有匹配得到的账号依次自动重登。每个号会起一次无头浏览器(无窗口、跑完即杀),所以是串行的、需要等一会儿。";
817
784
  const newGroupBtn = el("button", "dsw-btn ghost", "新建分组");
818
785
  newGroupBtn.title = "给账号分类,只影响列表的显示方式 —— 不参与切号、也不影响会话复用与清理";
819
- accountsIOPanel.append(addAccountBtn, exportBtn, importBtn, refreshAccountsBtn, newGroupBtn);
786
+ accountsIOPanel.append(addAccountBtn, reloginAllBtn, exportBtn, importBtn, refreshAccountsBtn, newGroupBtn);
820
787
  accountsCard.append(accountsIOPanel);
788
+ reloginAllBtn.addEventListener("click", () => {
789
+ reloginAllBtn.disabled = true;
790
+ showMessage("正在自动重登(每个号起一次无头浏览器,请稍候)…");
791
+ api("/login/relogin-all", {
792
+ method: "POST",
793
+ body: "{}"
794
+ }).then((result) => {
795
+ const rows = Array.isArray(result?.results) ? result.results : [];
796
+ if (rows.length === 0) {
797
+ showMessage(result?.error ?? "没有可重登的账号", "err");
798
+ return;
799
+ }
800
+ const failed = rows.filter((r) => !r.ok);
801
+ showMessage(`重登完成 ${result?.okCount ?? 0}/${rows.length}` + (failed.length ? `;失败:${failed.map((r) => `${r.display}(${String(r.message ?? "").slice(0, 40)})`).join(" ")}` : `:${rows.map((r) => r.display).join(" ")}`), failed.length ? "" : "ok");
802
+ }).catch((error) => showMessage(`重登失败:${error?.message ?? error}`, "err")).finally(() => {
803
+ reloginAllBtn.disabled = false;
804
+ });
805
+ });
821
806
  accountsCard.append(el("p", "dsw-hint", "💡 「登录新账号」会先清掉上次的浏览器登录态(库里已有的账号不受影响),登录后新账号只入库、不切换当前账号 ——加完在列表里点「切换」即可使用。某个账号标着「需要重新登录」时,用它自己那行上的按钮修 ——那条路不清浏览器登录态,能复用就直接复用。导出/导入会弹系统对话框,自己选位置和文件。"));
822
807
  const accountsMsg = createMsgNode();
823
808
  accountsCard.append(accountsMsg.node);
@@ -974,11 +959,8 @@ background:var(--bg2);white-space:pre-wrap;font-size:12px}
974
959
  else if (item.lastCheckError) title.append(el("span", "dsw-badge off", "⚠️ 未能校验(网络)"));
975
960
  main.append(title);
976
961
  const meta = [];
977
- if (item.display) meta.push(item.display);
978
962
  if (item.capturedAt) meta.push(`${shortTime(item.capturedAt)} 捕获`);
979
963
  meta.push(item.lastVerifiedAt ? `最近校验 ${relTime(item.lastVerifiedAt)}` : "尚未校验");
980
- const cookieMeta = Array.isArray(item.cookieMeta) ? item.cookieMeta : [];
981
- meta.push(`cookie:${describeCookieLife(summarizeCookieLife(cookieMeta))}`);
982
964
  main.append(el("div", "dsw-account-meta", meta.join(" · ")));
983
965
  if (item.lastVerifyError) main.append(el("div", "dsw-account-fix", `⚠️ ${relTime(item.lastVerifyError.at)}校验失败(登录态已失效):${item.lastVerifyError.message}`));
984
966
  else if (item.lastCheckError) main.append(el("div", "dsw-account-fix", `⚠️ ${relTime(item.lastCheckError.at)}未能校验(网络问题,账号未必失效):${item.lastCheckError.message}`));
@@ -1478,12 +1460,10 @@ background:var(--bg2);white-space:pre-wrap;font-size:12px}
1478
1460
  if (loggedIn) {
1479
1461
  rows.push(["账号", status.auth.display || "(未获取到账号信息)"]);
1480
1462
  rows.push(["捕获时间", status.auth.capturedAt ? new Date(status.auth.capturedAt).toLocaleString() : "未知"]);
1481
- if (!status.auth.hasCookie && !status.auth.hasFingerprint) rows.push(["凭证来源", "手动粘贴 token(实测:仅凭 Bearer token 即可完成校验/求解/生成)"]);
1482
- else rows.push(["凭证来源", "浏览器登录捕获(token + cookie + 指纹头,登录态通常更耐久)"]);
1483
- rows.push(["Cookie", status.auth.hasCookie ? "✅ 已捕获" : "未捕获 —— 手动 token 模式本就没有(已验证不影响请求;若日后频繁遇到 AUTH/40003,改用「浏览器登录」)"]);
1484
- if (status.auth.hasCookie) rows.push(["Cookie 过期", `${describeCookieLife(status.auth.cookieLife)}(会话级 = 浏览器关掉就没了;这是浏览器侧的上界,不是登录态寿命)`]);
1485
- rows.push(["指纹头", status.auth.hasFingerprint ? "✅ 已捕获(x-hif-* / x-client-*)" : "未捕获 —— 同上,已实测可用"]);
1486
- rows.push(["PoW WASM", status.auth.wasmHost || "默认地址"]);
1463
+ if (!status.auth.hasCookie && !status.auth.hasFingerprint) rows.push(["凭证来源", "手动粘贴 token"]);
1464
+ else rows.push(["凭证来源", "浏览器登录捕获"]);
1465
+ rows.push(["Cookie", status.auth.hasCookie ? `✅ 已捕获${status.auth.cookieLife ? ` · ${describeCookieLife(status.auth.cookieLife)}` : ""}` : "未捕获(手动 token 模式本就没有;不影响请求)"]);
1466
+ rows.push(["指纹头", status.auth.hasFingerprint ? "✅ 已捕获" : "未捕获(不影响请求)"]);
1487
1467
  rows.push(["token 长度", `${status.auth.tokenLength ?? 0} 字符`]);
1488
1468
  if (status.validation) rows.push(["服务端校验", status.validation.ok ? "通过" : `失败:${status.validation.error ?? ""}`]);
1489
1469
  const verifiedAt = status.auth?.lastVerifiedAt;
@@ -1781,27 +1761,6 @@ background:var(--bg2);white-space:pre-wrap;font-size:12px}
1781
1761
  });
1782
1762
  cleanupNowRow.append(cleanupNowBtn, cleanupNowHint);
1783
1763
  gateCard.append(cleanupNowRow);
1784
- const reloginAllRow = el("div", "dsw-gate-row");
1785
- reloginAllRow.append(el("span", "dsw-gate-label", "一键重登"));
1786
- const reloginAllBtn = el("button", "dsw-btn ghost", "用存的密码重登全部");
1787
- const reloginAllHint = el("span", "dsw-hint", "");
1788
- reloginAllBtn.addEventListener("click", () => {
1789
- reloginAllBtn.disabled = true;
1790
- reloginAllHint.textContent = "正在自动重登(每个号起一次无头浏览器,请稍候)…";
1791
- api("/login/relogin-all", {
1792
- method: "POST",
1793
- body: "{}"
1794
- }).then((result) => {
1795
- const rows = Array.isArray(result?.results) ? result.results : [];
1796
- reloginAllHint.textContent = rows.length ? `完成 ${result?.okCount ?? 0}/${rows.length}:` + rows.map((r) => `${r.ok ? "✅" : "❌"}${String(r.display ?? "").slice(0, 20)}`).join(" ") : result?.error ?? "没有可重登的账号";
1797
- }).catch((error) => {
1798
- reloginAllHint.textContent = `重登失败:${error?.message ?? error}`;
1799
- }).finally(() => {
1800
- reloginAllBtn.disabled = false;
1801
- });
1802
- });
1803
- reloginAllRow.append(reloginAllBtn, reloginAllHint);
1804
- gateCard.append(reloginAllRow);
1805
1764
  const autoReloginRow = el("div", "dsw-gate-row");
1806
1765
  const autoReloginLabel = el("label", "dsw-switch");
1807
1766
  const autoReloginInput = el("input");