mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-09 22:43:25 +08:00
feat(gateway): surface negotiated handshake protocol version in UI
Users have reported confusion about "when will ClawPanel update its gateway protocol to v4". This is actually a misreading: ClawPanel v0.15+ already advertises `minProtocol=3, maxProtocol=4` in its connect frame, and negotiates v4 transparently when the kernel is >= 2026.5.12. The `v3|` prefix users were seeing in dev-api.js is the device signature payload string schema version, which is a completely separate concept from the handshake protocol version. Make this visible and unambiguous: UI - Add a "Proto v4" badge next to the Gateway service name in /services once the WS handshake succeeds, with a tooltip explaining that this is the WS handshake protocol version (not the device signature payload v3 format). - Add the same protocol info to the WebSocket row in /chat-debug. API - WsClient now exposes `negotiatedProtocol` which prefers the explicit field from the hello payload (`protocol` / `protocolVersion` / `negotiatedProtocol`) and falls back to inferring from serverVersion: kernels >= 2026.5.12 are reported as v4, older as v3. This matches the panel's advertised range of [3, 4]. - KernelSnapshot grows a `protocol` field so feature gates and UIs that already consume the snapshot can read it without touching wsClient. Comments - Expand the KERNEL_TARGET comment in feature-catalog.js to spell out the two-distinct-version-numbers rule explicitly. - Add matching clarifying comments next to the `v3|...` payload string in both scripts/dev-api.js and src-tauri/src/commands/device.rs, so the next reader does not confuse payload schema with handshake. ## Verification - node --check on all touched JS files - npm run build - cargo fmt --check && cargo check (clippy errors that surface are pre-existing debt in config.rs, untouched here) - Playwright /services: mock wsClient state, observe `协议 v4` badge rendered with `rgba(99, 102, 241, 0.1)` background and accent color, for both the explicit-protocol path and the version-inferred path.
This commit is contained in:
@@ -190,7 +190,12 @@ export const KERNEL_FLOOR = {
|
||||
export const KERNEL_TARGET = {
|
||||
openclaw: {
|
||||
// 内核协议在 5.12 升级到 v4(MIN_CLIENT_PROTOCOL_VERSION=4,新增增量 chat delta payloads),
|
||||
// 面板通过 connect frame `[minProtocol=3, maxProtocol=4]` 同时兼容新旧内核
|
||||
// 面板通过 connect frame `[minProtocol=3, maxProtocol=4]` 同时兼容新旧内核。
|
||||
//
|
||||
// ⚠️ 警告:这里的"协议 v3/v4"指 **Gateway WebSocket 握手帧协议版本**。
|
||||
// 不要与 dev-api.js 中设备签名 payload 字符串前缀 `v3|deviceId|...` 混淆——
|
||||
// 后者是 **device signature payload 字符串格式版本**,两者完全独立、互不相关。
|
||||
// 即使在 v4 握手协议下,签名 payload 字符串仍以 `v3|` 开头(这是 payload schema 版本)。
|
||||
official: '2026.5.12',
|
||||
chinese: '2026.5.12-zh.2',
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@ const _listeners = []
|
||||
* @property {boolean} isLatest 是否 >= target
|
||||
* @property {Set<string>} features 当前启用的特性 id 集合
|
||||
* @property {string} versionLabel 人类可读的版本显示,例如 "2026.5.6 汉化"
|
||||
* @property {number|null} protocol 握手协商出的 Gateway WS 协议版本 (3 或 4),未握手为 null
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -104,6 +105,7 @@ export function buildSnapshot(engineId, version) {
|
||||
versionLabel: version
|
||||
? `${versionBase}${variant === 'chinese' ? ' 汉化' : ''}`
|
||||
: '',
|
||||
protocol: wsClient.negotiatedProtocol,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +188,7 @@ function refresh() {
|
||||
|| _snapshot.engine !== next.engine
|
||||
|| _snapshot.version !== next.version
|
||||
|| _snapshot.features.size !== next.features.size
|
||||
|| _snapshot.protocol !== next.protocol
|
||||
_snapshot = next
|
||||
if (changed) {
|
||||
_listeners.forEach(fn => {
|
||||
|
||||
@@ -128,6 +128,34 @@ export class WsClient {
|
||||
get hello() { return this._hello }
|
||||
get sessionKey() { return this._sessionKey }
|
||||
get serverVersion() { return this._serverVersion }
|
||||
/**
|
||||
* 当前 Gateway 与 ClawPanel 协商出的握手协议版本 (3 或 4)。
|
||||
*
|
||||
* 注意:这里的 "协议版本" 指 Gateway WebSocket 握手帧协议 (kernel ws frame protocol),
|
||||
* 不要与设备签名 payload 的前缀 `v3|deviceId|...` 混淆,后者是 device-signature
|
||||
* payload 字符串格式版本,两者完全独立。
|
||||
*
|
||||
* 取值优先级:
|
||||
* 1. hello payload 中显式回传的 protocol / protocolVersion / negotiatedProtocol 字段
|
||||
* 2. 按 serverVersion 推断:OpenClaw 内核 >= 2026.5.12 → v4,否则 v3
|
||||
* (ClawPanel 客户端永远声明 minProtocol=3, maxProtocol=4,所以协商只会是 3 或 4)
|
||||
*
|
||||
* 未握手时返回 null。
|
||||
*/
|
||||
get negotiatedProtocol() {
|
||||
if (!this._hello) return null
|
||||
const explicit = this._hello.protocol ?? this._hello.protocolVersion ?? this._hello.negotiatedProtocol
|
||||
if (Number.isFinite(explicit)) return explicit
|
||||
const ver = this._serverVersion
|
||||
if (!ver) return null
|
||||
const base = String(ver).replace(/-.*$/, '')
|
||||
const parts = base.split('.').map(n => Number(n))
|
||||
if (parts.length >= 3 && parts.every(Number.isFinite)) {
|
||||
const [y, mo, d] = parts
|
||||
if (y > 2026 || (y === 2026 && (mo > 5 || (mo === 5 && d >= 12)))) return 4
|
||||
}
|
||||
return 3
|
||||
}
|
||||
get reconnectState() { return this._reconnectState }
|
||||
get reconnectAttempts() { return this._reconnectAttempts }
|
||||
get lastConnectedAt() { return this._lastConnectedAt }
|
||||
@@ -145,6 +173,7 @@ export class WsClient {
|
||||
reconnectAttempts: this._reconnectAttempts,
|
||||
reconnectState: this._reconnectState,
|
||||
serverVersion: this._serverVersion,
|
||||
negotiatedProtocol: this.negotiatedProtocol,
|
||||
missedHeartbeats: this._missedHeartbeats,
|
||||
pendingReconnect: this._pendingReconnect,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user