fix(weixin): refine Harness access errors

This commit is contained in:
xmanrui 2026-08-22 18:56:49 +08:00
parent ae49413871
commit 83ee0d63a7
7 changed files with 194 additions and 135 deletions

View file

@ -8,6 +8,55 @@ import { adoptRegisteredWorkspaceSession } from './harness-session-binding.mjs';
// Harness origin prevents two channel-specific clients bound to one Session
// from claiming or cancelling each other's interactions.
const interactionRegistries = new Map();
const MAX_ERROR_CLASSIFICATION_BYTES = 64;
async function smallResponseText(response) {
const stream = response?.body;
if (!stream || typeof stream.getReader !== 'function') return null;
const reader = stream.getReader();
const chunks = [];
let length = 0;
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (!(value instanceof Uint8Array)
|| length + value.byteLength > MAX_ERROR_CLASSIFICATION_BYTES) return null;
chunks.push(value);
length += value.byteLength;
}
} catch {
return null;
} finally {
try {
await reader.cancel();
} catch {
// The response body is diagnostic-only; cancellation failures do not
// replace the HTTP status that caused the transport error.
}
}
const bytes = new Uint8Array(length);
let offset = 0;
for (const chunk of chunks) {
bytes.set(chunk, offset);
offset += chunk.byteLength;
}
return new TextDecoder().decode(bytes);
}
async function harnessHttpErrorCode(response) {
if (response.status === 401) return 'harness-auth-required';
if (response.status === 403) {
const body = await smallResponseText(response);
return body?.trim() === 'forbidden'
? 'harness-host-untrusted'
: 'harness-request-forbidden';
}
if (response.status === 404) return 'harness-api-not-found';
return 'harness-http-failed';
}
function interactionRegistry(origin) {
let registry = interactionRegistries.get(origin);
@ -494,11 +543,7 @@ export class HarnessClient {
);
}
if (!response.ok) {
const code = response.status === 401 || response.status === 403
? 'harness-access-denied'
: response.status === 404
? 'harness-api-not-found'
: 'harness-http-failed';
const code = await harnessHttpErrorCode(response);
throw new HarnessTransportError(code, method, { status: response.status });
}
let body;

View file

@ -27,7 +27,9 @@ const ACTIVATION_ERROR_MESSAGES = Object.freeze({
'runtime-prepare-failed': '微信已授权,但无法初始化账号状态或工作区。请检查 DSH_HOME 和工作区目录。',
'harness-connect-failed': '微信已授权,但插件无法连接本机 Harness。请检查 dsh web 地址和端口。',
'harness-timeout': '微信已授权,但 Harness 健康检查超时。请确认 dsh web 未阻塞。',
'harness-access-denied': '微信已授权,但 Harness 拒绝了本机健康检查。请检查 Host 信任配置。',
'harness-auth-required': '微信已授权,但 Harness 健康检查需要身份认证。请检查代理、网关或自定义鉴权配置。',
'harness-host-untrusted': '微信已授权,但 Harness 的 Host 信任检查拒绝了本机请求。请检查 harnessBaseUrl 与 trustedHosts 配置。',
'harness-request-forbidden': '微信已授权,但健康检查收到了非 Harness 标准的 403 拒绝响应。请检查代理或网关配置。',
'harness-api-not-found': '微信已授权,但找不到 Harness 健康检查接口。请确认 Harness 与插件版本兼容。',
'harness-http-failed': '微信已授权,但 Harness 健康检查返回服务错误。请查看 dsh web 日志。',
'harness-response-invalid': '微信已授权,但 Harness 返回了无法识别的响应。请确认 Harness 与插件版本兼容。',

View file

@ -9,7 +9,9 @@ const DEFAULT_START_RETRY_DELAYS_MS = Object.freeze([250, 1_000, 3_000]);
const HARNESS_HEALTH_ERROR_CODES = new Set([
'harness-connect-failed',
'harness-timeout',
'harness-access-denied',
'harness-auth-required',
'harness-host-untrusted',
'harness-request-forbidden',
'harness-api-not-found',
'harness-http-failed',
'harness-response-invalid',