dsh-bridge-gateway 0.2.1 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +8 -0
- package/README.zh.md +17 -0
- package/lib/feishu/lark-bundled.mjs +23 -4
- package/lib/index.js +80 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
本项目 `dsh-bridge-gateway` 是基于 [dsh-bridge](https://github.com/wenbin-wb/dsh-bridge) 的移植增强分支,在保留原版能力之外,重点新增「公网直连网关」。
|
|
4
4
|
|
|
5
|
+
## [0.2.2] - 2026-10-11
|
|
6
|
+
|
|
7
|
+
### 修复:旧内核浏览器打开后工作区永远加载不了
|
|
8
|
+
|
|
9
|
+
远程浏览器(统信 UOS、360 浏览器,内核均为 Chromium 108)访问时外壳能渲染出来,但工作区
|
|
10
|
+
一直停在「选择一个工作区开始」;DevTools 报 `Promise.withResolvers is not a function` 与
|
|
11
|
+
`AbortSignal.any is not a function`,并伴随 `connection lost, retry #N` 无限重连。
|
|
12
|
+
|
|
13
|
+
根因是 DSH `0.2.0-rc.2` 的浏览器端直接使用了三个较新的 Web API,旧内核上没有:
|
|
14
|
+
|
|
15
|
+
| API | 要求版本 | 调用点 |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `Promise.withResolvers` | Chrome/Edge ≥ 119、Firefox ≥ 121、Safari ≥ 17.4 | index.html 末尾的启动门 `(globalThis.__DSH_BOOT_READY__ ??= Promise.withResolvers())`;`dsh-api-workspace-files` 客户端的工作区文件流 |
|
|
18
|
+
| `AbortSignal.any` | Chrome/Edge ≥ 116、Firefox ≥ 124、Safari ≥ 17.4 | `dsh-api-gateway` 的 `RemoteStream.read`、`dsh-api-session-controller`、`dsh-client-ui-workspace`、`dsh-client-ui-sidebar-files` 等 |
|
|
19
|
+
| `Array.prototype.toSorted` | Chrome/Edge ≥ 110 | `dsh-client-ui-model-selection` / `-schedule` / `-settings-models` |
|
|
20
|
+
|
|
21
|
+
缺前两个的后果是启动门永不 resolve、远程流层直接抛错 —— 于是插件全部注册成功,工作区却
|
|
22
|
+
永远加载不出来。
|
|
23
|
+
|
|
24
|
+
修复走官方 `webserver/index-inject` 机制(与既有的 `crypto.randomUUID` polyfill 同一处),
|
|
25
|
+
向 index.html 的 `<head>` 注入一段兜底脚本;该位置早于 `<body>` 末尾的启动门,所以一定先执行。
|
|
26
|
+
三个 polyfill 全部带 `typeof` 守卫且幂等,现代浏览器上原样跳过、**绝不覆盖原生实现**;
|
|
27
|
+
`AbortSignal.any` 对齐原生语义:空数组不 abort、已 abort 的成员立即短路并沿用其 reason、
|
|
28
|
+
组合信号 abort 后摘掉监听器(避免在 `lifetime` / `generation` 这类长命 signal 上堆积)。
|
|
29
|
+
|
|
30
|
+
新增 `test/legacy-compat.test.mjs` 第 3 节共 10 条断言,在 `node:vm` 沙箱里剥离这三个 API 后
|
|
31
|
+
验证注入脚本本身,其中两条是反向断言:原生齐全时三个函数引用必须原封不动、重复执行必须是 no-op。
|
|
32
|
+
|
|
33
|
+
> 附带说明:同一现场出现的 `404` / `net::ERR_HTTP_RESPONSE_CODE_FAILURE` 与本次故障无关 ——
|
|
34
|
+
> 那是 DevTools 打开时去取不存在的 source map(`/assets/index-*.js.map`)以及
|
|
35
|
+
> `/api/changes.summary`(deliverables 的 RPC)返回 404;工作区在零 4xx 的情况下照样加载失败。
|
|
36
|
+
|
|
5
37
|
## [0.2.1] - 2026-09-25
|
|
6
38
|
|
|
7
39
|
### 兼容性核查:DSH 0.1.7 无需改动
|
package/README.md
CHANGED
|
@@ -61,6 +61,12 @@ Upstream iterates quickly and this repository keeps up. Ported fixes are listed
|
|
|
61
61
|
|
|
62
62
|
> Both sides evolve, so **feature coverage may lead on either side**. When in doubt, check upstream's README to see which side owns a given capability.
|
|
63
63
|
|
|
64
|
+
**5. Fixed: old-kernel browsers could not load the workspace (still present upstream)**
|
|
65
|
+
|
|
66
|
+
DSH's browser client calls `Promise.withResolvers` (Chrome/Edge ≥ 119) and `AbortSignal.any` (Chrome/Edge ≥ 116) directly. On an older kernel — UOS and 360 Browser, both Chromium 108 — the boot gate never resolves and the remote-stream layer throws, so the shell renders but the workspace stays stuck on "pick a workspace" while the console loops on `connection lost`.
|
|
67
|
+
|
|
68
|
+
This repository injects a small shim through the official `webserver/index-inject` mechanism (the same place as the existing `crypto.randomUUID` polyfill), covering `Promise.withResolvers`, `AbortSignal.any` and `Array.prototype.toSorted`. Every shim is `typeof`-guarded and idempotent, so a modern browser skips it untouched and never loses its native implementation.
|
|
69
|
+
|
|
64
70
|
### Naming
|
|
65
71
|
|
|
66
72
|
The plugin was renamed from `dsh-bridge` to **`dsh-bridge-gateway`** (reflecting the added gateway capability). Existing users need no manual work — the data directory, configuration, passwords, certificates, and login state all migrate automatically:
|
|
@@ -78,6 +84,7 @@ The plugin was renamed from `dsh-bridge` to **`dsh-bridge-gateway`** (reflecting
|
|
|
78
84
|
- **LAN access.** Scan a QR code from the remote-access panel on the same Wi-Fi and you are in.
|
|
79
85
|
- **Public tunnels.** Cloudflare (temporary or fixed hostname) or a self-built tunnel, selectable per need alongside the direct gateway and Tailscale.
|
|
80
86
|
- **Remote workspace.** Browse the host's directory tree and pick workspace conversations from the phone (via DSH's official browse picker).
|
|
87
|
+
- **Legacy browser support.** The gateway injects `Promise.withResolvers` / `AbortSignal.any` / `Array.prototype.toSorted` polyfills into the page, so Chromium 108-class kernels (UOS, 360 Browser) load the workspace too. Modern browsers skip them untouched.
|
|
81
88
|
- **Security.** Password gate + QR-based password-free token + admin unlock/lock for sensitive config + rate limiting against brute force, plus persisted login state (no re-login after a host restart). Client-claimed `isLocalhost` is never trusted.
|
|
82
89
|
- **Visitor management.** See live who is currently connected (LAN / direct gateway / tunnel, with the real source IP behind a tunnel) plus an IP blacklist. **Viewing is open to every visitor**; disconnecting and blacklisting require admin rights.
|
|
83
90
|
- **IM bots.** WeChat / QQ / Feishu / Telegram bots connect through their own gateway links — no public IP needed. Per-platform workspace / agent preset / model selection is configurable. Guides live in [docs/](./docs).
|
|
@@ -101,6 +108,7 @@ The panel has 5 tabs: **Access** / **Visitors** / **IM Bots** / **Security** / *
|
|
|
101
108
|
|
|
102
109
|
- Node `^22.19.0 || >=24.0.0`.
|
|
103
110
|
- DSH with a web profile (built and tested against `0.1.7-rc.2`, and still compatible with the `0.1.5` line: the host services, client slots and settings entry points it uses exist in both, so no extra adaptation is needed).
|
|
111
|
+
- A modern browser for the web UI: Chrome/Edge ≥ 119, Safari ≥ 17.4 or Firefox ≥ 121. Older kernels are kept working by the injected polyfills described above (Chromium 108-class UOS / 360 Browser included).
|
|
104
112
|
|
|
105
113
|
## Install
|
|
106
114
|
|
package/README.zh.md
CHANGED
|
@@ -77,6 +77,18 @@ DeepSeek Harness(DSH)远程访问插件。以 [wenbin-wb/dsh-bridge](https:/
|
|
|
77
77
|
> 由于两边都在演进,**功能上可能各有先后**。遇到问题时建议对照上游 README 确认某项能力
|
|
78
78
|
> 归属哪一侧。
|
|
79
79
|
|
|
80
|
+
**五、修正:旧内核浏览器打开后工作区永远加载不了(上游至今保留)**
|
|
81
|
+
|
|
82
|
+
DSH 的浏览器端直接调用了 `Promise.withResolvers`(Chrome/Edge ≥ 119)与 `AbortSignal.any`
|
|
83
|
+
(Chrome/Edge ≥ 116)。旧内核上(统信 UOS、360 浏览器,内核均为 Chromium 108)启动门永不
|
|
84
|
+
resolve、远程流层直接抛错 —— 于是外壳渲染得出来,工作区却一直停在「选择一个工作区」,
|
|
85
|
+
控制台刷 `connection lost` 重连。
|
|
86
|
+
|
|
87
|
+
本仓库经官方 `webserver/index-inject` 机制(与既有的 `crypto.randomUUID` polyfill 同一处)
|
|
88
|
+
注入一段兜底脚本,覆盖 `Promise.withResolvers`、`AbortSignal.any` 与
|
|
89
|
+
`Array.prototype.toSorted`。每段都带 `typeof` 守卫且幂等,现代浏览器上原样跳过,
|
|
90
|
+
**绝不覆盖原生实现**。
|
|
91
|
+
|
|
80
92
|
### 命名说明
|
|
81
93
|
|
|
82
94
|
插件名由 `dsh-bridge` 改为 **`dsh-bridge-gateway`**(体现「网关」这一新增能力)。
|
|
@@ -99,6 +111,9 @@ DeepSeek Harness(DSH)远程访问插件。以 [wenbin-wb/dsh-bridge](https:/
|
|
|
99
111
|
- **局域网访问。** 同一 Wi-Fi 下用手机扫远程访问面板里的二维码即可进入。
|
|
100
112
|
- **公网隧道。** Cloudflare(临时/固定域名)或自建隧道,与直连网关、Tailscale 按需选用。
|
|
101
113
|
- **远程工作区。** 手机端网页目录树直接浏览并选择工作区对话(走 DSH 官方 browse 选择器)。
|
|
114
|
+
- **旧内核浏览器支持。** 网关向页面注入 `Promise.withResolvers` / `AbortSignal.any` /
|
|
115
|
+
`Array.prototype.toSorted` 三段 polyfill,Chromium 108 档内核(统信 UOS、360 浏览器)
|
|
116
|
+
也能正常加载工作区;现代浏览器上原样跳过。
|
|
102
117
|
- **安全认证。** 密码门禁 + 扫码免密 Token + 敏感配置的管理员解锁/锁定 + 防暴力破解限频、
|
|
103
118
|
登录态持久化(宿主重启不必重新登录);永不信任客户端自称的 `isLocalhost`。
|
|
104
119
|
- **访客管理。** 实时查看「谁正连着本机」(局域网 / 直连 / 隧道,隧道场景显示真实来源 IP),
|
|
@@ -128,6 +143,8 @@ DeepSeek Harness(DSH)远程访问插件。以 [wenbin-wb/dsh-bridge](https:/
|
|
|
128
143
|
- Node `^22.19.0 || >=24.0.0`。
|
|
129
144
|
- DSH web profile(构建与测试基于 `0.1.7-rc.2`,同时兼容 `0.1.5` 线:宿主服务、客户端槽位与
|
|
130
145
|
设置接口在两个版本上都存在,无需额外适配)。
|
|
146
|
+
- 网页端建议使用现代浏览器:Chrome/Edge ≥ 119、Safari ≥ 17.4 或 Firefox ≥ 121。
|
|
147
|
+
旧内核由上文注入的 polyfill 兜底(含 Chromium 108 档的统信 UOS / 360 浏览器)。
|
|
131
148
|
|
|
132
149
|
## 安装
|
|
133
150
|
|
|
@@ -11637,6 +11637,12 @@ var require_follow_redirects = __commonJS({
|
|
|
11637
11637
|
RedirectableRequest.prototype._sanitizeOptions = function(options) {
|
|
11638
11638
|
if (!options.headers) {
|
|
11639
11639
|
options.headers = {};
|
|
11640
|
+
} else if (Array.isArray(options.headers)) {
|
|
11641
|
+
var headerList = options.headers;
|
|
11642
|
+
options.headers = {};
|
|
11643
|
+
for (var i = 0; i < headerList.length; i += 2) {
|
|
11644
|
+
options.headers[headerList[i]] = headerList[i + 1];
|
|
11645
|
+
}
|
|
11640
11646
|
}
|
|
11641
11647
|
if (!isArray(options.sensitiveHeaders)) {
|
|
11642
11648
|
options.sensitiveHeaders = [];
|
|
@@ -11911,7 +11917,7 @@ var require_follow_redirects = __commonJS({
|
|
|
11911
11917
|
return URL2 && value instanceof URL2;
|
|
11912
11918
|
}
|
|
11913
11919
|
function escapeRegex(regex) {
|
|
11914
|
-
return regex.replace(/[\]\\/()
|
|
11920
|
+
return regex.replace(/[[\]\\/()*+?.^$|]/g, "\\$&");
|
|
11915
11921
|
}
|
|
11916
11922
|
module.exports = wrap({ http, https });
|
|
11917
11923
|
module.exports.wrap = wrap;
|
|
@@ -22776,11 +22782,23 @@ var require_websocket = __commonJS({
|
|
|
22776
22782
|
this._isServer = false;
|
|
22777
22783
|
this._redirects = 0;
|
|
22778
22784
|
if (protocols === void 0) {
|
|
22779
|
-
protocols
|
|
22785
|
+
if (!options || options.protocols === void 0) {
|
|
22786
|
+
protocols = [];
|
|
22787
|
+
} else if (Array.isArray(options.protocols)) {
|
|
22788
|
+
protocols = options.protocols;
|
|
22789
|
+
} else {
|
|
22790
|
+
protocols = [options.protocols];
|
|
22791
|
+
}
|
|
22780
22792
|
} else if (!Array.isArray(protocols)) {
|
|
22781
22793
|
if (typeof protocols === "object" && protocols !== null) {
|
|
22782
22794
|
options = protocols;
|
|
22783
|
-
protocols
|
|
22795
|
+
if (options.protocols === void 0) {
|
|
22796
|
+
protocols = [];
|
|
22797
|
+
} else if (Array.isArray(options.protocols)) {
|
|
22798
|
+
protocols = options.protocols;
|
|
22799
|
+
} else {
|
|
22800
|
+
protocols = [options.protocols];
|
|
22801
|
+
}
|
|
22784
22802
|
} else {
|
|
22785
22803
|
protocols = [protocols];
|
|
22786
22804
|
}
|
|
@@ -22977,7 +22995,6 @@ var require_websocket = __commonJS({
|
|
|
22977
22995
|
}
|
|
22978
22996
|
return;
|
|
22979
22997
|
}
|
|
22980
|
-
this._readyState = _WebSocket.CLOSING;
|
|
22981
22998
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
22982
22999
|
if (err) return;
|
|
22983
23000
|
this._closeFrameSent = true;
|
|
@@ -22985,6 +23002,7 @@ var require_websocket = __commonJS({
|
|
|
22985
23002
|
this._socket.end();
|
|
22986
23003
|
}
|
|
22987
23004
|
});
|
|
23005
|
+
this._readyState = _WebSocket.CLOSING;
|
|
22988
23006
|
setCloseTimer(this);
|
|
22989
23007
|
}
|
|
22990
23008
|
/**
|
|
@@ -23209,6 +23227,7 @@ var require_websocket = __commonJS({
|
|
|
23209
23227
|
socketPath: void 0,
|
|
23210
23228
|
hostname: void 0,
|
|
23211
23229
|
protocol: void 0,
|
|
23230
|
+
protocols: void 0,
|
|
23212
23231
|
timeout: void 0,
|
|
23213
23232
|
method: "GET",
|
|
23214
23233
|
host: void 0,
|
package/lib/index.js
CHANGED
|
@@ -57,6 +57,77 @@ const inject = ['connection', 'webServer', 'sessions', 'agents', 'approval', 'wo
|
|
|
57
57
|
const DIRECTORY_PICKER_BACKEND = '@deepseek-ai/dsh-host-directory-picker-browse';
|
|
58
58
|
const DIRECTORY_PICKER_SURFACE = '@deepseek-ai/dsh-client-ui-directory-picker-browse';
|
|
59
59
|
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// 旧内核兜底 polyfill(由 apply() 经 webserver/index-inject 注入 index.html 的 <head>)
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// 为什么需要:DSH 0.2.0-rc.2 的浏览器端直接用了三个较新的 Web API,旧内核上一进
|
|
64
|
+
// 页面就抛异常,表现为「外壳出得来、工作区永远加载不了」。2026-10-11 在统信 UOS /
|
|
65
|
+
// 360 浏览器(内核均为 Chrome 108)上实测复现,DevTools 报
|
|
66
|
+
// `Promise.withResolvers is not a function` 与 `AbortSignal.any is not a function`,
|
|
67
|
+
// 后者抛在 dsh-api-gateway 的 RemoteStream.read 里,表现为 connection lost 无限重连。
|
|
68
|
+
//
|
|
69
|
+
// Promise.withResolvers Chrome/Edge <119、Firefox <121、Safari <17.4
|
|
70
|
+
// 调用点:index.html 末尾的启动门
|
|
71
|
+
// (globalThis.__DSH_BOOT_READY__ ??= Promise.withResolvers()).resolve()
|
|
72
|
+
// 以及 @deepseek-ai/dsh-api-workspace-files 的客户端(工作区文件流启动)。
|
|
73
|
+
// AbortSignal.any Chrome/Edge <116、Firefox <124、Safari <17.4
|
|
74
|
+
// 调用点:@deepseek-ai/dsh-api-gateway、dsh-api-session-controller、
|
|
75
|
+
// dsh-client-ui-workspace、dsh-client-ui-sidebar-files 等。
|
|
76
|
+
// Array.prototype.toSorted Chrome/Edge <110
|
|
77
|
+
// 调用点:dsh-client-ui-model-selection / -schedule / -settings-models。
|
|
78
|
+
//
|
|
79
|
+
// 注入位置在 <head>,早于 <body> 末尾的启动门,因此一定先于它们执行。
|
|
80
|
+
// 全部带 typeof 守卫且幂等:现代内核上原样跳过,绝不覆盖原生实现。
|
|
81
|
+
const LEGACY_BROWSER_POLYFILL = `
|
|
82
|
+
!function () {
|
|
83
|
+
try {
|
|
84
|
+
if (typeof Promise.withResolvers !== 'function') {
|
|
85
|
+
Promise.withResolvers = function () {
|
|
86
|
+
var resolve, reject;
|
|
87
|
+
var promise = new Promise(function (res, rej) { resolve = res; reject = rej; });
|
|
88
|
+
return { promise: promise, resolve: resolve, reject: reject };
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.any !== 'function') {
|
|
92
|
+
AbortSignal.any = function (signals) {
|
|
93
|
+
if (signals == null || typeof signals[Symbol.iterator] !== 'function') {
|
|
94
|
+
throw new TypeError('AbortSignal.any: argument is not iterable');
|
|
95
|
+
}
|
|
96
|
+
var list = [], it = signals[Symbol.iterator](), step;
|
|
97
|
+
while (!(step = it.next()).done) list.push(step.value);
|
|
98
|
+
for (var i = 0; i < list.length; i++) {
|
|
99
|
+
if (list[i] === null || typeof list[i] !== 'object' || typeof list[i].aborted !== 'boolean') {
|
|
100
|
+
throw new TypeError('AbortSignal.any: member is not an AbortSignal');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
var ctrl = new AbortController();
|
|
104
|
+
for (var j = 0; j < list.length; j++) {
|
|
105
|
+
if (list[j].aborted) { ctrl.abort(list[j].reason); return ctrl.signal; }
|
|
106
|
+
}
|
|
107
|
+
var onAbort = function (e) { ctrl.abort(e.target.reason); };
|
|
108
|
+
for (var k = 0; k < list.length; k++) list[k].addEventListener('abort', onAbort, { once: true });
|
|
109
|
+
// 组合信号 abort 后摘掉监听:lifetime / generation 这类长命 signal 上不能堆监听器
|
|
110
|
+
ctrl.signal.addEventListener('abort', function () {
|
|
111
|
+
for (var m = 0; m < list.length; m++) list[m].removeEventListener('abort', onAbort);
|
|
112
|
+
}, { once: true });
|
|
113
|
+
return ctrl.signal;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (typeof Array.prototype.toSorted !== 'function') {
|
|
117
|
+
Object.defineProperty(Array.prototype, 'toSorted', {
|
|
118
|
+
value: function (compareFn) {
|
|
119
|
+
'use strict';
|
|
120
|
+
if (this === null || this === undefined) throw new TypeError('Array.prototype.toSorted called on null or undefined');
|
|
121
|
+
if (compareFn !== undefined && typeof compareFn !== 'function') throw new TypeError('The comparison function must be either a function or undefined');
|
|
122
|
+
return Array.prototype.slice.call(this).sort(compareFn);
|
|
123
|
+
},
|
|
124
|
+
writable: true, enumerable: false, configurable: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
} catch (e) { void e; }
|
|
128
|
+
}();
|
|
129
|
+
`;
|
|
130
|
+
|
|
60
131
|
// 从 package.json 动态读取版本号,发版只需改 package.json 一处
|
|
61
132
|
const PACKAGE_JSON = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'));
|
|
62
133
|
const VERSION = PACKAGE_JSON.version ?? '0.0.0';
|
|
@@ -2085,6 +2156,14 @@ function apply(ctx, config = {}) {
|
|
|
2085
2156
|
text:
|
|
2086
2157
|
"!function(){try{if(self.crypto&&!self.crypto.randomUUID){self.crypto.randomUUID=function(){var b=new Uint8Array(16);self.crypto.getRandomValues(b);b[6]=b[6]&15|64;b[8]=b[8]&63|128;var h=\"\";for(var i=0;i<16;i++){var x=b[i].toString(16);h+=(x.length<2?\"0\":\"\")+x;if(i===3||i===5||i===7||i===9)h+=\"-\";}return h;}}}catch(e){}}();",
|
|
2087
2158
|
},
|
|
2159
|
+
{
|
|
2160
|
+
// 旧内核兜底:Promise.withResolvers / AbortSignal.any / Array.prototype.toSorted。
|
|
2161
|
+
// 缺前两个的表现是「外壳出来、工作区永远加载不了」,取舍与调用点见文件顶部
|
|
2162
|
+
// LEGACY_BROWSER_POLYFILL 的注释块。
|
|
2163
|
+
kind: 'script',
|
|
2164
|
+
placement: 'head',
|
|
2165
|
+
text: LEGACY_BROWSER_POLYFILL,
|
|
2166
|
+
},
|
|
2088
2167
|
);
|
|
2089
2168
|
});
|
|
2090
2169
|
|
|
@@ -2611,4 +2690,4 @@ function apply(ctx, config = {}) {
|
|
|
2611
2690
|
}, 'dsh-bridge: stop wechat, qq, feishu, telegram, proxy, auth and tunnels');
|
|
2612
2691
|
}
|
|
2613
2692
|
|
|
2614
|
-
export { name, inject, apply, ProxyServer, BridgeService, selectLanIPv4, listAllLanIPv4 };
|
|
2693
|
+
export { name, inject, apply, ProxyServer, BridgeService, selectLanIPv4, listAllLanIPv4, LEGACY_BROWSER_POLYFILL };
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-bridge-gateway",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "以 dsh-bridge 为根基的 DSH 远程访问网关:局域网 / Cloudflare 隧道 / Tailscale 隧道 / 自建隧道 / 公网直连网关 / IM Bot,全部在 Web 面板中配置。",
|
|
5
|
-
"releaseNotes": "【dsh-bridge-gateway 0.2.
|
|
5
|
+
"releaseNotes": "【dsh-bridge-gateway 0.2.2】\n• 🐛 修复旧内核浏览器(统信 UOS / 360 浏览器等 Chromium 108)打开后工作区永远加载不了:经官方 `webserver/index-inject` 注入 `Promise.withResolvers` / `AbortSignal.any` / `Array.prototype.toSorted` 三个 polyfill,全部带 `typeof` 守卫且幂等,现代浏览器上原样跳过、不覆盖原生实现\n• ✅ 新增 10 条兼容性断言(含「不得覆盖原生实现」「幂等」两条反向断言),全套 216 项测试通过",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|