@jingyi0605/codingns4dsh 0.1.2 → 0.1.7-rc.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/README.en.md +4 -4
- package/README.md +14 -5
- package/data/build/dist/client/account-bar.js +57 -3
- package/data/build/dist/client/bundle.js +36246 -268
- package/data/build/dist/client/cli-slots.js +60 -18
- package/data/build/dist/client/dsh-h5-bootstrap.d.ts +2 -0
- package/data/build/dist/client/dsh-h5-bootstrap.js +79 -13
- package/data/build/dist/client/features/file-management-panel.d.ts +5 -0
- package/data/build/dist/client/features/file-management-panel.js +48 -0
- package/data/build/dist/client/features/file-management.d.ts +4 -0
- package/data/build/dist/client/features/file-management.js +50 -0
- package/data/build/dist/client/features/index.d.ts +10 -1
- package/data/build/dist/client/features/index.js +12 -1
- package/data/build/dist/client/features/login-protection-panel.js +1 -1
- package/data/build/dist/client/features/login-protection-session.d.ts +2 -0
- package/data/build/dist/client/features/login-protection-session.js +18 -0
- package/data/build/dist/client/features/peer-host.d.ts +4 -0
- package/data/build/dist/client/features/peer-host.js +89 -0
- package/data/build/dist/client/features/reverse-proxy-panel.js +23 -7
- package/data/build/dist/client/features/reverse-proxy.d.ts +1 -2
- package/data/build/dist/client/features/reverse-proxy.js +75 -5
- package/data/build/dist/client/features/types.d.ts +9 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +10 -0
- package/data/build/dist/client/features/workspace-session-enhancement.js +18 -1
- package/data/build/dist/client/file-management-dom.d.ts +12 -0
- package/data/build/dist/client/file-management-dom.js +527 -0
- package/data/build/dist/client/git-management.js +24 -5
- package/data/build/dist/client/host-navigation.d.ts +29 -0
- package/data/build/dist/client/host-navigation.js +29 -0
- package/data/build/dist/client/host-router.d.ts +3 -0
- package/data/build/dist/client/host-router.js +2 -0
- package/data/build/dist/client/index.d.ts +6 -1
- package/data/build/dist/client/index.js +30 -10
- package/data/build/dist/client/locale.js +36 -7
- package/data/build/dist/client/peer-host-connection-button.d.ts +13 -0
- package/data/build/dist/client/peer-host-connection-button.js +69 -0
- package/data/build/dist/client/peer-host-management-api.d.ts +40 -0
- package/data/build/dist/client/peer-host-management-api.js +33 -0
- package/data/build/dist/client/peer-host-management-panel.d.ts +13 -0
- package/data/build/dist/client/peer-host-management-panel.js +181 -0
- package/data/build/dist/client/peer-host-native-session-ui.d.ts +60 -0
- package/data/build/dist/client/peer-host-native-session-ui.js +372 -0
- package/data/build/dist/client/peer-host-scoped-client.d.ts +67 -0
- package/data/build/dist/client/peer-host-scoped-client.js +316 -0
- package/data/build/dist/client/peer-host-session-controller.d.ts +30 -0
- package/data/build/dist/client/peer-host-session-controller.js +95 -0
- package/data/build/dist/client/quick-phrase-slot.js +14 -0
- package/data/build/dist/client/remote-web-context.js +26 -4
- package/data/build/dist/client/session-changed-files-view.d.ts +15 -0
- package/data/build/dist/client/session-changed-files-view.js +337 -0
- package/data/build/dist/client/settings-bridge.d.ts +14 -4
- package/data/build/dist/client/settings-bridge.js +59 -12
- package/data/build/dist/client/settings-section.js +17 -4
- package/data/build/dist/client/subscription-slot.js +21 -8
- package/data/build/dist/client/workspace-session-rightbar-dom.d.ts +28 -0
- package/data/build/dist/client/workspace-session-rightbar-dom.js +291 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +25 -33
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +7 -1
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +34 -11
- package/data/build/dist/dsh-capabilities/matrix.js +9 -0
- package/data/build/dist/dsh-capabilities/routes.js +35 -0
- package/data/build/dist/dsh-capabilities/settings-store.d.ts +12 -0
- package/data/build/dist/dsh-capabilities/settings-store.js +34 -0
- package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
- package/data/build/dist/features/host-router.d.ts +26 -0
- package/data/build/dist/features/host-router.js +110 -0
- package/data/build/dist/features/registry.js +3 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +13 -1
- package/data/build/dist/host/cli-adapters/codex-driver.js +528 -72
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +21 -2
- package/data/build/dist/host/cli-adapters/command-code-driver.js +408 -105
- package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +47 -28
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +37 -0
- package/data/build/dist/host/cli-adapters/feature.js +94 -10
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +14 -3
- package/data/build/dist/host/cli-adapters/kimi-driver.js +3 -3
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/model-catalog.js +8 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +14 -2
- package/data/build/dist/host/cli-adapters/pi-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +8 -0
- package/data/build/dist/host/cli-adapters/process-utils.js +48 -0
- package/data/build/dist/host/cli-adapters/registry.d.ts +6 -1
- package/data/build/dist/host/cli-adapters/registry.js +41 -7
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +23 -4
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +36 -15
- package/data/build/dist/host/dsh-device-runtime.d.ts +2 -0
- package/data/build/dist/host/dsh-device-runtime.js +55 -29
- package/data/build/dist/host/dsh-runtime-version.d.ts +3 -2
- package/data/build/dist/host/dsh-runtime-version.js +71 -2
- package/data/build/dist/host/features/auth.js +10 -2
- package/data/build/dist/host/features/file-management.d.ts +5 -0
- package/data/build/dist/host/features/file-management.js +267 -0
- package/data/build/dist/host/features/git-management.js +24 -2
- package/data/build/dist/host/features/index.d.ts +2 -0
- package/data/build/dist/host/features/index.js +6 -0
- package/data/build/dist/host/features/peer-host.d.ts +22 -0
- package/data/build/dist/host/features/peer-host.js +322 -0
- package/data/build/dist/host/features/types.d.ts +4 -0
- package/data/build/dist/host/host-router.d.ts +3 -0
- package/data/build/dist/host/host-router.js +2 -0
- package/data/build/dist/host/index-injection.d.ts +17 -0
- package/data/build/dist/host/index-injection.js +24 -0
- package/data/build/dist/host/index.d.ts +4 -1
- package/data/build/dist/host/index.js +41 -7
- package/data/build/dist/host/lan-access-dsh.d.ts +34 -1
- package/data/build/dist/host/lan-access-dsh.js +145 -26
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts +52 -0
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js +166 -0
- package/data/build/dist/host/modules/peer-host/host-ws-connector.d.ts +31 -0
- package/data/build/dist/host/modules/peer-host/host-ws-connector.js +108 -0
- package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.d.ts +26 -0
- package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.js +159 -0
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.d.ts +50 -0
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.js +97 -0
- package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.d.ts +19 -0
- package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.js +54 -0
- package/data/build/dist/host/modules/peer-host/peer-host-handshake.d.ts +44 -0
- package/data/build/dist/host/modules/peer-host/peer-host-handshake.js +133 -0
- package/data/build/dist/host/modules/peer-host/peer-host-relay.d.ts +65 -0
- package/data/build/dist/host/modules/peer-host/peer-host-relay.js +196 -0
- package/data/build/dist/host/modules/peer-host/peer-host-session.d.ts +42 -0
- package/data/build/dist/host/modules/peer-host/peer-host-session.js +152 -0
- package/data/build/dist/host/modules/peer-host/peer-host-store.d.ts +92 -0
- package/data/build/dist/host/modules/peer-host/peer-host-store.js +323 -0
- package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.d.ts +34 -0
- package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.js +156 -0
- package/data/build/dist/host/native-session-bridge.d.ts +18 -2
- package/data/build/dist/host/native-session-bridge.js +236 -8
- package/data/build/dist/host/remote-web-runtime.js +26 -1
- package/data/build/dist/host/rpc.js +8 -3
- package/data/build/dist/host/session-changes.d.ts +5 -0
- package/data/build/dist/host/session-changes.js +163 -0
- package/data/build/dist/host/settings.js +6 -0
- package/data/build/dist/index.d.ts +1 -1
- package/data/build/dist/index.js +1 -1
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +23 -2
- package/data/build/dist/shared/contracts/config.d.ts +15 -0
- package/data/build/dist/shared/contracts/config.js +10 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +2 -0
- package/data/build/dist/shared/contracts/errors.d.ts +14 -0
- package/data/build/dist/shared/contracts/errors.js +15 -0
- package/data/build/dist/shared/contracts/feature.d.ts +2 -0
- package/data/build/dist/shared/contracts/file-management.d.ts +6 -0
- package/data/build/dist/shared/contracts/file-management.js +3 -0
- package/data/build/dist/shared/contracts/peer-host.d.ts +116 -15
- package/data/build/dist/shared/contracts/peer-host.js +19 -0
- package/data/build/dist/shared/contracts/version.d.ts +1 -1
- package/data/build/dist/shared/contracts/version.js +1 -1
- package/data/build/dist/shared/index.d.ts +4 -3
- package/data/build/dist/shared/index.js +2 -2
- package/data/build/dist/transport/debug.d.ts +5 -0
- package/data/build/dist/transport/debug.js +23 -1
- package/data/build/dist/transport/dsh-transport.d.ts +2 -0
- package/data/build/dist/transport/dsh-transport.js +11 -0
- package/data/build/dist/transport/webrtc-client.d.ts +2 -0
- package/data/build/dist/transport/webrtc-client.js +2 -1
- package/data/build/dist/transport/webrtc-host.d.ts +2 -0
- package/data/build/dist/transport/webrtc-host.js +6 -3
- package/data/build/dist/typert.host.d.ts +1 -1
- package/data/build/dist/typert.host.js +2 -1
- package/dsh.bundle.patch +10 -0
- package/package.json +19 -11
- package/profile/README.md +2 -2
- package/profile/package.json +2 -2
- package/profile/version.json +1 -1
- package/version.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
/** DSH 原生工作台 WebSocket 的固定入口;不接受客户端传入路径。 */
|
|
3
|
+
export const PEER_HOST_REMOTE_WS_PATH = '/ws';
|
|
4
|
+
export class PeerHostConnectorError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = 'PeerHostConnectorError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 创建当前 Host 到目标 DSH 的 LAN 工作台 connector。
|
|
14
|
+
*
|
|
15
|
+
* 目标 URL、access token 和 WebSocket 路径均由 Host 侧生成;Client 只能通过
|
|
16
|
+
* 上层代理提交 HostScope。目标 DSH 的消息白名单仍由 `PeerHostWsProxyService`
|
|
17
|
+
* 在双端转发前执行,这里只负责固定端点的认证握手和消息类型的文本适配。
|
|
18
|
+
*/
|
|
19
|
+
export function createPeerHostRemoteConnector(options = {}) {
|
|
20
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
21
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 60_000)
|
|
22
|
+
throw new TypeError('PeerHost WebSocket connector 超时时间无效');
|
|
23
|
+
const websocketFactory = options.websocketFactory ?? ((url, init) => new WebSocket(url, { headers: init.headers }));
|
|
24
|
+
return async (record, accessToken, scope) => {
|
|
25
|
+
if (record.route.kind !== 'lan')
|
|
26
|
+
throw new PeerHostConnectorError('PEER_HOST_RELAY_UNAVAILABLE', '中转 PeerHost 暂不支持 Host-to-Host WebSocket');
|
|
27
|
+
if (accessToken.trim() === '')
|
|
28
|
+
throw new Error('PeerHost 目标登录态为空');
|
|
29
|
+
assertScope(scope, record.id);
|
|
30
|
+
const url = buildRemoteWebSocketUrl(record.route.normalizedOrigin, accessToken);
|
|
31
|
+
const socket = websocketFactory(url, { headers: { authorization: `Bearer ${accessToken}` } });
|
|
32
|
+
await waitForOpen(socket, timeoutMs);
|
|
33
|
+
return adaptSocket(socket);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function buildRemoteWebSocketUrl(origin, accessToken) {
|
|
37
|
+
const url = new URL(PEER_HOST_REMOTE_WS_PATH, origin);
|
|
38
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
39
|
+
url.searchParams.set('access_token', accessToken);
|
|
40
|
+
return url.toString();
|
|
41
|
+
}
|
|
42
|
+
function assertScope(scope, peerHostId) {
|
|
43
|
+
if (scope.targetHostId !== peerHostId || scope.hostId.trim() === '' || scope.workspaceId.trim() === '' || !Number.isSafeInteger(scope.scopeGeneration) || scope.scopeGeneration < 0)
|
|
44
|
+
throw new Error('PeerHost connector 作用域无效');
|
|
45
|
+
if (scope.sessionId !== null && scope.sessionId.trim() === '')
|
|
46
|
+
throw new Error('PeerHost connector 会话作用域无效');
|
|
47
|
+
}
|
|
48
|
+
async function waitForOpen(socket, timeoutMs) {
|
|
49
|
+
if (socket.readyState === 1)
|
|
50
|
+
return;
|
|
51
|
+
await new Promise((resolve, reject) => {
|
|
52
|
+
let settled = false;
|
|
53
|
+
const finish = (error) => {
|
|
54
|
+
if (settled)
|
|
55
|
+
return;
|
|
56
|
+
settled = true;
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
remove(socket, 'open', onOpen);
|
|
59
|
+
remove(socket, 'error', onError);
|
|
60
|
+
remove(socket, 'close', onClose);
|
|
61
|
+
remove(socket, 'unexpected-response', onUnexpectedResponse);
|
|
62
|
+
if (error === undefined)
|
|
63
|
+
resolve();
|
|
64
|
+
else
|
|
65
|
+
reject(error);
|
|
66
|
+
};
|
|
67
|
+
const onOpen = () => finish();
|
|
68
|
+
const onError = () => finish(new PeerHostConnectorError('PEER_HOST_PROXY_UNREACHABLE', '目标 Host WebSocket 连接失败'));
|
|
69
|
+
const onClose = () => finish(new PeerHostConnectorError('PEER_HOST_PROXY_UNREACHABLE', '目标 Host WebSocket 在打开前关闭'));
|
|
70
|
+
const onUnexpectedResponse = (_request, response) => {
|
|
71
|
+
const code = response.statusCode === 401 || response.statusCode === 403 ? 'PEER_HOST_SESSION_REQUIRED' : 'PEER_HOST_PROXY_UNREACHABLE';
|
|
72
|
+
finish(new PeerHostConnectorError(code, code === 'PEER_HOST_SESSION_REQUIRED' ? '目标 Host 登录态已失效' : '目标 Host WebSocket 握手失败'));
|
|
73
|
+
};
|
|
74
|
+
const timer = setTimeout(() => {
|
|
75
|
+
socket.terminate?.();
|
|
76
|
+
finish(new PeerHostConnectorError('PEER_HOST_PROXY_UNREACHABLE', '目标 Host WebSocket 连接超时'));
|
|
77
|
+
}, timeoutMs);
|
|
78
|
+
socket.once('open', onOpen);
|
|
79
|
+
socket.once('error', onError);
|
|
80
|
+
socket.once('close', onClose);
|
|
81
|
+
socket.once('unexpected-response', onUnexpectedResponse);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function adaptSocket(socket) {
|
|
85
|
+
return {
|
|
86
|
+
get readyState() { return socket.readyState; },
|
|
87
|
+
send(data) { socket.send(data); },
|
|
88
|
+
close(code, reason) { socket.close(code, reason); },
|
|
89
|
+
on(event, listener) {
|
|
90
|
+
if (event === 'message') {
|
|
91
|
+
socket.on('message', (value, isBinary) => listener(toText(value), isBinary));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
socket.on(event, (...args) => listener(...args));
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function toText(value) {
|
|
99
|
+
if (typeof value === 'string')
|
|
100
|
+
return value;
|
|
101
|
+
if (value instanceof Uint8Array)
|
|
102
|
+
return new TextDecoder().decode(value);
|
|
103
|
+
return String(value);
|
|
104
|
+
}
|
|
105
|
+
function remove(socket, event, listener) {
|
|
106
|
+
socket.off?.(event, listener);
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=host-ws-connector.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HostScope, PeerHostErrorCode, PeerHostRecord } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
import { PeerHostSessionService } from './peer-host-session.js';
|
|
3
|
+
import { PeerHostStore } from './peer-host-store.js';
|
|
4
|
+
export declare const PEER_HOST_WS_CLIENT_MESSAGE_TYPES: Set<string>;
|
|
5
|
+
export declare const PEER_HOST_WS_REMOTE_MESSAGE_TYPES: Set<string>;
|
|
6
|
+
export interface PeerHostSocket {
|
|
7
|
+
readonly readyState: number;
|
|
8
|
+
send(data: string): void;
|
|
9
|
+
close(code?: number, reason?: string): void;
|
|
10
|
+
on(event: 'message' | 'close' | 'error', listener: (...args: any[]) => void): void;
|
|
11
|
+
}
|
|
12
|
+
export type PeerHostRemoteConnector = (record: PeerHostRecord, accessToken: string, scope: HostScope) => Promise<PeerHostSocket>;
|
|
13
|
+
export declare class PeerHostWsProxyError extends Error {
|
|
14
|
+
readonly code: PeerHostErrorCode;
|
|
15
|
+
constructor(code: PeerHostErrorCode, message: string);
|
|
16
|
+
}
|
|
17
|
+
/** PeerHost WebSocket 双端代理;只转发带完整作用域的白名单消息。 */
|
|
18
|
+
export declare class PeerHostWsProxyService {
|
|
19
|
+
private readonly store;
|
|
20
|
+
private readonly sessions;
|
|
21
|
+
private readonly connectRemote;
|
|
22
|
+
constructor(store: PeerHostStore, sessions: PeerHostSessionService, connectRemote: PeerHostRemoteConnector);
|
|
23
|
+
open(peerHostId: string, client: PeerHostSocket, scope: HostScope): Promise<() => void>;
|
|
24
|
+
private requireReady;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=host-ws-proxy-service.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { PEER_HOST_ERROR_CODES } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
import { PeerHostSessionService } from './peer-host-session.js';
|
|
3
|
+
import { PeerHostStore } from './peer-host-store.js';
|
|
4
|
+
import { PeerHostConnectorError } from './host-ws-connector.js';
|
|
5
|
+
import { peerHostSafeError } from './peer-host-diagnostics.js';
|
|
6
|
+
export const PEER_HOST_WS_CLIENT_MESSAGE_TYPES = new Set([
|
|
7
|
+
'workbench.subscribe', 'workbench.refresh', 'fileTree.subscribe', 'fileTree.refresh',
|
|
8
|
+
'git.subscribe', 'git.refresh', 'session.subscribe', 'session.load_older',
|
|
9
|
+
'session.send', 'session.stop', 'session.permission_reply', 'session.answer',
|
|
10
|
+
'terminal.subscribe', 'terminal.input', 'terminal.resize', 'terminal.close',
|
|
11
|
+
'rightTool.subscribe', 'rightTool.refresh', 'rightTool.close',
|
|
12
|
+
]);
|
|
13
|
+
export const PEER_HOST_WS_REMOTE_MESSAGE_TYPES = new Set([
|
|
14
|
+
'system.connected', 'workbench.snapshot', 'workbench.delta', 'fileTree.snapshot',
|
|
15
|
+
'git.snapshot', 'session.subscribed', 'session.backfill', 'session.delta',
|
|
16
|
+
'session.runtime_message', 'session.runtime_status', 'session.activity',
|
|
17
|
+
'session.permission_request', 'session.error', 'terminal.output', 'terminal.status',
|
|
18
|
+
'terminal.exit', 'terminal.error', 'rightTool.snapshot', 'rightTool.delta',
|
|
19
|
+
]);
|
|
20
|
+
const OPEN = 1;
|
|
21
|
+
const MAX_QUEUE = 128;
|
|
22
|
+
export class PeerHostWsProxyError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = 'PeerHostWsProxyError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** PeerHost WebSocket 双端代理;只转发带完整作用域的白名单消息。 */
|
|
31
|
+
export class PeerHostWsProxyService {
|
|
32
|
+
store;
|
|
33
|
+
sessions;
|
|
34
|
+
connectRemote;
|
|
35
|
+
constructor(store, sessions, connectRemote) {
|
|
36
|
+
this.store = store;
|
|
37
|
+
this.sessions = sessions;
|
|
38
|
+
this.connectRemote = connectRemote;
|
|
39
|
+
}
|
|
40
|
+
async open(peerHostId, client, scope) {
|
|
41
|
+
const record = await this.requireReady(peerHostId);
|
|
42
|
+
assertScopeTarget(scope, peerHostId);
|
|
43
|
+
const accessToken = await this.sessions.getAccessToken(peerHostId);
|
|
44
|
+
let remote;
|
|
45
|
+
try {
|
|
46
|
+
remote = await this.connectRemote(record, accessToken, scope);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof PeerHostConnectorError && error.code === PEER_HOST_ERROR_CODES.SESSION_REQUIRED) {
|
|
50
|
+
await this.sessions.invalidate(peerHostId);
|
|
51
|
+
throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SESSION_REQUIRED, '目标 Host 登录态已失效');
|
|
52
|
+
}
|
|
53
|
+
if (error instanceof PeerHostConnectorError)
|
|
54
|
+
throw new PeerHostWsProxyError(error.code, peerHostSafeError(error.code).message);
|
|
55
|
+
throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.PROXY_UNREACHABLE, '目标 Host 代理连接失败');
|
|
56
|
+
}
|
|
57
|
+
let closed = false;
|
|
58
|
+
const remoteQueue = [];
|
|
59
|
+
const clientQueue = [];
|
|
60
|
+
const closeBoth = (code = 1000, reason = 'peer host proxy closed') => {
|
|
61
|
+
if (closed)
|
|
62
|
+
return;
|
|
63
|
+
closed = true;
|
|
64
|
+
if (client.readyState === OPEN)
|
|
65
|
+
client.close(code, reason);
|
|
66
|
+
if (remote.readyState === OPEN)
|
|
67
|
+
remote.close(code, reason);
|
|
68
|
+
};
|
|
69
|
+
const sendClientError = (code, message) => {
|
|
70
|
+
if (client.readyState === OPEN)
|
|
71
|
+
client.send(JSON.stringify({ type: 'peerHost.error', ...scope, error_code: code, message }));
|
|
72
|
+
};
|
|
73
|
+
const forward = (socket, raw, queue) => {
|
|
74
|
+
if (socket.readyState !== OPEN) {
|
|
75
|
+
if (queue.length >= MAX_QUEUE) {
|
|
76
|
+
queue.shift();
|
|
77
|
+
sendClientError(PEER_HOST_ERROR_CODES.PROXY_UNREACHABLE, 'PeerHost 实时消息队列已满');
|
|
78
|
+
}
|
|
79
|
+
queue.push(raw);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
socket.send(raw);
|
|
83
|
+
};
|
|
84
|
+
const flush = () => {
|
|
85
|
+
while (remoteQueue.length > 0 && remote.readyState === OPEN)
|
|
86
|
+
remote.send(remoteQueue.shift());
|
|
87
|
+
while (clientQueue.length > 0 && client.readyState === OPEN)
|
|
88
|
+
client.send(clientQueue.shift());
|
|
89
|
+
};
|
|
90
|
+
client.on('message', (data, isBinary) => {
|
|
91
|
+
if (isBinary === true || typeof data !== 'string') {
|
|
92
|
+
sendClientError(PEER_HOST_ERROR_CODES.PROXY_PATH_NOT_ALLOWED, 'PeerHost WebSocket 不支持二进制消息');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const parsed = parseScopedMessage(data, scope, PEER_HOST_WS_CLIENT_MESSAGE_TYPES);
|
|
96
|
+
if (parsed.error !== null) {
|
|
97
|
+
sendClientError(parsed.error.code, parsed.error.message);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
forward(remote, data, remoteQueue);
|
|
101
|
+
});
|
|
102
|
+
remote.on('message', (data, isBinary) => {
|
|
103
|
+
if (isBinary === true || typeof data !== 'string') {
|
|
104
|
+
sendClientError(PEER_HOST_ERROR_CODES.RESPONSE_INVALID, 'PeerHost 目标消息格式无效');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const parsed = parseScopedMessage(data, scope, PEER_HOST_WS_REMOTE_MESSAGE_TYPES);
|
|
108
|
+
if (parsed.error !== null) {
|
|
109
|
+
sendClientError(parsed.error.code, parsed.error.message);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
forward(client, data, clientQueue);
|
|
113
|
+
});
|
|
114
|
+
client.on('close', () => closeBoth());
|
|
115
|
+
remote.on('close', () => closeBoth(1011, 'target peer host closed'));
|
|
116
|
+
client.on('error', () => closeBoth(1011, 'client socket failed'));
|
|
117
|
+
remote.on('error', () => closeBoth(1011, 'target peer host failed'));
|
|
118
|
+
flush();
|
|
119
|
+
return () => closeBoth();
|
|
120
|
+
}
|
|
121
|
+
async requireReady(peerHostId) {
|
|
122
|
+
const record = await this.store.get(peerHostId);
|
|
123
|
+
if (record === null)
|
|
124
|
+
throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.NOT_FOUND, 'PeerHost 不存在');
|
|
125
|
+
if (record.status !== 'ready')
|
|
126
|
+
throw new PeerHostWsProxyError(record.status === 'session_required' ? PEER_HOST_ERROR_CODES.SESSION_REQUIRED : PEER_HOST_ERROR_CODES.NOT_READY, 'PeerHost 尚未准备好实时代理');
|
|
127
|
+
if (record.route.kind !== 'lan')
|
|
128
|
+
throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.RELAY_UNAVAILABLE, '中转 PeerHost 暂不可用');
|
|
129
|
+
return record;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function assertScopeTarget(scope, peerHostId) {
|
|
133
|
+
if (!scope.hostId || scope.targetHostId !== peerHostId || !scope.workspaceId || !Number.isSafeInteger(scope.scopeGeneration) || scope.scopeGeneration < 0)
|
|
134
|
+
throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 作用域不匹配');
|
|
135
|
+
}
|
|
136
|
+
function parseScopedMessage(raw, expected, allowed) {
|
|
137
|
+
let value;
|
|
138
|
+
try {
|
|
139
|
+
value = JSON.parse(raw);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return { error: new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.RESPONSE_INVALID, 'PeerHost WebSocket 消息不是 JSON') };
|
|
143
|
+
}
|
|
144
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
145
|
+
return { error: new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.RESPONSE_INVALID, 'PeerHost WebSocket 消息格式无效') };
|
|
146
|
+
const message = value;
|
|
147
|
+
if (typeof message.type !== 'string' || !allowed.has(message.type))
|
|
148
|
+
return { error: new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.TOOL_UNSUPPORTED, 'PeerHost WebSocket 消息未加入白名单') };
|
|
149
|
+
if (requiresSession(message.type) && (typeof message.sessionId !== 'string' || message.sessionId.trim() === ''))
|
|
150
|
+
return { error: new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost 会话消息缺少 sessionId') };
|
|
151
|
+
const messageSessionId = typeof message.sessionId === 'string' && message.sessionId.trim() !== '' ? message.sessionId : null;
|
|
152
|
+
if (message.hostId !== expected.hostId || message.targetHostId !== expected.targetHostId || message.workspaceId !== expected.workspaceId || message.scopeGeneration !== expected.scopeGeneration || messageSessionId !== expected.sessionId)
|
|
153
|
+
return { error: new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 消息作用域不匹配') };
|
|
154
|
+
return { error: null };
|
|
155
|
+
}
|
|
156
|
+
function requiresSession(type) {
|
|
157
|
+
return type.startsWith('session.');
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=host-ws-proxy-service.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AggregateHostResult } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
export interface AggregateSessionSource {
|
|
3
|
+
readonly sessionId: string;
|
|
4
|
+
readonly title: string;
|
|
5
|
+
readonly status: string;
|
|
6
|
+
readonly updatedAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface AggregateWorkspaceSource {
|
|
9
|
+
readonly workspaceId: string;
|
|
10
|
+
readonly displayName: string;
|
|
11
|
+
readonly sessions: readonly AggregateSessionSource[];
|
|
12
|
+
}
|
|
13
|
+
export interface AggregateHostSource {
|
|
14
|
+
readonly hostId: string;
|
|
15
|
+
readonly targetHostId: string | null;
|
|
16
|
+
readonly hostLabel: string;
|
|
17
|
+
readonly load: () => Promise<readonly AggregateWorkspaceSource[]>;
|
|
18
|
+
/** DSH 未提供稳定摘要来源时显式声明降级,不把失败折叠成空数组。 */
|
|
19
|
+
readonly capability?: {
|
|
20
|
+
readonly available: boolean;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 当前 Host 的 workspace/session 摘要来源契约。
|
|
26
|
+
*
|
|
27
|
+
* DSH 版本之间的 SessionStore/WorkspaceController 不是稳定公共 API,聚合层
|
|
28
|
+
* 不直接读取未知对象。Host 只有在完成结构探测后,才能注入这个 source。
|
|
29
|
+
*/
|
|
30
|
+
export interface PeerHostWorkspaceSessionSummarySource {
|
|
31
|
+
readonly capabilityId: 'peer-host.native-workspace-session-summary';
|
|
32
|
+
readonly available: boolean;
|
|
33
|
+
readonly reason?: string;
|
|
34
|
+
load(signal?: AbortSignal): Promise<readonly AggregateWorkspaceSource[]>;
|
|
35
|
+
}
|
|
36
|
+
/** 将显式 source 转为聚合任务;不可用 source 保留 unsupported 诊断。 */
|
|
37
|
+
export declare function createAggregateHostSource(input: {
|
|
38
|
+
readonly hostId: string;
|
|
39
|
+
readonly targetHostId: string | null;
|
|
40
|
+
readonly hostLabel: string;
|
|
41
|
+
readonly source: PeerHostWorkspaceSessionSummarySource;
|
|
42
|
+
}): AggregateHostSource;
|
|
43
|
+
/** 并发加载多 Host 摘要;单个目标失败只生成该 Host 的错误节点。 */
|
|
44
|
+
export declare class PeerHostAggregateService {
|
|
45
|
+
private readonly timeoutMs;
|
|
46
|
+
constructor(timeoutMs?: number);
|
|
47
|
+
load(sources: readonly AggregateHostSource[]): Promise<readonly AggregateHostResult[]>;
|
|
48
|
+
private loadOne;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=peer-host-aggregate-service.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** 将显式 source 转为聚合任务;不可用 source 保留 unsupported 诊断。 */
|
|
2
|
+
export function createAggregateHostSource(input) {
|
|
3
|
+
const source = input.source;
|
|
4
|
+
return {
|
|
5
|
+
hostId: input.hostId,
|
|
6
|
+
targetHostId: input.targetHostId,
|
|
7
|
+
hostLabel: input.hostLabel,
|
|
8
|
+
capability: {
|
|
9
|
+
available: source.available,
|
|
10
|
+
reason: source.reason?.trim() || (source.available ? '摘要 source 已就绪' : 'Host 未提供稳定 workspace/session 摘要 source'),
|
|
11
|
+
},
|
|
12
|
+
load: () => source.load(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** 并发加载多 Host 摘要;单个目标失败只生成该 Host 的错误节点。 */
|
|
16
|
+
export class PeerHostAggregateService {
|
|
17
|
+
timeoutMs;
|
|
18
|
+
constructor(timeoutMs = 5_000) {
|
|
19
|
+
this.timeoutMs = timeoutMs;
|
|
20
|
+
}
|
|
21
|
+
async load(sources) {
|
|
22
|
+
return Promise.all(sources.map((source) => this.loadOne(source)));
|
|
23
|
+
}
|
|
24
|
+
async loadOne(source) {
|
|
25
|
+
if (source.capability?.available === false) {
|
|
26
|
+
return {
|
|
27
|
+
hostId: source.hostId,
|
|
28
|
+
targetHostId: source.targetHostId,
|
|
29
|
+
hostLabel: source.hostLabel,
|
|
30
|
+
availability: 'unsupported',
|
|
31
|
+
errorCode: null,
|
|
32
|
+
diagnostic: source.capability.reason,
|
|
33
|
+
workspaces: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const workspaces = await withTimeout(source.load(), this.timeoutMs);
|
|
38
|
+
return {
|
|
39
|
+
hostId: source.hostId,
|
|
40
|
+
targetHostId: source.targetHostId,
|
|
41
|
+
hostLabel: source.hostLabel,
|
|
42
|
+
availability: 'ready',
|
|
43
|
+
errorCode: null,
|
|
44
|
+
workspaces: workspaces.map((workspace) => toWorkspace(source, workspace)),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
hostId: source.hostId,
|
|
50
|
+
targetHostId: source.targetHostId,
|
|
51
|
+
hostLabel: source.hostLabel,
|
|
52
|
+
availability: 'unreachable',
|
|
53
|
+
errorCode: 'PEER_HOST_UNREACHABLE',
|
|
54
|
+
workspaces: [],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function toWorkspace(source, workspace) {
|
|
60
|
+
return {
|
|
61
|
+
key: `${source.hostId}:${workspace.workspaceId}`,
|
|
62
|
+
hostId: source.hostId,
|
|
63
|
+
targetHostId: source.targetHostId,
|
|
64
|
+
workspaceId: workspace.workspaceId,
|
|
65
|
+
displayName: workspace.displayName,
|
|
66
|
+
hostLabel: source.hostLabel,
|
|
67
|
+
availability: 'ready',
|
|
68
|
+
sessions: workspace.sessions.map((session) => ({
|
|
69
|
+
scope: {
|
|
70
|
+
hostId: source.hostId,
|
|
71
|
+
targetHostId: source.targetHostId,
|
|
72
|
+
workspaceId: workspace.workspaceId,
|
|
73
|
+
sessionId: session.sessionId,
|
|
74
|
+
scopeGeneration: 0,
|
|
75
|
+
},
|
|
76
|
+
title: session.title,
|
|
77
|
+
status: session.status,
|
|
78
|
+
updatedAt: session.updatedAt,
|
|
79
|
+
})),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function withTimeout(promise, timeoutMs) {
|
|
83
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
84
|
+
throw new TypeError('摘要超时时间必须为正数');
|
|
85
|
+
let timer;
|
|
86
|
+
try {
|
|
87
|
+
return await Promise.race([
|
|
88
|
+
promise,
|
|
89
|
+
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('PeerHost 摘要超时')), timeoutMs); }),
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
if (timer !== undefined)
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=peer-host-aggregate-service.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PeerHostDiagnosticSnapshot, PeerHostErrorCode, PeerHostRecord } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
export type { PeerHostDiagnosticSnapshot } from '../../../shared/contracts/peer-host.js';
|
|
3
|
+
export interface PeerHostDiagnosticSink {
|
|
4
|
+
readonly enabled?: boolean;
|
|
5
|
+
emit(event: string, snapshot: PeerHostDiagnosticSnapshot): void;
|
|
6
|
+
}
|
|
7
|
+
/** 将内部错误转换成稳定错误码和固定文案,禁止回显底层异常正文。 */
|
|
8
|
+
export declare function peerHostSafeError(code: PeerHostErrorCode): Error & {
|
|
9
|
+
readonly code: PeerHostErrorCode;
|
|
10
|
+
};
|
|
11
|
+
/** 对外诊断字段只保留脱敏 fingerprint;完整地址、ticket 和凭据永远不进入快照。 */
|
|
12
|
+
export declare function toPeerHostDiagnosticSnapshot(record: PeerHostRecord): PeerHostDiagnosticSnapshot;
|
|
13
|
+
export declare function redactFingerprint(value: string | null): string | null;
|
|
14
|
+
/** 默认关闭;即使开启,也只向 sink 发送已验证的诊断快照。 */
|
|
15
|
+
export declare function createPeerHostDiagnosticSink(options?: {
|
|
16
|
+
readonly enabled?: boolean;
|
|
17
|
+
readonly sink?: (event: string, snapshot: PeerHostDiagnosticSnapshot) => void;
|
|
18
|
+
}): PeerHostDiagnosticSink;
|
|
19
|
+
//# sourceMappingURL=peer-host-diagnostics.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { PEER_HOST_ERROR_CODES } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
const DEFAULT_MESSAGES = {
|
|
3
|
+
[PEER_HOST_ERROR_CODES.NOT_FOUND]: 'PeerHost 不存在',
|
|
4
|
+
[PEER_HOST_ERROR_CODES.NOT_READY]: 'PeerHost 尚未准备好',
|
|
5
|
+
[PEER_HOST_ERROR_CODES.SESSION_REQUIRED]: '目标 Host 登录态已失效',
|
|
6
|
+
[PEER_HOST_ERROR_CODES.PROXY_PATH_NOT_ALLOWED]: 'PeerHost 代理路径或方法不受支持',
|
|
7
|
+
[PEER_HOST_ERROR_CODES.SCOPE_MISMATCH]: 'PeerHost 作用域不匹配',
|
|
8
|
+
[PEER_HOST_ERROR_CODES.PROXY_UNREACHABLE]: '目标 Host 代理不可达',
|
|
9
|
+
[PEER_HOST_ERROR_CODES.RESPONSE_INVALID]: '目标 Host 响应无效',
|
|
10
|
+
[PEER_HOST_ERROR_CODES.TOOL_UNSUPPORTED]: '目标 Host 工具不受支持',
|
|
11
|
+
[PEER_HOST_ERROR_CODES.INVALID_ROUTE]: 'PeerHost 路由无效',
|
|
12
|
+
[PEER_HOST_ERROR_CODES.DUPLICATE]: 'PeerHost 路由已存在',
|
|
13
|
+
[PEER_HOST_ERROR_CODES.PLUGIN_MISSING]: '目标 Host 未安装兼容插件',
|
|
14
|
+
[PEER_HOST_ERROR_CODES.VERSION_MISMATCH]: '目标 Host 版本不兼容',
|
|
15
|
+
[PEER_HOST_ERROR_CODES.IDENTITY_CHANGED]: '目标 Host 身份已变化',
|
|
16
|
+
[PEER_HOST_ERROR_CODES.UNREACHABLE]: '目标 Host 不可达',
|
|
17
|
+
[PEER_HOST_ERROR_CODES.RELAY_UNAVAILABLE]: '中转 PeerHost 暂不可用',
|
|
18
|
+
[PEER_HOST_ERROR_CODES.STALE_GENERATION]: 'PeerHost 作用域已过期',
|
|
19
|
+
[PEER_HOST_ERROR_CODES.AGGREGATE_UNAVAILABLE]: 'PeerHost 摘要暂不可用',
|
|
20
|
+
};
|
|
21
|
+
/** 将内部错误转换成稳定错误码和固定文案,禁止回显底层异常正文。 */
|
|
22
|
+
export function peerHostSafeError(code) {
|
|
23
|
+
const error = new Error(DEFAULT_MESSAGES[code] ?? 'PeerHost 请求失败');
|
|
24
|
+
Object.defineProperty(error, 'code', { value: code, enumerable: true });
|
|
25
|
+
return error;
|
|
26
|
+
}
|
|
27
|
+
/** 对外诊断字段只保留脱敏 fingerprint;完整地址、ticket 和凭据永远不进入快照。 */
|
|
28
|
+
export function toPeerHostDiagnosticSnapshot(record) {
|
|
29
|
+
return {
|
|
30
|
+
peerHostId: record.id,
|
|
31
|
+
routeKind: record.route.kind,
|
|
32
|
+
status: record.status,
|
|
33
|
+
lastErrorCode: record.lastErrorCode,
|
|
34
|
+
lastCheckedAt: record.lastCheckedAt,
|
|
35
|
+
fingerprint: redactFingerprint(record.fingerprint),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function redactFingerprint(value) {
|
|
39
|
+
if (value === null || value.length <= 12)
|
|
40
|
+
return value;
|
|
41
|
+
return `${value.slice(0, 8)}...${value.slice(-4)}`;
|
|
42
|
+
}
|
|
43
|
+
/** 默认关闭;即使开启,也只向 sink 发送已验证的诊断快照。 */
|
|
44
|
+
export function createPeerHostDiagnosticSink(options = {}) {
|
|
45
|
+
return {
|
|
46
|
+
enabled: options.enabled ?? false,
|
|
47
|
+
emit(event, snapshot) {
|
|
48
|
+
if (!(options.enabled ?? false))
|
|
49
|
+
return;
|
|
50
|
+
options.sink?.(event, snapshot);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=peer-host-diagnostics.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PeerHostErrorCode, PeerHostRecord } from '../../../shared/contracts/peer-host.js';
|
|
2
|
+
import { PeerHostStore } from './peer-host-store.js';
|
|
3
|
+
export declare const PEER_HOST_HANDSHAKE_PATH = "/api/public/host-handshake";
|
|
4
|
+
export interface PeerHostHandshakePayload {
|
|
5
|
+
readonly productId: string;
|
|
6
|
+
readonly pluginId: string | null;
|
|
7
|
+
readonly pluginVersion: string | null;
|
|
8
|
+
readonly dshVersion: string;
|
|
9
|
+
readonly apiCompatibility: string;
|
|
10
|
+
readonly fingerprint: string | null;
|
|
11
|
+
readonly capabilities: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface PeerHostHandshakeOptions {
|
|
14
|
+
readonly productId: string;
|
|
15
|
+
readonly pluginId: string;
|
|
16
|
+
readonly pluginVersion: string;
|
|
17
|
+
readonly apiCompatibility: string;
|
|
18
|
+
readonly isDshVersionSupported: (version: string) => boolean;
|
|
19
|
+
readonly fetchImpl?: typeof fetch;
|
|
20
|
+
readonly timeoutMs?: number;
|
|
21
|
+
readonly now?: () => number;
|
|
22
|
+
}
|
|
23
|
+
/** 目标 Host 握手协调器;只把脱敏结果写回 PeerHostRecord。 */
|
|
24
|
+
export declare class PeerHostHandshakeService {
|
|
25
|
+
private readonly store;
|
|
26
|
+
private readonly credentials;
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly fetchImpl;
|
|
29
|
+
private readonly timeoutMs;
|
|
30
|
+
private readonly now;
|
|
31
|
+
constructor(store: PeerHostStore, credentials: {
|
|
32
|
+
clear(peerHostId: string): Promise<void>;
|
|
33
|
+
}, options: PeerHostHandshakeOptions);
|
|
34
|
+
check(peerHostId: string): Promise<PeerHostRecord>;
|
|
35
|
+
private fetchHandshake;
|
|
36
|
+
private resolve;
|
|
37
|
+
private emptyUpdate;
|
|
38
|
+
private requireRecord;
|
|
39
|
+
}
|
|
40
|
+
export declare class HandshakeError extends Error {
|
|
41
|
+
readonly code: PeerHostErrorCode;
|
|
42
|
+
constructor(code: PeerHostErrorCode, message: string);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=peer-host-handshake.d.ts.map
|