@jingyi0605/codingns4dsh 0.1.2 → 0.1.7-rc.2
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 +232 -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,372 @@
|
|
|
1
|
+
import { buildHostNavigation } from './host-navigation.js';
|
|
2
|
+
export const PEER_HOST_NAVIGATION_ATTRIBUTE = 'data-codingns-peer-host-navigation';
|
|
3
|
+
export const PEER_HOST_SESSION_ATTRIBUTE = 'data-codingns-peer-host-session';
|
|
4
|
+
export const PEER_HOST_STATUS_ATTRIBUTE = 'data-codingns-peer-host-status';
|
|
5
|
+
/** 仅探测,不修改 DOM;供能力诊断和版本 fixture 使用。 */
|
|
6
|
+
export function probePeerHostNativeNavigation(dom) {
|
|
7
|
+
return resolveNavigationState(dom);
|
|
8
|
+
}
|
|
9
|
+
/** 仅探测会话容器,不触发请求。 */
|
|
10
|
+
export function probePeerHostNativeSession(dom) {
|
|
11
|
+
return resolveSessionState(dom);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 将聚合导航挂到 DSH 已声明的 tree 容器。
|
|
15
|
+
*
|
|
16
|
+
* 这里不猜 React Fiber 或版本专属 class。只有同时存在 tree 和至少一个
|
|
17
|
+
* treeitem 才认为原生导航扩展点可用;否则保留明确的降级状态,不创建伪造
|
|
18
|
+
* 的工作区数据。
|
|
19
|
+
*/
|
|
20
|
+
export function startPeerHostNativeNavigation(options) {
|
|
21
|
+
const dom = options.document ?? (typeof document === 'undefined' ? undefined : document);
|
|
22
|
+
let currentState = resolveNavigationState(dom);
|
|
23
|
+
let disposed = false;
|
|
24
|
+
let latestResults = [];
|
|
25
|
+
let overrideState;
|
|
26
|
+
const report = (state) => {
|
|
27
|
+
currentState = state;
|
|
28
|
+
options.onStatus?.(state);
|
|
29
|
+
};
|
|
30
|
+
const render = () => {
|
|
31
|
+
if (disposed || dom === undefined)
|
|
32
|
+
return;
|
|
33
|
+
const state = overrideState ?? resolveNavigationState(dom);
|
|
34
|
+
report(state);
|
|
35
|
+
removeNodes(dom, PEER_HOST_NAVIGATION_ATTRIBUTE);
|
|
36
|
+
// 清理旧版本曾插入的状态节点;降级信息不应残留在 DSH 原生工作区树顶部。
|
|
37
|
+
removeNodes(dom, PEER_HOST_STATUS_ATTRIBUTE);
|
|
38
|
+
if (state.status !== 'ready') {
|
|
39
|
+
// 原生工作区树属于 DSH 宿主;降级信息只通过 controller.state/onStatus
|
|
40
|
+
// 暴露,不能把插件错误节点插入宿主导航顶部,避免污染单 Host UI。
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const tree = findNativeTree(dom);
|
|
44
|
+
if (tree === undefined)
|
|
45
|
+
return;
|
|
46
|
+
const root = dom.createElement('section');
|
|
47
|
+
root.setAttribute(PEER_HOST_NAVIGATION_ATTRIBUTE, '');
|
|
48
|
+
root.setAttribute('aria-label', 'PeerHost 工作区与会话');
|
|
49
|
+
root.style.display = 'contents';
|
|
50
|
+
const navigation = buildHostNavigation(latestResults);
|
|
51
|
+
for (const host of navigation)
|
|
52
|
+
root.appendChild(renderHost(dom, host, options.onSelect));
|
|
53
|
+
tree.appendChild(root);
|
|
54
|
+
};
|
|
55
|
+
const controller = {
|
|
56
|
+
get state() { return currentState; },
|
|
57
|
+
refresh(results) {
|
|
58
|
+
latestResults = results;
|
|
59
|
+
overrideState = undefined;
|
|
60
|
+
render();
|
|
61
|
+
},
|
|
62
|
+
setStatus(state) {
|
|
63
|
+
overrideState = state;
|
|
64
|
+
render();
|
|
65
|
+
},
|
|
66
|
+
dispose() {
|
|
67
|
+
if (disposed)
|
|
68
|
+
return;
|
|
69
|
+
disposed = true;
|
|
70
|
+
if (dom !== undefined) {
|
|
71
|
+
removeNodes(dom, PEER_HOST_NAVIGATION_ATTRIBUTE);
|
|
72
|
+
removeNodes(dom, PEER_HOST_STATUS_ATTRIBUTE);
|
|
73
|
+
removeNodes(dom, PEER_HOST_SESSION_ATTRIBUTE);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
render();
|
|
78
|
+
return controller;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 远端会话的原生三栏适配器。
|
|
82
|
+
*
|
|
83
|
+
* DSH 目前没有公开的 HostScope 会话 store 注入点,因此消息先落到当前
|
|
84
|
+
* conversation 容器中的插件节点。这个节点与原生布局同层渲染,明确标记
|
|
85
|
+
* 来源和作用域;若容器探测失败则报告 degraded,不把 iframe 当作原生 UI。
|
|
86
|
+
*/
|
|
87
|
+
export function startPeerHostNativeSession(options) {
|
|
88
|
+
const dom = options.document ?? (typeof document === 'undefined' ? undefined : document);
|
|
89
|
+
let activeScope = null;
|
|
90
|
+
let subscription;
|
|
91
|
+
let currentState = resolveSessionState(dom);
|
|
92
|
+
let panel;
|
|
93
|
+
const report = (state) => {
|
|
94
|
+
currentState = state;
|
|
95
|
+
options.onStatus?.(state);
|
|
96
|
+
};
|
|
97
|
+
const open = async (scope) => {
|
|
98
|
+
close();
|
|
99
|
+
activeScope = scope;
|
|
100
|
+
const root = findConversationRoot(dom);
|
|
101
|
+
if (root === undefined) {
|
|
102
|
+
report({ status: 'degraded', reason: '未探测到 DSH 原生 conversation 容器' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
panel = createSessionPanel(dom, scope);
|
|
106
|
+
root.prepend(panel);
|
|
107
|
+
panel.appendChild(createChatControls(dom, async (action, body) => {
|
|
108
|
+
try {
|
|
109
|
+
const response = action === 'send'
|
|
110
|
+
? await options.controller.sendMessage(scope, body)
|
|
111
|
+
: action === 'stop'
|
|
112
|
+
? await options.controller.stop(scope)
|
|
113
|
+
: action === 'permission'
|
|
114
|
+
? await options.controller.replyPermission(scope, body)
|
|
115
|
+
: await options.controller.answerQuestion(scope, body);
|
|
116
|
+
if (isActive(scope))
|
|
117
|
+
appendNotice(panel, response.body || '已提交');
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (isActive(scope))
|
|
121
|
+
appendNotice(panel, `操作失败:${error instanceof Error ? error.message : String(error)}`);
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
124
|
+
report({ status: 'ready', reason: '已绑定 PeerHost 原生会话容器' });
|
|
125
|
+
try {
|
|
126
|
+
const response = await options.controller.loadHistory(scope);
|
|
127
|
+
if (!isActive(scope))
|
|
128
|
+
return;
|
|
129
|
+
appendHistory(panel, response.body);
|
|
130
|
+
if (options.socketFactory !== undefined) {
|
|
131
|
+
subscription = await options.controller.subscribe(scope, options.socketFactory, (event) => {
|
|
132
|
+
if (isActive(scope))
|
|
133
|
+
appendEvent(panel, event);
|
|
134
|
+
});
|
|
135
|
+
// 建立连接后立即登记会话订阅;ScopedClient 会在重连后只重放此类幂等订阅。
|
|
136
|
+
subscription.send('session.subscribe');
|
|
137
|
+
panel.appendChild(createToolControls(dom, async (kind, body) => {
|
|
138
|
+
try {
|
|
139
|
+
if (!isActive(scope) || subscription === undefined)
|
|
140
|
+
return;
|
|
141
|
+
if (kind === 'terminal.subscribe')
|
|
142
|
+
subscription.send('terminal.subscribe', parseJsonObject(body));
|
|
143
|
+
else if (kind === 'terminal.input')
|
|
144
|
+
subscription.terminalInput(parseJsonPayload(body, 'data'));
|
|
145
|
+
else if (kind === 'terminal.resize')
|
|
146
|
+
subscription.terminalResize(parseJsonPayload(body, 'cols'));
|
|
147
|
+
else if (kind === 'terminal.close')
|
|
148
|
+
subscription.terminalClose(parseJsonObject(body));
|
|
149
|
+
else if (kind === 'rightTool.subscribe')
|
|
150
|
+
subscription.rightToolSubscribe(parseJsonObject(body));
|
|
151
|
+
else if (kind === 'rightTool.refresh')
|
|
152
|
+
subscription.rightToolRefresh(parseJsonObject(body));
|
|
153
|
+
else
|
|
154
|
+
subscription.rightToolClose(parseJsonObject(body));
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (isActive(scope))
|
|
158
|
+
appendNotice(panel, `工具操作失败:${error instanceof Error ? error.message : String(error)}`);
|
|
159
|
+
}
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
appendNotice(panel, 'PeerHost 实时工具通道不可用,终端和右侧工具保持降级');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (isActive(scope))
|
|
168
|
+
appendNotice(panel, `远端会话加载失败:${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const close = () => {
|
|
172
|
+
subscription?.close();
|
|
173
|
+
subscription = undefined;
|
|
174
|
+
panel?.remove();
|
|
175
|
+
panel = undefined;
|
|
176
|
+
activeScope = null;
|
|
177
|
+
};
|
|
178
|
+
const run = async (operation) => {
|
|
179
|
+
const scope = activeScope;
|
|
180
|
+
if (scope === null)
|
|
181
|
+
throw new Error('尚未选择 PeerHost 会话');
|
|
182
|
+
await operation(scope);
|
|
183
|
+
};
|
|
184
|
+
const api = {
|
|
185
|
+
get state() { return currentState; },
|
|
186
|
+
open,
|
|
187
|
+
close,
|
|
188
|
+
send: (body) => run((scope) => options.controller.sendMessage(scope, body)),
|
|
189
|
+
stop: () => run((scope) => options.controller.stop(scope)),
|
|
190
|
+
replyPermission: (body) => run((scope) => options.controller.replyPermission(scope, body)),
|
|
191
|
+
answerQuestion: (body) => run((scope) => options.controller.answerQuestion(scope, body)),
|
|
192
|
+
};
|
|
193
|
+
report(currentState);
|
|
194
|
+
return api;
|
|
195
|
+
function isActive(scope) {
|
|
196
|
+
return activeScope !== null && activeScope.scopeGeneration === scope.scopeGeneration
|
|
197
|
+
&& activeScope.hostId === scope.hostId && activeScope.targetHostId === scope.targetHostId
|
|
198
|
+
&& activeScope.workspaceId === scope.workspaceId && activeScope.sessionId === scope.sessionId;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function resolveNavigationState(dom) {
|
|
202
|
+
return findNativeTree(dom) === undefined
|
|
203
|
+
? { status: 'degraded', reason: '未探测到 DSH 原生工作区树,PeerHost 导航暂不可用' }
|
|
204
|
+
: { status: 'ready', reason: '已探测到 DSH 原生工作区树' };
|
|
205
|
+
}
|
|
206
|
+
function resolveSessionState(dom) {
|
|
207
|
+
return findConversationRoot(dom) === undefined
|
|
208
|
+
? { status: 'degraded', reason: '未探测到 DSH 原生 conversation 容器' }
|
|
209
|
+
: { status: 'ready', reason: '已探测到 DSH 原生 conversation 容器' };
|
|
210
|
+
}
|
|
211
|
+
function findNativeTree(dom) {
|
|
212
|
+
if (dom === undefined)
|
|
213
|
+
return undefined;
|
|
214
|
+
const trees = [...dom.querySelectorAll('[role="tree"]')];
|
|
215
|
+
return trees.find((tree) => tree.querySelector('[role="treeitem"]') !== null);
|
|
216
|
+
}
|
|
217
|
+
function findConversationRoot(dom) {
|
|
218
|
+
if (dom === undefined)
|
|
219
|
+
return undefined;
|
|
220
|
+
const composer = dom.querySelector('[data-composer-card]');
|
|
221
|
+
if (composer === null)
|
|
222
|
+
return undefined;
|
|
223
|
+
return composer.closest('[data-conversation-content],main') ?? composer.parentElement ?? undefined;
|
|
224
|
+
}
|
|
225
|
+
function renderHost(dom, host, onSelect) {
|
|
226
|
+
const section = dom.createElement('div');
|
|
227
|
+
section.dataset.hostKey = host.key;
|
|
228
|
+
section.dataset.hostAvailability = host.availability;
|
|
229
|
+
section.style.display = 'contents';
|
|
230
|
+
const heading = dom.createElement('div');
|
|
231
|
+
heading.textContent = host.label;
|
|
232
|
+
heading.setAttribute('data-codingns-peer-host-host', '');
|
|
233
|
+
section.appendChild(heading);
|
|
234
|
+
for (const workspace of host.workspaces) {
|
|
235
|
+
const workspaceNode = dom.createElement('div');
|
|
236
|
+
workspaceNode.textContent = workspace.label;
|
|
237
|
+
workspaceNode.setAttribute('data-codingns-peer-host-workspace', workspace.key);
|
|
238
|
+
section.appendChild(workspaceNode);
|
|
239
|
+
for (const session of workspace.sessions) {
|
|
240
|
+
const button = dom.createElement('button');
|
|
241
|
+
button.type = 'button';
|
|
242
|
+
button.textContent = session.title;
|
|
243
|
+
button.dataset.codingnsPeerHostScope = JSON.stringify(session.scope);
|
|
244
|
+
button.setAttribute(PEER_HOST_SESSION_ATTRIBUTE, '');
|
|
245
|
+
button.addEventListener('click', () => { void onSelect?.(session.scope); });
|
|
246
|
+
section.appendChild(button);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return section;
|
|
250
|
+
}
|
|
251
|
+
function createSessionPanel(dom, scope) {
|
|
252
|
+
const panel = dom.createElement('section');
|
|
253
|
+
panel.setAttribute(PEER_HOST_SESSION_ATTRIBUTE, '');
|
|
254
|
+
panel.setAttribute('aria-label', `PeerHost 会话 ${scope.sessionId ?? ''}`);
|
|
255
|
+
panel.dataset.codingnsPeerHostScope = JSON.stringify(scope);
|
|
256
|
+
panel.style.padding = '8px 12px';
|
|
257
|
+
panel.style.borderBottom = '1px solid var(--dsw-alias-border-l2, currentColor)';
|
|
258
|
+
return panel;
|
|
259
|
+
}
|
|
260
|
+
function createChatControls(dom, run) {
|
|
261
|
+
const controls = dom.createElement('div');
|
|
262
|
+
controls.setAttribute('data-codingns-peer-host-chat', '');
|
|
263
|
+
const input = dom.createElement('textarea');
|
|
264
|
+
input.setAttribute('aria-label', 'PeerHost 消息');
|
|
265
|
+
input.rows = 2;
|
|
266
|
+
input.style.width = '100%';
|
|
267
|
+
input.style.resize = 'vertical';
|
|
268
|
+
const send = button(dom, '发送', 'send');
|
|
269
|
+
const stop = button(dom, '停止', 'stop');
|
|
270
|
+
const permission = button(dom, '权限回复', 'permission');
|
|
271
|
+
const question = button(dom, '回答问题', 'question');
|
|
272
|
+
const submit = (action) => {
|
|
273
|
+
const body = input.value.trim();
|
|
274
|
+
if (action !== 'stop' && body === '')
|
|
275
|
+
return;
|
|
276
|
+
if (action !== 'stop')
|
|
277
|
+
input.value = '';
|
|
278
|
+
void run(action, body);
|
|
279
|
+
};
|
|
280
|
+
send.addEventListener('click', () => submit('send'));
|
|
281
|
+
stop.addEventListener('click', () => submit('stop'));
|
|
282
|
+
permission.addEventListener('click', () => submit('permission'));
|
|
283
|
+
question.addEventListener('click', () => submit('question'));
|
|
284
|
+
input.addEventListener('keydown', (event) => {
|
|
285
|
+
if (event.key !== 'Enter' || event.shiftKey || event.isComposing)
|
|
286
|
+
return;
|
|
287
|
+
event.preventDefault();
|
|
288
|
+
submit('send');
|
|
289
|
+
});
|
|
290
|
+
controls.append(input, send, stop, permission, question);
|
|
291
|
+
return controls;
|
|
292
|
+
}
|
|
293
|
+
function button(dom, label, action) {
|
|
294
|
+
const element = dom.createElement('button');
|
|
295
|
+
element.type = 'button';
|
|
296
|
+
element.textContent = label;
|
|
297
|
+
element.dataset.codingnsPeerHostAction = action;
|
|
298
|
+
return element;
|
|
299
|
+
}
|
|
300
|
+
function appendHistory(panel, body) {
|
|
301
|
+
let value;
|
|
302
|
+
try {
|
|
303
|
+
value = JSON.parse(body);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
appendNotice(panel, '远端历史响应不是合法 JSON');
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const records = Array.isArray(value) ? value : isRecord(value) && Array.isArray(value.messages) ? value.messages : [];
|
|
310
|
+
for (const record of records)
|
|
311
|
+
appendRecord(panel, record);
|
|
312
|
+
}
|
|
313
|
+
function appendEvent(panel, event) {
|
|
314
|
+
if (panel === undefined)
|
|
315
|
+
return;
|
|
316
|
+
appendRecord(panel, event);
|
|
317
|
+
}
|
|
318
|
+
function appendRecord(panel, value) {
|
|
319
|
+
const item = panel.ownerDocument.createElement('div');
|
|
320
|
+
item.setAttribute('data-codingns-peer-host-message', '');
|
|
321
|
+
item.textContent = isRecord(value)
|
|
322
|
+
? typeof value.text === 'string' ? value.text : typeof value.content === 'string' ? value.content : JSON.stringify(value)
|
|
323
|
+
: String(value);
|
|
324
|
+
panel.appendChild(item);
|
|
325
|
+
}
|
|
326
|
+
function appendNotice(panel, text) {
|
|
327
|
+
if (panel === undefined)
|
|
328
|
+
return;
|
|
329
|
+
appendRecord(panel, { text });
|
|
330
|
+
}
|
|
331
|
+
function createToolControls(dom, run) {
|
|
332
|
+
const root = dom.createElement('div');
|
|
333
|
+
root.setAttribute('data-codingns-peer-host-tools', '');
|
|
334
|
+
const input = dom.createElement('input');
|
|
335
|
+
input.type = 'text';
|
|
336
|
+
input.setAttribute('aria-label', 'PeerHost 工具参数');
|
|
337
|
+
input.placeholder = '{"terminalId":"...","data":"ls\\n"}';
|
|
338
|
+
const actions = [
|
|
339
|
+
['terminal.subscribe', '订阅终端'], ['terminal.input', '终端输入'], ['terminal.resize', '终端调整'], ['terminal.close', '关闭终端'],
|
|
340
|
+
['rightTool.subscribe', '打开右侧工具'], ['rightTool.refresh', '刷新右侧工具'], ['rightTool.close', '关闭右侧工具'],
|
|
341
|
+
];
|
|
342
|
+
for (const [action, label] of actions) {
|
|
343
|
+
const control = dom.createElement('button');
|
|
344
|
+
control.type = 'button';
|
|
345
|
+
control.textContent = label;
|
|
346
|
+
control.dataset.codingnsPeerHostToolAction = action;
|
|
347
|
+
control.addEventListener('click', () => { void run(action, input.value.trim() || '{}'); });
|
|
348
|
+
root.appendChild(control);
|
|
349
|
+
}
|
|
350
|
+
root.prepend(input);
|
|
351
|
+
return root;
|
|
352
|
+
}
|
|
353
|
+
function parseJsonObject(body) {
|
|
354
|
+
try {
|
|
355
|
+
const value = JSON.parse(body);
|
|
356
|
+
return isRecord(value) ? value : {};
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
return {};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function parseJsonPayload(body, fallbackKey) {
|
|
363
|
+
const value = parseJsonObject(body);
|
|
364
|
+
return Object.keys(value).length > 0 ? value : { [fallbackKey]: body };
|
|
365
|
+
}
|
|
366
|
+
function removeNodes(dom, attribute) {
|
|
367
|
+
dom.querySelectorAll(`[${attribute}]`).forEach((node) => node.remove());
|
|
368
|
+
}
|
|
369
|
+
function isRecord(value) {
|
|
370
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=peer-host-native-session-ui.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { HostScope, PeerHostWebSocketEndpoint } from '../shared/contracts/peer-host.js';
|
|
2
|
+
import type { CodingNsRpcClient } from './features/types.js';
|
|
3
|
+
export interface PeerHostEventSocket {
|
|
4
|
+
readonly readyState: number;
|
|
5
|
+
send(data: string): void;
|
|
6
|
+
close(code?: number, reason?: string): void;
|
|
7
|
+
on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: any[]) => void): void;
|
|
8
|
+
}
|
|
9
|
+
export interface PeerHostEventSubscription {
|
|
10
|
+
readonly close: () => void;
|
|
11
|
+
/**
|
|
12
|
+
* 向当前 PeerHost 作用域发送白名单消息。作用域字段由适配器注入,调用方不能覆盖。
|
|
13
|
+
*/
|
|
14
|
+
readonly send: (type: PeerHostClientMessageType, payload?: Readonly<Record<string, unknown>>) => void;
|
|
15
|
+
readonly terminalInput: (payload: Readonly<Record<string, unknown>>) => void;
|
|
16
|
+
readonly terminalResize: (payload: Readonly<Record<string, unknown>>) => void;
|
|
17
|
+
readonly terminalClose: (payload?: Readonly<Record<string, unknown>>) => void;
|
|
18
|
+
readonly rightToolSubscribe: (payload: Readonly<Record<string, unknown>>) => void;
|
|
19
|
+
readonly rightToolRefresh: (payload?: Readonly<Record<string, unknown>>) => void;
|
|
20
|
+
readonly rightToolClose: (payload?: Readonly<Record<string, unknown>>) => void;
|
|
21
|
+
}
|
|
22
|
+
export type PeerHostEventSocketFactory = (scope: HostScope) => Promise<PeerHostEventSocket>;
|
|
23
|
+
export type PeerHostEventListener = (event: Record<string, unknown>) => void;
|
|
24
|
+
/** Client 到目标 Host 的 WebSocket 消息白名单。未知类型不会被透传。 */
|
|
25
|
+
export type PeerHostClientMessageType = 'workbench.subscribe' | 'workbench.refresh' | 'fileTree.subscribe' | 'fileTree.refresh' | 'git.subscribe' | 'git.refresh' | 'session.subscribe' | 'session.load_older' | 'session.send' | 'session.stop' | 'session.permission_reply' | 'session.answer' | 'terminal.subscribe' | 'terminal.input' | 'terminal.resize' | 'terminal.close' | 'rightTool.subscribe' | 'rightTool.refresh' | 'rightTool.close';
|
|
26
|
+
export interface PeerHostEventStreamOptions {
|
|
27
|
+
/** 连接关闭后最多重试次数;默认 4 次,避免无限创建计时器。 */
|
|
28
|
+
readonly maxReconnectAttempts?: number;
|
|
29
|
+
/** 每次重试等待时间,超出数组长度后使用最后一个值。 */
|
|
30
|
+
readonly reconnectDelaysMs?: readonly number[];
|
|
31
|
+
}
|
|
32
|
+
/** 根据当前 Host 自有端点生成作用域 WebSocket 工厂;不接受目标 Host URL。 */
|
|
33
|
+
export declare function createPeerHostWebSocketFactory(endpoint: PeerHostWebSocketEndpoint, options?: {
|
|
34
|
+
readonly host?: string;
|
|
35
|
+
readonly protocol?: 'ws' | 'wss';
|
|
36
|
+
}): PeerHostEventSocketFactory;
|
|
37
|
+
export interface PeerHostProxyResponse {
|
|
38
|
+
readonly status: number;
|
|
39
|
+
readonly headers: readonly [string, string][];
|
|
40
|
+
readonly body: string;
|
|
41
|
+
}
|
|
42
|
+
export interface PeerHostScopedClient {
|
|
43
|
+
request(scope: HostScope, path: string, options?: {
|
|
44
|
+
readonly method?: string;
|
|
45
|
+
readonly body?: string;
|
|
46
|
+
}): Promise<PeerHostProxyResponse>;
|
|
47
|
+
loadSessionHistory(scope: HostScope, cursor?: string): Promise<PeerHostProxyResponse>;
|
|
48
|
+
sendMessage(scope: HostScope, body: string): Promise<PeerHostProxyResponse>;
|
|
49
|
+
stopSession(scope: HostScope): Promise<PeerHostProxyResponse>;
|
|
50
|
+
replyPermission(scope: HostScope, body: string): Promise<PeerHostProxyResponse>;
|
|
51
|
+
answerQuestion(scope: HostScope, body: string): Promise<PeerHostProxyResponse>;
|
|
52
|
+
readFile(scope: HostScope, path: string): Promise<PeerHostProxyResponse>;
|
|
53
|
+
writeFile(scope: HostScope, body: string): Promise<PeerHostProxyResponse>;
|
|
54
|
+
gitStatus(scope: HostScope): Promise<PeerHostProxyResponse>;
|
|
55
|
+
terminal(scope: HostScope, action: 'create' | 'input' | 'resize' | 'close', body?: string): Promise<PeerHostProxyResponse>;
|
|
56
|
+
rightTool(scope: HostScope, action: 'open' | 'refresh' | 'close', body?: string): Promise<PeerHostProxyResponse>;
|
|
57
|
+
/**
|
|
58
|
+
* 接入已经完成 Host 侧升级握手的事件流。
|
|
59
|
+
*
|
|
60
|
+
* 插件自有网关端点由 `peerHost/wsEndpoint` 提供;调用方必须显式提供
|
|
61
|
+
* socketFactory,避免在适配器内部猜测当前 Host 地址。
|
|
62
|
+
*/
|
|
63
|
+
openEventStream(scope: HostScope, socketFactory: PeerHostEventSocketFactory, listener: PeerHostEventListener, options?: PeerHostEventStreamOptions): Promise<PeerHostEventSubscription>;
|
|
64
|
+
}
|
|
65
|
+
/** Client 侧远端资源适配器;每一次请求都从 HostScope 生成,不保存目标凭据。 */
|
|
66
|
+
export declare function createPeerHostScopedClient(rpc: CodingNsRpcClient): PeerHostScopedClient;
|
|
67
|
+
//# sourceMappingURL=peer-host-scoped-client.d.ts.map
|