@wenbin_wb/dsh-bridge 2.10.1 → 2.10.3
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 +39 -0
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/client/client.js +103 -1
- package/client/index.js +83 -0
- package/lib/bridge-rpc-constants.js +1 -0
- package/lib/bridge-rpc.js +10 -0
- package/lib/feishu/node.js +10 -4
- package/lib/index.js +126 -14
- package/lib/platform/conversation-bridge.js +8 -3
- package/lib/platform/message-split.js +39 -1
- package/lib/qq/node.js +539 -532
- package/lib/session-strip.js +57 -0
- package/lib/telegram/node.js +0 -27
- package/lib/tunnel-client.mjs +419 -426
- package/lib/wechat/gateway.js +17 -4
- package/package.json +2 -2
package/lib/wechat/gateway.js
CHANGED
|
@@ -84,17 +84,19 @@ function baseInfo() {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/** 带超时与 abort 的 POST JSON。非 2xx 抛出带 HTTP 状态的错误。 */
|
|
87
|
-
async function postJson({ baseUrl = ILINK_BASE_URL, endpoint, payload, token, timeoutMs = API_TIMEOUT_MS }) {
|
|
87
|
+
async function postJson({ baseUrl = ILINK_BASE_URL, endpoint, payload, token, timeoutMs = API_TIMEOUT_MS, signal: externalSignal }) {
|
|
88
88
|
const body = JSON.stringify({ ...payload, base_info: baseInfo() })
|
|
89
89
|
const url = `${baseUrl.replace(/\/+$/, '')}/${endpoint}`
|
|
90
90
|
const controller = new AbortController()
|
|
91
91
|
const timer = setTimeout(() => controller.abort(), timeoutMs)
|
|
92
|
+
// 外部 signal(如轮询停止)与超时合并:任一触发即中止请求
|
|
93
|
+
const signal = externalSignal ? AbortSignal.any([externalSignal, controller.signal]) : controller.signal
|
|
92
94
|
try {
|
|
93
95
|
const response = await fetch(url, {
|
|
94
96
|
method: 'POST',
|
|
95
97
|
headers: requestHeaders(token, body),
|
|
96
98
|
body,
|
|
97
|
-
signal
|
|
99
|
+
signal,
|
|
98
100
|
})
|
|
99
101
|
const raw = await response.text()
|
|
100
102
|
if (!response.ok) {
|
|
@@ -136,7 +138,7 @@ async function getJson({ baseUrl = ILINK_BASE_URL, endpoint, timeoutMs = QR_TIME
|
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
/** 长轮询收消息;超时返回空批次(不算错误)。 */
|
|
139
|
-
async function getUpdates({ baseUrl, token, syncBuf, timeoutMs = LONG_POLL_TIMEOUT_MS }) {
|
|
141
|
+
async function getUpdates({ baseUrl, token, syncBuf, timeoutMs = LONG_POLL_TIMEOUT_MS, signal } = {}) {
|
|
140
142
|
try {
|
|
141
143
|
const raw = await postJson({
|
|
142
144
|
baseUrl,
|
|
@@ -144,6 +146,7 @@ async function getUpdates({ baseUrl, token, syncBuf, timeoutMs = LONG_POLL_TIMEO
|
|
|
144
146
|
payload: { get_updates_buf: syncBuf },
|
|
145
147
|
token,
|
|
146
148
|
timeoutMs,
|
|
149
|
+
signal,
|
|
147
150
|
})
|
|
148
151
|
return {
|
|
149
152
|
messages: Array.isArray(raw.msgs) ? raw.msgs : [],
|
|
@@ -315,6 +318,8 @@ export class WechatGateway extends Service {
|
|
|
315
318
|
this.syncBuf = ''
|
|
316
319
|
this.pollTask = null
|
|
317
320
|
this.stopPollingLocal = false
|
|
321
|
+
// 当前轮询循环的中止信号:stop/restart 时 abort 以立即中断 in-flight 长轮询
|
|
322
|
+
this._pollAbort = null
|
|
318
323
|
this.statusValue = 'idle'
|
|
319
324
|
this.contextTokens = new Map()
|
|
320
325
|
try {
|
|
@@ -388,6 +393,7 @@ export class WechatGateway extends Service {
|
|
|
388
393
|
|
|
389
394
|
async stop() {
|
|
390
395
|
this.stopPollingLocal = true
|
|
396
|
+
this._pollAbort?.abort()
|
|
391
397
|
const task = this.pollTask
|
|
392
398
|
this.pollTask = null
|
|
393
399
|
if (task) {
|
|
@@ -770,6 +776,8 @@ export class WechatGateway extends Service {
|
|
|
770
776
|
if (this._restartingPromise) return this._restartingPromise
|
|
771
777
|
this._restartingPromise = (async () => {
|
|
772
778
|
this.stopPollingLocal = true
|
|
779
|
+
// 立即中断旧循环的 in-flight 长轮询,避免等待最长 35s 才切换
|
|
780
|
+
this._pollAbort?.abort()
|
|
773
781
|
const previous = this.pollTask
|
|
774
782
|
this.pollTask = null
|
|
775
783
|
if (previous) {
|
|
@@ -799,6 +807,9 @@ export class WechatGateway extends Service {
|
|
|
799
807
|
}
|
|
800
808
|
|
|
801
809
|
async runPollLoop() {
|
|
810
|
+
// 每次轮询循环持有一个独立 abort:stop/restart 时中断 in-flight getUpdates(最长 35s)
|
|
811
|
+
const pollAbort = new AbortController()
|
|
812
|
+
this._pollAbort = pollAbort
|
|
802
813
|
let consecutiveFailures = 0
|
|
803
814
|
let timeoutMs = this.c.longPollTimeoutMs
|
|
804
815
|
let fatal = false
|
|
@@ -809,6 +820,7 @@ export class WechatGateway extends Service {
|
|
|
809
820
|
token: this.c.token,
|
|
810
821
|
syncBuf: this.syncBuf,
|
|
811
822
|
timeoutMs,
|
|
823
|
+
signal: pollAbort.signal,
|
|
812
824
|
})
|
|
813
825
|
if (this.stopPollingLocal) break
|
|
814
826
|
|
|
@@ -874,7 +886,8 @@ export class WechatGateway extends Service {
|
|
|
874
886
|
await sleep(backoff)
|
|
875
887
|
}
|
|
876
888
|
}
|
|
877
|
-
//
|
|
889
|
+
// 清理当前轮询 abort(避免悬挂引用);致命错误保持终态,普通停止回到 idle
|
|
890
|
+
if (this._pollAbort === pollAbort) this._pollAbort = null
|
|
878
891
|
if (!fatal) this.setStatus('idle')
|
|
879
892
|
}
|
|
880
893
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenbin_wb/dsh-bridge",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.3",
|
|
4
4
|
"description": "手机扫码即可在移动端/公网继续用 DeepSeek Harness,人不在电脑前也能接着干。一键局域网二维码、Cloudflare 公网隧道、自建隧道与微信 / QQ / 飞书 / Telegram Bot(多工作区/会话持久化/媒体/卡片审批/流式输出),无需自己搭公网服务器。",
|
|
5
|
-
"releaseNotes": "【v2.10.
|
|
5
|
+
"releaseNotes": "【v2.10.3】\n• 兼容 DSH 原生端口 3080 直连(issue #28):本机直连不再被误判为远程而锁定,走代理与直连行为一致\n• 安全:AI [SEND_FILE] 仅允许发送工作目录内文件,拦截 .ssh/.credentials/.env 等敏感路径\n• 修复:代理响应头透传、QQ 流式重复发送、微信轮询竞态(iLink 403)、飞书群聊审批按钮误拦\n• 会话列表/历史大字段剥离覆盖局域网与 Cloudflare 等全部公网入口",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
8
8
|
"exports": {
|