@ntdgy/dsh-remote-bridge-plugin 0.0.0-stage → 0.1.0
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/cordis.patch.yml +28 -0
- package/lib/client.js +336 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +106 -0
- package/lib/index.js +535 -0
- package/lib/index.js.map +1 -0
- package/lib/local/sessions.d.ts +52 -0
- package/lib/local/sessions.js +25 -0
- package/lib/local/sessions.js.map +1 -0
- package/lib/relay/client.d.ts +262 -0
- package/lib/relay/client.js +825 -0
- package/lib/relay/client.js.map +1 -0
- package/lib/relay/dispatcher.d.ts +74 -0
- package/lib/relay/dispatcher.js +192 -0
- package/lib/relay/dispatcher.js.map +1 -0
- package/lib/relay/errors.d.ts +37 -0
- package/lib/relay/errors.js +71 -0
- package/lib/relay/errors.js.map +1 -0
- package/lib/relay/follow.d.ts +176 -0
- package/lib/relay/follow.js +307 -0
- package/lib/relay/follow.js.map +1 -0
- package/lib/relay/http-forward.d.ts +30 -0
- package/lib/relay/http-forward.js +183 -0
- package/lib/relay/http-forward.js.map +1 -0
- package/lib/relay/route-observer.d.ts +66 -0
- package/lib/relay/route-observer.js +122 -0
- package/lib/relay/route-observer.js.map +1 -0
- package/lib/relay/ws-forward.d.ts +28 -0
- package/lib/relay/ws-forward.js +138 -0
- package/lib/relay/ws-forward.js.map +1 -0
- package/lib/types/client/StatusPanel.d.ts +36 -0
- package/lib/types/client/index.d.ts +27 -0
- package/lib/types/client/types.d.ts +40 -0
- package/package.json +68 -4
- package/README.md +0 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 运行时路由观测:读取 DSH webServer 上**已注册**的 HTTP 路径,归并成前缀后上报网关。
|
|
3
|
+
*
|
|
4
|
+
* 为什么观测而不是推断:包名与路由常常不一致(dshmarket 的路由是 /dsh-market/,
|
|
5
|
+
* 不是 /dshmarket/)。推断猜错了与猜对了在日志上无法区分,运维没法判断该不该信 ——
|
|
6
|
+
* 而观测到的是**真实存在**的路由,可审计。
|
|
7
|
+
*
|
|
8
|
+
* 技术依据(dsh-host-webserver 0.2.1-rc.2,lib/index.js:267-269):
|
|
9
|
+
* exact = new Map(); prefixes = new Map(); upgrades = new Map();
|
|
10
|
+
* 三者都是**类字段初始化的普通 Map**(非 #private),运行时可直接遍历。
|
|
11
|
+
* register()(:326-333)就是往 exact/prefixes 里 set。
|
|
12
|
+
*
|
|
13
|
+
* 时机:hello 发生在运行时、晚于所有插件注册,取到的正是完整快照。
|
|
14
|
+
*/
|
|
15
|
+
/** 单条前缀长度上限。 */
|
|
16
|
+
export const MAX_PREFIX_LENGTH = 64;
|
|
17
|
+
/** 单实例上报条数上限。 */
|
|
18
|
+
export const MAX_PREFIXES_PER_INSTANCE = 32;
|
|
19
|
+
/** 把任意值描述成「类型(条数)」形式的短串,用于诊断日志。 */
|
|
20
|
+
function describeTable(value) {
|
|
21
|
+
if (value instanceof Map)
|
|
22
|
+
return 'Map(' + value.size + ')';
|
|
23
|
+
if (Array.isArray(value))
|
|
24
|
+
return 'Array(' + value.length + ')';
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return 'undefined';
|
|
27
|
+
if (value === null)
|
|
28
|
+
return 'null';
|
|
29
|
+
return typeof value;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 校验单条前缀。
|
|
33
|
+
*
|
|
34
|
+
* 拒绝规则(任何一条命中即拒):
|
|
35
|
+
* - 不以 '/' 开头或不以 '/' 结尾(前缀必须是目录形式,避免 /ab 误匹配 /abc)
|
|
36
|
+
* - 含 '..'(路径穿越)、':'(协议)、'*'(通配)
|
|
37
|
+
* - 长度超过 MAX_PREFIX_LENGTH
|
|
38
|
+
* - 含空白或控制字符
|
|
39
|
+
*/
|
|
40
|
+
export function validatePrefix(raw) {
|
|
41
|
+
if (typeof raw !== 'string')
|
|
42
|
+
return { ok: false, reason: 'not a string' };
|
|
43
|
+
const value = raw;
|
|
44
|
+
if (value.length === 0)
|
|
45
|
+
return { ok: false, reason: 'empty' };
|
|
46
|
+
if (value.length > MAX_PREFIX_LENGTH)
|
|
47
|
+
return { ok: false, reason: 'too long (>' + MAX_PREFIX_LENGTH + ')' };
|
|
48
|
+
if (!value.startsWith('/'))
|
|
49
|
+
return { ok: false, reason: 'must start with /' };
|
|
50
|
+
if (!value.endsWith('/'))
|
|
51
|
+
return { ok: false, reason: 'must end with /' };
|
|
52
|
+
if (value.includes('..'))
|
|
53
|
+
return { ok: false, reason: 'contains ..' };
|
|
54
|
+
if (value.includes(':'))
|
|
55
|
+
return { ok: false, reason: 'contains :' };
|
|
56
|
+
if (value.includes('*'))
|
|
57
|
+
return { ok: false, reason: 'contains *' };
|
|
58
|
+
if (/[\s\u0000-\u001f]/.test(value))
|
|
59
|
+
return { ok: false, reason: 'contains whitespace/control chars' };
|
|
60
|
+
return { ok: true, value };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 从一条完整路径推出它的前缀。
|
|
64
|
+
*
|
|
65
|
+
* 规则:取第一段(/dsh-market/status → /dsh-market/)。
|
|
66
|
+
* 对根级路径(/favicon.svg)返回 undefined —— 根级资源不构成可放行前缀。
|
|
67
|
+
*/
|
|
68
|
+
export function prefixOf(pathname) {
|
|
69
|
+
if (!pathname.startsWith('/'))
|
|
70
|
+
return undefined;
|
|
71
|
+
const rest = pathname.slice(1);
|
|
72
|
+
const slash = rest.indexOf('/');
|
|
73
|
+
if (slash <= 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
return '/' + rest.slice(0, slash) + '/';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 读取 webServer 已注册路由,归并成前缀集合。
|
|
79
|
+
*
|
|
80
|
+
* @param webServer DSH 的 webServer 服务实例(可以是 undefined,表示该 profile 没有 web 面)
|
|
81
|
+
*/
|
|
82
|
+
export function collectRegisteredPrefixes(webServer) {
|
|
83
|
+
const rejected = [];
|
|
84
|
+
if (!webServer || typeof webServer !== 'object') {
|
|
85
|
+
return {
|
|
86
|
+
accepted: [],
|
|
87
|
+
rejected,
|
|
88
|
+
observed: 0,
|
|
89
|
+
diagnostic: 'webServer=' + (webServer === undefined ? 'undefined' : String(webServer)),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const tables = webServer;
|
|
93
|
+
const diagnostic = 'webServer=present, exact=' + describeTable(tables.exact) +
|
|
94
|
+
', prefixes=' + describeTable(tables.prefixes) +
|
|
95
|
+
', upgrades=' + describeTable(tables.upgrades);
|
|
96
|
+
const paths = [];
|
|
97
|
+
for (const table of [tables.exact, tables.prefixes, tables.upgrades]) {
|
|
98
|
+
if (!(table instanceof Map))
|
|
99
|
+
continue;
|
|
100
|
+
for (const key of table.keys())
|
|
101
|
+
if (typeof key === 'string')
|
|
102
|
+
paths.push(key);
|
|
103
|
+
}
|
|
104
|
+
const prefixes = new Set();
|
|
105
|
+
for (const pathname of paths) {
|
|
106
|
+
const prefix = prefixOf(pathname);
|
|
107
|
+
if (prefix === undefined)
|
|
108
|
+
continue;
|
|
109
|
+
const verdict = validatePrefix(prefix);
|
|
110
|
+
if (!verdict.ok) {
|
|
111
|
+
rejected.push({ value: prefix, reason: verdict.reason });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
prefixes.add(verdict.value);
|
|
115
|
+
}
|
|
116
|
+
const accepted = Array.from(prefixes).slice(0, MAX_PREFIXES_PER_INSTANCE);
|
|
117
|
+
if (prefixes.size > MAX_PREFIXES_PER_INSTANCE) {
|
|
118
|
+
rejected.push({ value: '(overflow)', reason: 'more than ' + MAX_PREFIXES_PER_INSTANCE + ' prefixes; truncated' });
|
|
119
|
+
}
|
|
120
|
+
return { accepted, rejected, observed: paths.length, diagnostic };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=route-observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-observer.js","sourceRoot":"","sources":["../../src/relay/route-observer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { WsOpenFrame } from '@ntdgy/dsh-remote-proto';
|
|
2
|
+
/** 单条上游 WS 的最大缓冲,超过即认为消费端不可救,主动关闭。 */
|
|
3
|
+
export declare const UPSTREAM_HARD_LIMIT: number;
|
|
4
|
+
export interface WsSink {
|
|
5
|
+
/** 回 ws-open-ack。 */
|
|
6
|
+
ack(id: string, ok: boolean, error?: string): void;
|
|
7
|
+
/** 回一条上游数据帧。 */
|
|
8
|
+
data(id: string, opcode: number, payload: Uint8Array): void;
|
|
9
|
+
/** 回 ws-close。 */
|
|
10
|
+
close(id: string, code: number, reason: string, from: 'browser' | 'upstream' | 'gateway'): void;
|
|
11
|
+
}
|
|
12
|
+
export declare class WsForwarder {
|
|
13
|
+
private readonly upstreams;
|
|
14
|
+
private readonly origin;
|
|
15
|
+
private readonly sink;
|
|
16
|
+
constructor(origin: () => string | undefined, sink: WsSink);
|
|
17
|
+
/** 发起上游 upgrade。任何失败都必须 ack(false),绝不让网关干等。 */
|
|
18
|
+
open(frame: WsOpenFrame): void;
|
|
19
|
+
/** 网关→上游 的数据。 */
|
|
20
|
+
toUpstream(id: string, opcode: number, payload: Uint8Array): void;
|
|
21
|
+
/** 流量控制:暂停/恢复从上游读取,避免慢消费者把内存撑爆。 */
|
|
22
|
+
setFlow(id: string, action: 'pause' | 'resume'): void;
|
|
23
|
+
/** 网关要求关闭。 */
|
|
24
|
+
closeUpstream(id: string, code: number, reason: string): void;
|
|
25
|
+
/** 连接整体断开:清掉所有上游,不留半开。 */
|
|
26
|
+
closeAll(): void;
|
|
27
|
+
get size(): number;
|
|
28
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WS 升级转发执行器(ADR-005,T34):把网关转来的升级请求打到本机 DSH。
|
|
3
|
+
*
|
|
4
|
+
* 与 HTTP 转发同一条出站连接(C7),只是这里要维持一条长连接并双向搬帧。
|
|
5
|
+
* Host 必须原样保留(理由同 T33:DSH 的 cookie 绑它)。
|
|
6
|
+
*/
|
|
7
|
+
import WebSocket from 'ws';
|
|
8
|
+
import { encodeWsDataFrame, WS_OPCODE_BINARY, WS_OPCODE_TEXT } from '@ntdgy/dsh-remote-proto';
|
|
9
|
+
/** 单条上游 WS 的最大缓冲,超过即认为消费端不可救,主动关闭。 */
|
|
10
|
+
export const UPSTREAM_HARD_LIMIT = 32 * 1024 * 1024;
|
|
11
|
+
export class WsForwarder {
|
|
12
|
+
upstreams = new Map();
|
|
13
|
+
origin;
|
|
14
|
+
sink;
|
|
15
|
+
constructor(origin, sink) {
|
|
16
|
+
this.origin = origin;
|
|
17
|
+
this.sink = sink;
|
|
18
|
+
}
|
|
19
|
+
/** 发起上游 upgrade。任何失败都必须 ack(false),绝不让网关干等。 */
|
|
20
|
+
open(frame) {
|
|
21
|
+
const base = this.origin();
|
|
22
|
+
if (!base) {
|
|
23
|
+
this.sink.ack(frame.id, false, 'DSH webServer origin unavailable');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let url;
|
|
27
|
+
try {
|
|
28
|
+
url = new URL(frame.path, base);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
this.sink.ack(frame.id, false, 'invalid path');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (url.origin !== new URL(base).origin) {
|
|
35
|
+
this.sink.ack(frame.id, false, 'path escapes upstream origin');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const headers = { ...frame.headers };
|
|
39
|
+
// Host 保留网关透传来的值(cookie 绑它);缺失才回落 loopback。
|
|
40
|
+
if (!headers['host'])
|
|
41
|
+
headers['host'] = url.host;
|
|
42
|
+
const wsUrl = (url.protocol === 'https:' ? 'wss:' : 'ws:') + '//' + url.host + url.pathname + url.search;
|
|
43
|
+
const socket = new WebSocket(wsUrl, { headers });
|
|
44
|
+
const entry = { socket, paused: false };
|
|
45
|
+
this.upstreams.set(frame.id, entry);
|
|
46
|
+
let settled = false;
|
|
47
|
+
const fail = (reason) => {
|
|
48
|
+
if (settled)
|
|
49
|
+
return;
|
|
50
|
+
settled = true;
|
|
51
|
+
this.upstreams.delete(frame.id);
|
|
52
|
+
try {
|
|
53
|
+
socket.terminate();
|
|
54
|
+
}
|
|
55
|
+
catch { /* gone */ }
|
|
56
|
+
this.sink.ack(frame.id, false, reason);
|
|
57
|
+
};
|
|
58
|
+
socket.on('unexpected-response', (_req, res) => {
|
|
59
|
+
// 上游明确拒绝升级(如 401/403):把它作为 ack 失败原因回传。
|
|
60
|
+
fail('upstream rejected upgrade: HTTP ' + res.statusCode);
|
|
61
|
+
});
|
|
62
|
+
socket.on('error', (err) => fail(err.message));
|
|
63
|
+
socket.on('open', () => {
|
|
64
|
+
if (settled)
|
|
65
|
+
return;
|
|
66
|
+
settled = true;
|
|
67
|
+
this.sink.ack(frame.id, true);
|
|
68
|
+
});
|
|
69
|
+
socket.on('message', (data, isBinary) => {
|
|
70
|
+
const payload = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
71
|
+
// 二进制安全:opcode 由 isBinary 还原,payload 原样搬运。
|
|
72
|
+
const opcode = isBinary ? WS_OPCODE_BINARY : WS_OPCODE_TEXT;
|
|
73
|
+
this.sink.data(frame.id, opcode, new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength));
|
|
74
|
+
if (socket.bufferedAmount > UPSTREAM_HARD_LIMIT) {
|
|
75
|
+
this.closeUpstream(frame.id, 1011, 'upstream backpressure');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
socket.on('close', (code, reason) => {
|
|
79
|
+
if (this.upstreams.delete(frame.id)) {
|
|
80
|
+
this.sink.close(frame.id, code, reason.toString() || 'upstream closed', 'upstream');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** 网关→上游 的数据。 */
|
|
85
|
+
toUpstream(id, opcode, payload) {
|
|
86
|
+
const entry = this.upstreams.get(id);
|
|
87
|
+
if (!entry)
|
|
88
|
+
return;
|
|
89
|
+
const s = entry.socket;
|
|
90
|
+
if (s.readyState !== WebSocket.OPEN)
|
|
91
|
+
return;
|
|
92
|
+
if (opcode === WS_OPCODE_TEXT)
|
|
93
|
+
s.send(Buffer.from(payload).toString('utf8'), { binary: false });
|
|
94
|
+
else
|
|
95
|
+
s.send(Buffer.from(payload), { binary: true });
|
|
96
|
+
}
|
|
97
|
+
/** 流量控制:暂停/恢复从上游读取,避免慢消费者把内存撑爆。 */
|
|
98
|
+
setFlow(id, action) {
|
|
99
|
+
const entry = this.upstreams.get(id);
|
|
100
|
+
if (!entry)
|
|
101
|
+
return;
|
|
102
|
+
if (action === 'pause' && !entry.paused) {
|
|
103
|
+
entry.paused = true;
|
|
104
|
+
entry.socket.pause();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (action === 'resume' && entry.paused) {
|
|
108
|
+
entry.paused = false;
|
|
109
|
+
entry.socket.resume();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** 网关要求关闭。 */
|
|
113
|
+
closeUpstream(id, code, reason) {
|
|
114
|
+
const entry = this.upstreams.get(id);
|
|
115
|
+
if (!entry)
|
|
116
|
+
return;
|
|
117
|
+
this.upstreams.delete(id);
|
|
118
|
+
// 1005/1006/1015 是「读到的」哨兵值,不允许写进 close 帧,ws 会抛错带崩进程。
|
|
119
|
+
const safe = code === 1005 || code === 1006 || code === 1015 || !Number.isInteger(code) ? 1000
|
|
120
|
+
: (code >= 1000 && code <= 4999 ? code : 1000);
|
|
121
|
+
try {
|
|
122
|
+
entry.socket.close(safe, reason.slice(0, 120));
|
|
123
|
+
}
|
|
124
|
+
catch { /* gone */ }
|
|
125
|
+
const t = setTimeout(() => { try {
|
|
126
|
+
entry.socket.terminate();
|
|
127
|
+
}
|
|
128
|
+
catch { /* gone */ } }, 5000);
|
|
129
|
+
t.unref?.();
|
|
130
|
+
}
|
|
131
|
+
/** 连接整体断开:清掉所有上游,不留半开。 */
|
|
132
|
+
closeAll() {
|
|
133
|
+
for (const id of Array.from(this.upstreams.keys()))
|
|
134
|
+
this.closeUpstream(id, 1011, 'gateway disconnected');
|
|
135
|
+
}
|
|
136
|
+
get size() { return this.upstreams.size; }
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=ws-forward.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-forward.js","sourceRoot":"","sources":["../../src/relay/ws-forward.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,SAAS,MAAM,IAAI,CAAA;AAC1B,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAG7F,sCAAsC;AACtC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AAgBnD,MAAM,OAAO,WAAW;IACL,SAAS,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEzC,MAAM,CAA0B;IAChC,IAAI,CAAQ;IAE7B,YAAY,MAAgC,EAAE,IAAY;QACxD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,+CAA+C;IAC/C,IAAI,CAAC,KAAkB;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;QAC1B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,kCAAkC,CAAC,CAAA;YAClE,OAAM;QACR,CAAC;QACD,IAAI,GAAQ,CAAA;QACZ,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,cAAc,CAAC,CAAA;YAC9C,OAAM;QACR,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,8BAA8B,CAAC,CAAA;YAC9D,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAA2B,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAA;QAC5D,4CAA4C;QAC5C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAA;QAEhD,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAA;QACxG,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAChD,MAAM,KAAK,GAAe,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;QACnD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;QAEnC,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,IAAI,GAAG,CAAC,MAAc,EAAQ,EAAE;YACpC,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC/B,IAAI,CAAC;gBAAC,MAAM,CAAC,SAAS,EAAE,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACxC,CAAC,CAAA;QAED,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7C,uCAAuC;YACvC,IAAI,CAAC,kCAAkC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAA;QAC3D,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAA;QAC9C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACrB,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;QAC/B,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;YACtC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAmB,CAAC,CAAA;YAC/E,2CAA2C;YAC3C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAA;YAC3D,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAA;YACxG,IAAI,MAAM,CAAC,cAAc,GAAG,mBAAmB,EAAE,CAAC;gBAChD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,uBAAuB,CAAC,CAAA;YAC7D,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAClC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,iBAAiB,EAAE,UAAU,CAAC,CAAA;YACrF,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,iBAAiB;IACjB,UAAU,CAAC,EAAU,EAAE,MAAc,EAAE,OAAmB;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;QACtB,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI;YAAE,OAAM;QAC3C,IAAI,MAAM,KAAK,cAAc;YAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;;YAC1F,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,mCAAmC;IACnC,OAAO,CAAC,EAAU,EAAE,MAA0B;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACxC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;YACnB,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,OAAM;QACR,CAAC;QACD,IAAI,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACxC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAA;YACpB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;QACvB,CAAC;IACH,CAAC;IAED,cAAc;IACd,aAAa,CAAC,EAAU,EAAE,IAAY,EAAE,MAAc;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACzB,qDAAqD;QACrD,MAAM,IAAI,GAAG,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;YAC5F,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,CAAC;YAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QAC3F,CAAC,CAAC,KAAK,EAAE,EAAE,CAAA;IACb,CAAC;IAED,0BAA0B;IAC1B,QAAQ;QACN,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAAE,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,sBAAsB,CAAC,CAAA;IAC1G,CAAC;IAED,IAAI,IAAI,KAAa,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAA,CAAC,CAAC;CAClD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge-plugin 只读状态面板的视图。
|
|
3
|
+
*
|
|
4
|
+
* 全部颜色取自 DSH 的 design token(--dsw-*),**没有任何硬编码十六进制** ——
|
|
5
|
+
* DSH 支持深浅主题切换,写死的颜色在另一套主题下会不可读。
|
|
6
|
+
*
|
|
7
|
+
* 纯只读:没有任何按钮会写状态。
|
|
8
|
+
*/
|
|
9
|
+
import type { StatusOutcome } from './types.ts';
|
|
10
|
+
/** 一行展示项。 */
|
|
11
|
+
export interface StatusRow {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
/** 异常项用警示色,便于一眼看出问题。 */
|
|
15
|
+
bad?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 把一次读取结果转成展示行。
|
|
19
|
+
*
|
|
20
|
+
* 读取失败时**只显示错误**,不显示上一次的快照 ——
|
|
21
|
+
* 否则「relay 已断但面板还显示旧的正常数据」会被误读成一切正常。
|
|
22
|
+
*/
|
|
23
|
+
export declare function rowsOf(outcome: StatusOutcome): StatusRow[];
|
|
24
|
+
/**
|
|
25
|
+
* 面板组件。
|
|
26
|
+
*
|
|
27
|
+
* 颜色全部走 token:
|
|
28
|
+
* 标题 / 值 → --dsw-alias-label-primary
|
|
29
|
+
* 标签列 → --dsw-alias-label-secondary
|
|
30
|
+
* 异常值 → --dsw-alias-state-error-primary
|
|
31
|
+
* 面板底色 → --dsw-specific-sidebar-fill(与侧边栏同底,避免突兀色块)
|
|
32
|
+
* 分隔线 → --dsw-alias-border-l3
|
|
33
|
+
*/
|
|
34
|
+
export declare function StatusPanel(props: {
|
|
35
|
+
outcome: StatusOutcome;
|
|
36
|
+
}): React.ReactElement;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge-plugin 只读状态面板(浏览器侧)。
|
|
3
|
+
*
|
|
4
|
+
* 样式全部取自 DSH 的 design token 与原生 CSS(--dsw-*),不硬编码颜色 ——
|
|
5
|
+
* DSH 支持深浅主题切换,写死的十六进制在另一套主题下会不可读。
|
|
6
|
+
*
|
|
7
|
+
* 入口对齐原生 sidebar 的 .panelRow(插件 / 自动化任务 那一列),逐项照抄数值:
|
|
8
|
+
* box-sizing:border-box; border-radius:var(--dsw-radius-md); min-height:36px;
|
|
9
|
+
* gap:8px; margin:0 2px; padding:7px 8px; line-height:22px; font:inherit; border:none;
|
|
10
|
+
* 悬停 background:var(--dsw-alias-interactive-bg-hover)。
|
|
11
|
+
*
|
|
12
|
+
* 宽度:原生 .panelRow **没有** width 声明 —— 它是 .panelList(column flex)的子项,
|
|
13
|
+
* 默认 stretch 所以撑满。这里用同样的办法:外层 column flex + align-items:stretch,
|
|
14
|
+
* 不写死 100%(写死会在将来加内边距时错位)。
|
|
15
|
+
*
|
|
16
|
+
* 图标:原生用 size:16(IconPanelLeftOutlineRegular 等,实测其 size 参数为 16),
|
|
17
|
+
* viewBox 24、stroke-width 1.5 —— 与 DS 图标集一致。
|
|
18
|
+
*
|
|
19
|
+
* 纯只读:不调用任何写接口。数据来自本插件注册的 GET /bridge-plugin/status。
|
|
20
|
+
*/
|
|
21
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
22
|
+
import { type StatusRow } from './StatusPanel.tsx';
|
|
23
|
+
import type { StatusOutcome } from './types.ts';
|
|
24
|
+
export declare const inject: string[];
|
|
25
|
+
export declare function readStatus(): Promise<StatusOutcome>;
|
|
26
|
+
export declare function apply(ctx: ClientContext): void;
|
|
27
|
+
export type { StatusRow };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 面板状态的数据结构(与 Host 侧 buildStatusPayload 一一对应)。
|
|
3
|
+
*
|
|
4
|
+
* 单独一行类型文件:Host 与浏览器两侧共享同一形状,避免各写一份漂移。
|
|
5
|
+
*/
|
|
6
|
+
export interface BridgeStatus {
|
|
7
|
+
connection: {
|
|
8
|
+
registered: boolean;
|
|
9
|
+
connected: boolean;
|
|
10
|
+
connectedAt: number | null;
|
|
11
|
+
reconnects: number;
|
|
12
|
+
};
|
|
13
|
+
identity: {
|
|
14
|
+
instanceId: string;
|
|
15
|
+
relayUrl: string;
|
|
16
|
+
};
|
|
17
|
+
heartbeat: {
|
|
18
|
+
intervalMs: number | null;
|
|
19
|
+
};
|
|
20
|
+
authFailures: number;
|
|
21
|
+
droppedFrames: number;
|
|
22
|
+
httpPrefixes: {
|
|
23
|
+
reported: string[];
|
|
24
|
+
observed: number;
|
|
25
|
+
};
|
|
26
|
+
lastClose: {
|
|
27
|
+
reason: string | null;
|
|
28
|
+
at: number | null;
|
|
29
|
+
};
|
|
30
|
+
sessionControllerReady: boolean;
|
|
31
|
+
sampledAt: number;
|
|
32
|
+
}
|
|
33
|
+
/** 一次读取的结果。失败也是一等公民:面板要能显示原因。 */
|
|
34
|
+
export type StatusOutcome = {
|
|
35
|
+
kind: 'ok';
|
|
36
|
+
status: BridgeStatus;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'error';
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ntdgy/dsh-remote-bridge-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DSH host-side plugin: outbound registration and narrow RPC server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"import": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./lib/types/client/index.d.ts",
|
|
15
|
+
"default": "./lib/client.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib",
|
|
21
|
+
"cordis.patch.yml"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.client-types.json && tsdown",
|
|
29
|
+
"prepack": "node scripts/prepack.mjs",
|
|
30
|
+
"test": "node --experimental-strip-types --test src/**/*.test.ts"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"engines": {
|
|
34
|
+
"dsh": "^0.2.1-rc.2"
|
|
35
|
+
},
|
|
36
|
+
"bundle": {
|
|
37
|
+
"patch": "./cordis.patch.yml"
|
|
38
|
+
},
|
|
39
|
+
"client": {
|
|
40
|
+
"inject": [
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
42
|
+
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
43
|
+
],
|
|
44
|
+
"platform": "web"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@ntdgy/dsh-remote-proto": "^0.1.0",
|
|
49
|
+
"@deepseek-ai/schemastery": "^3.18.5",
|
|
50
|
+
"ws": "^8.18.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
54
|
+
"@types/node": "^22.19.0",
|
|
55
|
+
"@types/ws": "^8.5.13"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@deepseek-ai/dsh": "^0.2.1-rc.2",
|
|
59
|
+
"react": "^18.2.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"react": {
|
|
63
|
+
"comment": "由宿主 DSH 提供 React 运行时;本包不打包它,否则会装出第二份 React 实例"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20.11.0",
|
|
68
|
+
"dsh": "^0.2.1-rc.2"
|
|
69
|
+
}
|
|
70
|
+
}
|
package/README.md
DELETED