dsh-remote-web 0.6.18-beta.1 → 0.6.18-beta.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-web",
|
|
3
|
-
"version": "0.6.18-beta.
|
|
3
|
+
"version": "0.6.18-beta.2",
|
|
4
4
|
"description": "公网远程控制 DeepSeek Harness(dsh web):安装即得专属加密地址,人在外面也能用手机访问电脑上的 dsh——无需同一局域网/WiFi、无需公网 IP 与内网穿透,全程加密;手机端 100% 还原电脑体验(对话/工具/审批/设置)。技术用户可选自建服务,流量走自己的服务器。Remote control DeepSeek Harness (dsh web) from anywhere over the public internet — install, get an encrypted URL, use it from your phone on any network (dual-half cordis plugin: Settings panel + same-origin /dsh-remote routes). (2026-09 由 dsh-remote-ui 更名 / renamed from dsh-remote-ui; dsh-remote 的 dsh web 插件半,不是纯 UI/皮肤插件)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -710,7 +710,7 @@
|
|
|
710
710
|
/** 生成 window.WebSocket 的替换构造函数:命中策略才套 SeE2eeWs,否则原样放行。 */
|
|
711
711
|
function seMakeWsCtor(NativeWS, ctx) {
|
|
712
712
|
function SeWs(url, protocols) {
|
|
713
|
-
var full = "", should = false, scoped = String(url);
|
|
713
|
+
var full = "", should = false, scoped = String(url), passUrl = url;
|
|
714
714
|
try {
|
|
715
715
|
var base = ctx.origin || (typeof window !== "undefined" ? window.location.href : undefined);
|
|
716
716
|
var uo = new URL(url, base);
|
|
@@ -727,12 +727,19 @@
|
|
|
727
727
|
scoped = seScopeToDevice(uo.pathname + uo.search, ctx.deviceId);
|
|
728
728
|
var so = scoped === (uo.pathname + uo.search) ? uo : new URL(scoped, base);
|
|
729
729
|
full = so.pathname + so.search;
|
|
730
|
-
|
|
730
|
+
/* ★ 关键:改写结果要**留住**。原来不改写分支 return 的是原始 url,等于把
|
|
731
|
+
`/remote/<dev>/api/remote.mux` 又丢回根路径 —— 那正是 404 的来源。 */
|
|
732
|
+
passUrl = so.href;
|
|
733
|
+
/* ⚠️ 必须同时要求"有会话":URL 改写在无会话时也要做(这是 2026-09-26 事故的正题),
|
|
734
|
+
但**加密**没有会话就做不了 —— 少了这个判断,未解锁的页面上任何
|
|
735
|
+
`new WebSocket("/api/…")` 都会在 ctx.sess.sessId 上抛 TypeError,
|
|
736
|
+
把整个页面打挂(比原来"不装补丁"还糟)。 */
|
|
737
|
+
should = !!ctx.sess && seShouldEncryptWs(full);
|
|
731
738
|
}
|
|
732
739
|
} catch (e) { should = false; }
|
|
733
|
-
if (!should) return new NativeWS(
|
|
740
|
+
if (!should) return new NativeWS(passUrl, protocols);
|
|
734
741
|
var target = seBuildWsTarget(scoped, ctx.sess.sessId, seRandomHex(8), base);
|
|
735
|
-
if (!target) return new NativeWS(
|
|
742
|
+
if (!target) return new NativeWS(passUrl, protocols);
|
|
736
743
|
return new SeE2eeWs(NativeWS, target.href, protocols, { sess: ctx.sess, wsLabel: target.wsLabel });
|
|
737
744
|
}
|
|
738
745
|
SeWs.OPEN = 1; SeWs.CONNECTING = 0; SeWs.CLOSING = 2; SeWs.CLOSED = 3;
|
|
@@ -875,9 +882,28 @@
|
|
|
875
882
|
+ "一直这样请返回设备列表重新解锁(会自动重建加密通道)。");
|
|
876
883
|
}
|
|
877
884
|
|
|
878
|
-
/* ----
|
|
885
|
+
/* ---- 补丁安装:**URL 改写永远装,加密按会话开关** ----
|
|
886
|
+
*
|
|
887
|
+
* 🔴 2026-09-27 修(P0-1 收尾):原来 fetch/WS 补丁只在"有会话"时安装(`_install(sess)`),
|
|
888
|
+
* 可**这正是它最需要生效的场景**:镜像页没有会话 → 不装补丁 → 官方客户端把 mux WS 拼成根路径
|
|
889
|
+
* `/api/remote.mux` → 中继 `resolveRoute` 只认 `dsh_device` cookie,cookie 过期/缺失就 404
|
|
890
|
+
* (`rejectUpgrade` 写状态行后立刻 destroy → nginx 只看到不透明的 009)→
|
|
891
|
+
* `e2ee-hello` 永远送不到宿主机 → **会话永远建不起来** → 之后每个受保护请求都是
|
|
892
|
+
* `unknown_session` 502。**死循环**:没会话→不装补丁→建不了会话。
|
|
893
|
+
* 生产已有实锤:上线 reject 分支日志后中继立刻打出
|
|
894
|
+
* `WS 升级被拒: reason=route_unresolved status=404 path=/api/remote.mux`。
|
|
895
|
+
*
|
|
896
|
+
* 现在:`_install()` 在 boot 阶段**无条件**安装(`ctx.sess` 先为 null),会话到了再填进去。
|
|
897
|
+
* 补丁里"不改写就原样放行"的分支也会带上改写后的 URL(见 seMakeWsCtor 的 passUrl)——
|
|
898
|
+
* 否则改写算出来又被丢掉,等于没做。
|
|
899
|
+
*/
|
|
879
900
|
var _installed = false;
|
|
901
|
+
var _ctx = null;
|
|
880
902
|
function _install(sess) {
|
|
903
|
+
if (sess) {
|
|
904
|
+
// 会话后到:只更新 ctx(补丁已装好,闭包读的是同一个对象)
|
|
905
|
+
if (_ctx) { _ctx.sess = sess; return; }
|
|
906
|
+
}
|
|
881
907
|
if (_installed) return;
|
|
882
908
|
_installed = true;
|
|
883
909
|
seFailureHook = _reportFail;
|
|
@@ -886,7 +912,8 @@
|
|
|
886
912
|
/* deviceId 取自**页面路径**(`/remote/<deviceId>/…`,2026-09-19 起的标准入口):
|
|
887
913
|
WS 侧用它把根路径的 mux 改写成设备作用域形态,从而不再依赖会过期的 dsh_device cookie
|
|
888
914
|
(见 seScopeToDevice 的长注释与 2026-09-26 的 nginx 日志证据)。读不到就留空 = 不改写。 */
|
|
889
|
-
|
|
915
|
+
_ctx = { sess: sess || null, origin: window.location.origin, deviceId: seDeviceIdOfPath(window.location.pathname) };
|
|
916
|
+
var ctx = _ctx;
|
|
890
917
|
try {
|
|
891
918
|
if (window.fetch) {
|
|
892
919
|
var origFetch = window.fetch.bind(window);
|
|
@@ -906,6 +933,7 @@
|
|
|
906
933
|
function _boot() {
|
|
907
934
|
try {
|
|
908
935
|
if (!(window.crypto && window.crypto.subtle && window.crypto.getRandomValues)) {
|
|
936
|
+
_install(null);
|
|
909
937
|
_badge("warn", "未加密:环境不支持", "当前浏览器环境不支持 WebCrypto(需 HTTPS 安全上下文),镜像页数据流保持明文。");
|
|
910
938
|
return;
|
|
911
939
|
}
|
|
@@ -936,12 +964,16 @@
|
|
|
936
964
|
return;
|
|
937
965
|
}
|
|
938
966
|
}
|
|
967
|
+
/* ⚠️ 即使没有会话也必须先装补丁:WS 的设备作用域改写与加密无关(见 _install 长注释)。
|
|
968
|
+
否则这里 return 掉 → mux 打根路径 → 404 → 会话永远建不起来(死循环)。 */
|
|
969
|
+
_install(null);
|
|
939
970
|
_badge("warn", "未加密:未解锁(明文)", "本次为明文连接。密钥只存内存;请返回设备列表重新点选设备解锁后,镜像页的数据请求与 WebSocket 将自动加密。");
|
|
940
971
|
return;
|
|
941
972
|
}
|
|
942
973
|
var sess = null;
|
|
943
974
|
try { sess = seSessionOfHandover(raw); } catch (e) { sess = null; }
|
|
944
975
|
if (!sess) {
|
|
976
|
+
_install(null);
|
|
945
977
|
_badge("warn", "未加密:交接单无效", "一次性交接单无法解析(可能已损坏或过期)。请返回设备列表重新解锁。");
|
|
946
978
|
return;
|
|
947
979
|
}
|