@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.
Files changed (171) hide show
  1. package/README.en.md +4 -4
  2. package/README.md +14 -5
  3. package/data/build/dist/client/account-bar.js +57 -3
  4. package/data/build/dist/client/bundle.js +36246 -268
  5. package/data/build/dist/client/cli-slots.js +60 -18
  6. package/data/build/dist/client/dsh-h5-bootstrap.d.ts +2 -0
  7. package/data/build/dist/client/dsh-h5-bootstrap.js +79 -13
  8. package/data/build/dist/client/features/file-management-panel.d.ts +5 -0
  9. package/data/build/dist/client/features/file-management-panel.js +48 -0
  10. package/data/build/dist/client/features/file-management.d.ts +4 -0
  11. package/data/build/dist/client/features/file-management.js +50 -0
  12. package/data/build/dist/client/features/index.d.ts +10 -1
  13. package/data/build/dist/client/features/index.js +12 -1
  14. package/data/build/dist/client/features/login-protection-panel.js +1 -1
  15. package/data/build/dist/client/features/login-protection-session.d.ts +2 -0
  16. package/data/build/dist/client/features/login-protection-session.js +18 -0
  17. package/data/build/dist/client/features/peer-host.d.ts +4 -0
  18. package/data/build/dist/client/features/peer-host.js +89 -0
  19. package/data/build/dist/client/features/reverse-proxy-panel.js +23 -7
  20. package/data/build/dist/client/features/reverse-proxy.d.ts +1 -2
  21. package/data/build/dist/client/features/reverse-proxy.js +75 -5
  22. package/data/build/dist/client/features/types.d.ts +9 -0
  23. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +10 -0
  24. package/data/build/dist/client/features/workspace-session-enhancement.js +18 -1
  25. package/data/build/dist/client/file-management-dom.d.ts +12 -0
  26. package/data/build/dist/client/file-management-dom.js +527 -0
  27. package/data/build/dist/client/git-management.js +24 -5
  28. package/data/build/dist/client/host-navigation.d.ts +29 -0
  29. package/data/build/dist/client/host-navigation.js +29 -0
  30. package/data/build/dist/client/host-router.d.ts +3 -0
  31. package/data/build/dist/client/host-router.js +2 -0
  32. package/data/build/dist/client/index.d.ts +6 -1
  33. package/data/build/dist/client/index.js +30 -10
  34. package/data/build/dist/client/locale.js +36 -7
  35. package/data/build/dist/client/peer-host-connection-button.d.ts +13 -0
  36. package/data/build/dist/client/peer-host-connection-button.js +69 -0
  37. package/data/build/dist/client/peer-host-management-api.d.ts +40 -0
  38. package/data/build/dist/client/peer-host-management-api.js +33 -0
  39. package/data/build/dist/client/peer-host-management-panel.d.ts +13 -0
  40. package/data/build/dist/client/peer-host-management-panel.js +181 -0
  41. package/data/build/dist/client/peer-host-native-session-ui.d.ts +60 -0
  42. package/data/build/dist/client/peer-host-native-session-ui.js +372 -0
  43. package/data/build/dist/client/peer-host-scoped-client.d.ts +67 -0
  44. package/data/build/dist/client/peer-host-scoped-client.js +316 -0
  45. package/data/build/dist/client/peer-host-session-controller.d.ts +30 -0
  46. package/data/build/dist/client/peer-host-session-controller.js +95 -0
  47. package/data/build/dist/client/quick-phrase-slot.js +14 -0
  48. package/data/build/dist/client/remote-web-context.js +26 -4
  49. package/data/build/dist/client/session-changed-files-view.d.ts +15 -0
  50. package/data/build/dist/client/session-changed-files-view.js +337 -0
  51. package/data/build/dist/client/settings-bridge.d.ts +14 -4
  52. package/data/build/dist/client/settings-bridge.js +59 -12
  53. package/data/build/dist/client/settings-section.js +17 -4
  54. package/data/build/dist/client/subscription-slot.js +21 -8
  55. package/data/build/dist/client/workspace-session-rightbar-dom.d.ts +28 -0
  56. package/data/build/dist/client/workspace-session-rightbar-dom.js +291 -0
  57. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +25 -33
  58. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +7 -1
  59. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +34 -11
  60. package/data/build/dist/dsh-capabilities/matrix.js +9 -0
  61. package/data/build/dist/dsh-capabilities/routes.js +35 -0
  62. package/data/build/dist/dsh-capabilities/settings-store.d.ts +12 -0
  63. package/data/build/dist/dsh-capabilities/settings-store.js +34 -0
  64. package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
  65. package/data/build/dist/features/host-router.d.ts +26 -0
  66. package/data/build/dist/features/host-router.js +110 -0
  67. package/data/build/dist/features/registry.js +3 -0
  68. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +13 -1
  69. package/data/build/dist/host/cli-adapters/codex-driver.js +528 -72
  70. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +21 -2
  71. package/data/build/dist/host/cli-adapters/command-code-driver.js +408 -105
  72. package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
  73. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +2 -0
  74. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +47 -28
  75. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +37 -0
  76. package/data/build/dist/host/cli-adapters/feature.js +94 -10
  77. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +6 -0
  78. package/data/build/dist/host/cli-adapters/json-rpc-process.js +14 -3
  79. package/data/build/dist/host/cli-adapters/kimi-driver.js +3 -3
  80. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +6 -0
  81. package/data/build/dist/host/cli-adapters/model-catalog.js +8 -0
  82. package/data/build/dist/host/cli-adapters/opencode-driver.js +14 -2
  83. package/data/build/dist/host/cli-adapters/pi-driver.js +2 -2
  84. package/data/build/dist/host/cli-adapters/process-utils.d.ts +8 -0
  85. package/data/build/dist/host/cli-adapters/process-utils.js +48 -0
  86. package/data/build/dist/host/cli-adapters/registry.d.ts +6 -1
  87. package/data/build/dist/host/cli-adapters/registry.js +41 -7
  88. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +23 -4
  89. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
  90. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +36 -15
  91. package/data/build/dist/host/dsh-device-runtime.d.ts +2 -0
  92. package/data/build/dist/host/dsh-device-runtime.js +55 -29
  93. package/data/build/dist/host/dsh-runtime-version.d.ts +3 -2
  94. package/data/build/dist/host/dsh-runtime-version.js +71 -2
  95. package/data/build/dist/host/features/auth.js +10 -2
  96. package/data/build/dist/host/features/file-management.d.ts +5 -0
  97. package/data/build/dist/host/features/file-management.js +267 -0
  98. package/data/build/dist/host/features/git-management.js +24 -2
  99. package/data/build/dist/host/features/index.d.ts +2 -0
  100. package/data/build/dist/host/features/index.js +6 -0
  101. package/data/build/dist/host/features/peer-host.d.ts +22 -0
  102. package/data/build/dist/host/features/peer-host.js +322 -0
  103. package/data/build/dist/host/features/types.d.ts +4 -0
  104. package/data/build/dist/host/host-router.d.ts +3 -0
  105. package/data/build/dist/host/host-router.js +2 -0
  106. package/data/build/dist/host/index-injection.d.ts +17 -0
  107. package/data/build/dist/host/index-injection.js +24 -0
  108. package/data/build/dist/host/index.d.ts +4 -1
  109. package/data/build/dist/host/index.js +41 -7
  110. package/data/build/dist/host/lan-access-dsh.d.ts +34 -1
  111. package/data/build/dist/host/lan-access-dsh.js +145 -26
  112. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts +52 -0
  113. package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js +166 -0
  114. package/data/build/dist/host/modules/peer-host/host-ws-connector.d.ts +31 -0
  115. package/data/build/dist/host/modules/peer-host/host-ws-connector.js +108 -0
  116. package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.d.ts +26 -0
  117. package/data/build/dist/host/modules/peer-host/host-ws-proxy-service.js +159 -0
  118. package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.d.ts +50 -0
  119. package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.js +97 -0
  120. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.d.ts +19 -0
  121. package/data/build/dist/host/modules/peer-host/peer-host-diagnostics.js +54 -0
  122. package/data/build/dist/host/modules/peer-host/peer-host-handshake.d.ts +44 -0
  123. package/data/build/dist/host/modules/peer-host/peer-host-handshake.js +133 -0
  124. package/data/build/dist/host/modules/peer-host/peer-host-relay.d.ts +65 -0
  125. package/data/build/dist/host/modules/peer-host/peer-host-relay.js +196 -0
  126. package/data/build/dist/host/modules/peer-host/peer-host-session.d.ts +42 -0
  127. package/data/build/dist/host/modules/peer-host/peer-host-session.js +152 -0
  128. package/data/build/dist/host/modules/peer-host/peer-host-store.d.ts +92 -0
  129. package/data/build/dist/host/modules/peer-host/peer-host-store.js +323 -0
  130. package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.d.ts +34 -0
  131. package/data/build/dist/host/modules/peer-host/peer-host-ws-gateway.js +156 -0
  132. package/data/build/dist/host/native-session-bridge.d.ts +18 -2
  133. package/data/build/dist/host/native-session-bridge.js +236 -8
  134. package/data/build/dist/host/remote-web-runtime.js +26 -1
  135. package/data/build/dist/host/rpc.js +8 -3
  136. package/data/build/dist/host/session-changes.d.ts +5 -0
  137. package/data/build/dist/host/session-changes.js +163 -0
  138. package/data/build/dist/host/settings.js +6 -0
  139. package/data/build/dist/index.d.ts +1 -1
  140. package/data/build/dist/index.js +1 -1
  141. package/data/build/dist/shared/contracts/cli-adapter.d.ts +23 -2
  142. package/data/build/dist/shared/contracts/config.d.ts +15 -0
  143. package/data/build/dist/shared/contracts/config.js +10 -0
  144. package/data/build/dist/shared/contracts/dsh-device.d.ts +2 -0
  145. package/data/build/dist/shared/contracts/errors.d.ts +14 -0
  146. package/data/build/dist/shared/contracts/errors.js +15 -0
  147. package/data/build/dist/shared/contracts/feature.d.ts +2 -0
  148. package/data/build/dist/shared/contracts/file-management.d.ts +6 -0
  149. package/data/build/dist/shared/contracts/file-management.js +3 -0
  150. package/data/build/dist/shared/contracts/peer-host.d.ts +116 -15
  151. package/data/build/dist/shared/contracts/peer-host.js +19 -0
  152. package/data/build/dist/shared/contracts/version.d.ts +1 -1
  153. package/data/build/dist/shared/contracts/version.js +1 -1
  154. package/data/build/dist/shared/index.d.ts +4 -3
  155. package/data/build/dist/shared/index.js +2 -2
  156. package/data/build/dist/transport/debug.d.ts +5 -0
  157. package/data/build/dist/transport/debug.js +23 -1
  158. package/data/build/dist/transport/dsh-transport.d.ts +2 -0
  159. package/data/build/dist/transport/dsh-transport.js +11 -0
  160. package/data/build/dist/transport/webrtc-client.d.ts +2 -0
  161. package/data/build/dist/transport/webrtc-client.js +2 -1
  162. package/data/build/dist/transport/webrtc-host.d.ts +2 -0
  163. package/data/build/dist/transport/webrtc-host.js +6 -3
  164. package/data/build/dist/typert.host.d.ts +1 -1
  165. package/data/build/dist/typert.host.js +2 -1
  166. package/dsh.bundle.patch +10 -0
  167. package/package.json +19 -11
  168. package/profile/README.md +2 -2
  169. package/profile/package.json +2 -2
  170. package/profile/version.json +1 -1
  171. package/version.json +1 -1
@@ -0,0 +1,322 @@
1
+ import { homedir } from 'node:os';
2
+ import { dirname, join } from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import { CODINGNS_VERSION, DSH_VERSION, isDshVersionCompatible } from '../../shared/contracts/version.js';
6
+ import { PeerHostHandshakeService } from '../modules/peer-host/peer-host-handshake.js';
7
+ import { PeerHostSessionService } from '../modules/peer-host/peer-host-session.js';
8
+ import { PeerHostHttpProxyService } from '../modules/peer-host/host-api-proxy-service.js';
9
+ import { EncryptedFilePeerHostCredentialStore, FilePeerHostRecordStore, PeerHostStore, } from '../modules/peer-host/peer-host-store.js';
10
+ import { PeerHostAggregateService } from '../modules/peer-host/peer-host-aggregate-service.js';
11
+ import { CodingNsRpcError } from '../rpc-table.js';
12
+ import { PeerHostWebSocketGateway, PEER_HOST_WS_PATH } from '../modules/peer-host/peer-host-ws-gateway.js';
13
+ import { PeerHostWsProxyError, PeerHostWsProxyService } from '../modules/peer-host/host-ws-proxy-service.js';
14
+ import { createPeerHostRemoteConnector } from '../modules/peer-host/host-ws-connector.js';
15
+ import { createPeerHostRelayConnector, PeerHostReconnectManager } from '../modules/peer-host/peer-host-relay.js';
16
+ import { PEER_HOST_ERROR_CODES } from '../../shared/contracts/peer-host.js';
17
+ import { FileLanAccessDshLoginStore, resolveLoginProtectionCookieName, verifyLoginProtectionSession } from '../lan-access-dsh.js';
18
+ import { createPeerHostDiagnosticSink, toPeerHostDiagnosticSnapshot } from '../modules/peer-host/peer-host-diagnostics.js';
19
+ /** PeerHost Host 模块;配置、握手和目标登录态只在 Host 进程内装配。 */
20
+ export function createPeerHostFeature(options = {}) {
21
+ return {
22
+ descriptor: {
23
+ name: 'peerHost',
24
+ version: '0.1.0',
25
+ enabledByDefault: false,
26
+ // 与 Client 保持一致;历史配置中的 true 也不能让未验收能力启动。
27
+ disabled: true,
28
+ dependencies: [],
29
+ runtime: 'host',
30
+ requires: [
31
+ { capability: 'peer-host.store', required: false, fallback: 'degrade' },
32
+ { capability: 'peer-host.handshake', required: false, fallback: 'degrade' },
33
+ { capability: 'peer-host.http-proxy', required: false, fallback: 'degrade' },
34
+ { capability: 'peer-host.ws-proxy', required: false, fallback: 'degrade' },
35
+ { capability: 'peer-host.aggregate', required: false, fallback: 'degrade' },
36
+ { capability: 'peer-host.relay-route', required: false, fallback: 'degrade' },
37
+ ],
38
+ },
39
+ async start(context) {
40
+ const stateDirectory = options.stateDirectory ?? process.env.CODINGNS4DSH_STATE_DIR?.trim() ?? join(homedir(), '.config', 'codingns4dsh');
41
+ const ownerUserId = options.ownerUserId ?? process.env.CODINGNS4DSH_OWNER_ID?.trim() ?? 'local-host';
42
+ const encryptionKey = options.encryptionKey ?? await loadPeerHostKey(join(stateDirectory, 'peer-host-key.bin'));
43
+ const credentials = new EncryptedFilePeerHostCredentialStore(join(stateDirectory, 'peer-host-credentials.enc'), encryptionKey);
44
+ const store = new PeerHostStore(ownerUserId, new FilePeerHostRecordStore(join(stateDirectory, 'peer-host-records.json')), credentials);
45
+ const handshake = new PeerHostHandshakeService(store, credentials, {
46
+ productId: 'CodingNS',
47
+ pluginId: '@jingyi0605/codingns4dsh',
48
+ pluginVersion: CODINGNS_VERSION,
49
+ apiCompatibility: 'peer-host-v1',
50
+ isDshVersionSupported: isDshVersionCompatible,
51
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
52
+ });
53
+ const sessions = new PeerHostSessionService(store, credentials, options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl });
54
+ const httpProxy = new PeerHostHttpProxyService(store, sessions, options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl });
55
+ const aggregate = new PeerHostAggregateService();
56
+ const diagnostics = createPeerHostDiagnosticSink({
57
+ enabled: process.env.CODINGNS4DSH_DEBUG === '1',
58
+ sink: (event, snapshot) => console.info('[codingns4dsh:peer-host]', { event, ...snapshot }),
59
+ });
60
+ const lanConnector = options.connectRemote ?? createPeerHostRemoteConnector();
61
+ const relayConnector = createPeerHostRelayConnector({ ...(options.relayTransport === undefined ? {} : { transport: options.relayTransport }) });
62
+ const connector = (record, accessToken, scope) => record.route.kind === 'relay'
63
+ ? relayConnector(record, accessToken, scope)
64
+ : lanConnector(record, accessToken, scope);
65
+ const reconnectManager = new PeerHostReconnectManager({
66
+ connect: connector,
67
+ onState: async (snapshot) => {
68
+ const current = await store.get(snapshot.peerHostId);
69
+ if (current === null || snapshot.state === 'connecting' || snapshot.state === 'reconnecting' || snapshot.state === 'stopped')
70
+ return;
71
+ if (snapshot.state === 'ready') {
72
+ if (current.status !== 'ready')
73
+ await store.updateStatus(snapshot.peerHostId, 'ready', null);
74
+ return;
75
+ }
76
+ const errorCode = snapshot.lastErrorCode === PEER_HOST_ERROR_CODES.RELAY_UNAVAILABLE
77
+ ? PEER_HOST_ERROR_CODES.RELAY_UNAVAILABLE
78
+ : PEER_HOST_ERROR_CODES.UNREACHABLE;
79
+ await store.updateStatus(snapshot.peerHostId, 'unreachable', errorCode);
80
+ },
81
+ });
82
+ context.resources.add(() => reconnectManager.close());
83
+ // WS 代理的客户端 socket 无法在关闭后替换远端 socket;因此这里保持一条连接一一绑定,
84
+ // 重连 manager 仅治理显式的 Host 侧长连接消费者,避免后台重连产生孤立远端连接。
85
+ const wsProxy = new PeerHostWsProxyService(store, sessions, connector);
86
+ const loginStore = new FileLanAccessDshLoginStore();
87
+ const lanSettings = context.services.settings?.get().lanAccessDsh;
88
+ const gateway = new PeerHostWebSocketGateway({
89
+ listenHost: process.env.CODINGNS4DSH_PEER_HOST_WS_HOST?.trim() || (lanSettings?.autoStart === true ? lanSettings.listenHost : '127.0.0.1'),
90
+ listenPort: parsePort(process.env.CODINGNS4DSH_PEER_HOST_WS_PORT),
91
+ path: PEER_HOST_WS_PATH,
92
+ authorizeUpgrade: (request) => authorizePeerHostUpgrade(loginStore, request),
93
+ onConnection: (socket, request) => {
94
+ const scope = parseWebSocketScope(request.url);
95
+ if (scope.targetHostId === null) {
96
+ throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 缺少目标 Host');
97
+ }
98
+ return wsProxy.open(scope.targetHostId, socket, scope);
99
+ },
100
+ });
101
+ let wsEndpoint = null;
102
+ try {
103
+ wsEndpoint = await gateway.start();
104
+ context.resources.add(() => gateway.close());
105
+ }
106
+ catch {
107
+ console.error('codingns4dsh: PeerHost WebSocket 网关启动失败');
108
+ }
109
+ if (context.services.registerPeerHostHandshakeRoute !== undefined) {
110
+ const unregisterHandshake = context.services.registerPeerHostHandshakeRoute(async () => Response.json({
111
+ productId: 'CodingNS',
112
+ pluginId: '@jingyi0605/codingns4dsh',
113
+ pluginVersion: CODINGNS_VERSION,
114
+ dshVersion: context.services.dshVersion ?? DSH_VERSION,
115
+ apiCompatibility: 'peer-host-v1',
116
+ fingerprint: process.env.CODINGNS4DSH_HOST_FINGERPRINT?.trim() || null,
117
+ capabilities: ['peer-host.store', 'peer-host.handshake', 'peer-host.http-proxy', 'peer-host.ws-proxy', 'peer-host.aggregate'],
118
+ }));
119
+ context.resources.add(unregisterHandshake);
120
+ }
121
+ const unregister = context.services.rpc.register('peerHost', async (action, payload) => {
122
+ const input = record(payload);
123
+ switch (action) {
124
+ case 'list': return (await store.list()).map(toPeerHostClientRecord);
125
+ case 'diagnostics': {
126
+ const snapshots = (await store.list()).map(toPeerHostDiagnosticSnapshot);
127
+ for (const snapshot of snapshots)
128
+ diagnostics.emit('peer-host.snapshot', snapshot);
129
+ return snapshots;
130
+ }
131
+ case 'create': return toPeerHostClientRecord(await store.create({ displayName: requiredString(input.displayName, 'displayName'), route: parseRoute(input.route) }));
132
+ case 'update': return toPeerHostClientRecord(await store.update(requiredString(input.peerHostId, 'peerHostId'), {
133
+ ...(input.displayName === undefined ? {} : { displayName: requiredString(input.displayName, 'displayName') }),
134
+ ...(input.route === undefined ? {} : { route: parseRoute(input.route) }),
135
+ }));
136
+ case 'remove':
137
+ await store.remove(requiredString(input.peerHostId, 'peerHostId'));
138
+ return { removed: true };
139
+ case 'check': return handshake.check(requiredString(input.peerHostId, 'peerHostId')).then(toPeerHostClientRecord);
140
+ case 'reconnect': {
141
+ const peerHostId = requiredString(input.peerHostId, 'peerHostId');
142
+ await store.updateStatus(peerHostId, 'reconnecting', null);
143
+ return handshake.check(peerHostId).then(toPeerHostClientRecord);
144
+ }
145
+ case 'login': return sessions.login(requiredString(input.peerHostId, 'peerHostId'), {
146
+ username: requiredString(input.username, 'username'),
147
+ password: requiredString(input.password, 'password'),
148
+ });
149
+ case 'logout': return sessions.logout(requiredString(input.peerHostId, 'peerHostId'));
150
+ case 'wsEndpoint': return wsEndpoint;
151
+ case 'aggregate': {
152
+ if (options.aggregateSources === undefined) {
153
+ return aggregate.load([{
154
+ hostId: 'local-host',
155
+ targetHostId: null,
156
+ hostLabel: '当前 Host',
157
+ capability: { available: false, reason: '当前 Host 尚未提供可验证的工作区/会话摘要 source' },
158
+ load: async () => [],
159
+ }]);
160
+ }
161
+ return aggregate.load(await options.aggregateSources());
162
+ }
163
+ case 'request': {
164
+ const peerHostId = requiredString(input.peerHostId, 'peerHostId');
165
+ const scope = parseScope(input.scope);
166
+ return httpProxy.request(peerHostId, {
167
+ scope,
168
+ path: requiredString(input.path, 'path'),
169
+ ...(input.method === undefined ? {} : { method: requiredString(input.method, 'method') }),
170
+ ...(input.body === undefined ? {} : { body: requiredString(input.body, 'body') }),
171
+ });
172
+ }
173
+ default: throw new CodingNsRpcError('CODINGNS_RPC_NOT_FOUND', `未知 PeerHost RPC: peerHost/${action}`);
174
+ }
175
+ });
176
+ context.resources.add(unregister);
177
+ },
178
+ };
179
+ }
180
+ async function authorizePeerHostUpgrade(loginStore, request) {
181
+ const origin = request.headers.origin;
182
+ const host = request.headers.host;
183
+ if (typeof origin === 'string' && typeof host === 'string') {
184
+ try {
185
+ const originUrl = new URL(origin);
186
+ const originHost = normalizeHostName(originUrl.hostname);
187
+ const requestHost = normalizeHostName(readRequestHost(host));
188
+ if (originHost !== requestHost)
189
+ return false;
190
+ }
191
+ catch {
192
+ return false;
193
+ }
194
+ }
195
+ const config = await loginStore.read();
196
+ if (config === null || !config.enabled || !config.scopes.lan)
197
+ return true;
198
+ const token = readCookie(request.headers.cookie, resolveLoginProtectionCookieName());
199
+ return verifyLoginProtectionSession(loginStore, token, 'lan');
200
+ }
201
+ function parseWebSocketScope(rawUrl) {
202
+ if (rawUrl === undefined)
203
+ throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 缺少作用域');
204
+ const url = new URL(rawUrl, 'http://peer-host.invalid');
205
+ const allowed = new Set(['hostId', 'targetHostId', 'workspaceId', 'sessionId', 'scopeGeneration']);
206
+ for (const key of url.searchParams.keys())
207
+ if (!allowed.has(key))
208
+ throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 查询参数未加入白名单');
209
+ for (const key of allowed)
210
+ if (url.searchParams.getAll(key).length > 1)
211
+ throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 作用域参数不得重复');
212
+ const hostId = requiredString(url.searchParams.get('hostId'), 'hostId');
213
+ const targetHostId = requiredString(url.searchParams.get('targetHostId'), 'targetHostId');
214
+ const workspaceId = requiredString(url.searchParams.get('workspaceId'), 'workspaceId');
215
+ const sessionId = url.searchParams.get('sessionId');
216
+ const scopeGeneration = Number(url.searchParams.get('scopeGeneration'));
217
+ if (!Number.isSafeInteger(scopeGeneration) || scopeGeneration < 0)
218
+ throw new PeerHostWsProxyError(PEER_HOST_ERROR_CODES.SCOPE_MISMATCH, 'PeerHost WebSocket 作用域 generation 无效');
219
+ return { hostId, targetHostId, workspaceId, sessionId: sessionId?.trim() || null, scopeGeneration };
220
+ }
221
+ function readCookie(value, name) {
222
+ for (const item of (value ?? '').split(';')) {
223
+ const separator = item.indexOf('=');
224
+ if (separator > 0 && item.slice(0, separator).trim() === name)
225
+ return item.slice(separator + 1).trim();
226
+ }
227
+ return undefined;
228
+ }
229
+ function parsePort(value) {
230
+ if (value === undefined || value.trim() === '')
231
+ return 0;
232
+ const port = Number(value);
233
+ return Number.isSafeInteger(port) && port >= 0 && port <= 65_535 ? port : 0;
234
+ }
235
+ function normalizeHostName(value) {
236
+ const host = value.trim().toLowerCase();
237
+ if (host === 'localhost' || host === '::1' || host === '::ffff:127.0.0.1')
238
+ return '127.0.0.1';
239
+ return host;
240
+ }
241
+ function readRequestHost(value) {
242
+ const host = value.trim();
243
+ if (host.startsWith('[')) {
244
+ const end = host.indexOf(']');
245
+ return end > 1 ? host.slice(1, end) : '';
246
+ }
247
+ return host.split(':')[0] ?? '';
248
+ }
249
+ async function loadPeerHostKey(path) {
250
+ try {
251
+ const key = await readFile(path);
252
+ if (key.byteLength !== 32)
253
+ throw new Error('PeerHost 密钥文件长度无效');
254
+ return key;
255
+ }
256
+ catch (error) {
257
+ if (!isNodeError(error, 'ENOENT'))
258
+ throw error;
259
+ const key = randomBytes(32);
260
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
261
+ try {
262
+ await writeFile(path, key, { mode: 0o600, flag: 'wx' });
263
+ return key;
264
+ }
265
+ catch (writeError) {
266
+ if (!isNodeError(writeError, 'EEXIST'))
267
+ throw writeError;
268
+ const existing = await readFile(path);
269
+ if (existing.byteLength !== 32)
270
+ throw new Error('PeerHost 密钥文件长度无效');
271
+ return existing;
272
+ }
273
+ }
274
+ }
275
+ function parseRoute(value) {
276
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
277
+ throw new TypeError('route 必须是对象');
278
+ const route = value;
279
+ if (route.kind === 'lan')
280
+ return { kind: 'lan', baseUrl: requiredString(route.baseUrl, 'baseUrl'), normalizedOrigin: typeof route.normalizedOrigin === 'string' ? route.normalizedOrigin : '' };
281
+ if (route.kind === 'relay')
282
+ return { kind: 'relay', deviceId: requiredString(route.deviceId, 'deviceId'), relayEntryId: requiredString(route.relayEntryId, 'relayEntryId'), transportVersion: requiredString(route.transportVersion, 'transportVersion') };
283
+ throw new TypeError('route.kind 无效');
284
+ }
285
+ function record(value) {
286
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
287
+ throw new TypeError('PeerHost RPC 参数必须是对象');
288
+ return value;
289
+ }
290
+ function requiredString(value, field) {
291
+ if (typeof value !== 'string' || value.trim() === '')
292
+ throw new TypeError(`${field} 不能为空`);
293
+ return value.trim();
294
+ }
295
+ function parseScope(value) {
296
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
297
+ throw new TypeError('scope 必须是对象');
298
+ const scope = value;
299
+ const hostId = requiredString(scope.hostId, 'scope.hostId');
300
+ const targetHostId = scope.targetHostId === null ? null : requiredString(scope.targetHostId, 'scope.targetHostId');
301
+ const workspaceId = requiredString(scope.workspaceId, 'scope.workspaceId');
302
+ const sessionId = scope.sessionId === null ? null : requiredString(scope.sessionId, 'scope.sessionId');
303
+ if (typeof scope.scopeGeneration !== 'number' || !Number.isSafeInteger(scope.scopeGeneration) || scope.scopeGeneration < 0)
304
+ throw new TypeError('scope.scopeGeneration 无效');
305
+ return { hostId, targetHostId, workspaceId, sessionId, scopeGeneration: scope.scopeGeneration };
306
+ }
307
+ export function toPeerHostClientRecord(record) {
308
+ return {
309
+ ...record,
310
+ fingerprint: redactFingerprint(record.fingerprint),
311
+ route: record.route.kind === 'lan' ? { kind: 'lan' } : { kind: 'relay' },
312
+ };
313
+ }
314
+ function redactFingerprint(value) {
315
+ if (value === null || value.length <= 12)
316
+ return value;
317
+ return `${value.slice(0, 8)}...${value.slice(-4)}`;
318
+ }
319
+ function isNodeError(error, code) {
320
+ return error instanceof Error && 'code' in error && error.code === code;
321
+ }
322
+ //# sourceMappingURL=peer-host.js.map
@@ -35,7 +35,11 @@ export interface CodingNsHostServices {
35
35
  readonly debug?: DebugWorkspaceService;
36
36
  /** Debug 模块启用时注册其专属 Fetch 路由,停用时由模块资源注销。 */
37
37
  readonly registerDebugProxyRoute?: (handler: (request: Request) => Promise<Response>) => () => Promise<void>;
38
+ /** PeerHost 固定握手入口;只允许模块返回脱敏能力摘要。 */
39
+ readonly registerPeerHostHandshakeRoute?: (handler: (request: Request) => Promise<Response>) => () => Promise<void>;
38
40
  /** 由 Host 权威解析 Workspace ID,Client 不可覆盖。 */
39
41
  readonly resolveWorkspaceRoot?: (workspaceId: string) => string | null;
42
+ /** 返回 Host 当前已知的工作区根目录,用于文件管理路径校验。 */
43
+ readonly listWorkspaceRoots?: () => readonly string[];
40
44
  }
41
45
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { HostRouter } from '../features/host-router.js';
2
+ export type { HostRouterInput } from '../features/host-router.js';
3
+ //# sourceMappingURL=host-router.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { HostRouter } from '../features/host-router.js';
2
+ //# sourceMappingURL=host-router.js.map
@@ -0,0 +1,17 @@
1
+ /** 启动页注入表中的全局变量记录。 */
2
+ export interface DshIndexInjectionEntry {
3
+ readonly kind?: unknown;
4
+ readonly name?: unknown;
5
+ readonly value?: unknown;
6
+ readonly [key: string]: unknown;
7
+ }
8
+ /**
9
+ * 在启动页声明 Host 所有权,同时保留 Desktop 已经提供的完整 Transport。
10
+ *
11
+ * 这里只使用 DSH WebServer 支持的 JSON global 行,不在首页安装全局 setter。
12
+ * 页面可能由 Desktop Shell 在更晚的阶段注入自己的 Transport;因此找不到
13
+ * 已有 Transport 时必须保持不变,不能追加同名全局,否则会覆盖 Desktop 的
14
+ * `streamBaseUrl`,让 `/api/remote.mux` 退回 `ws://app`。
15
+ */
16
+ export declare function injectDshWebTransportOwnership(table: unknown[]): void;
17
+ //# sourceMappingURL=index-injection.d.ts.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 在启动页声明 Host 所有权,同时保留 Desktop 已经提供的完整 Transport。
3
+ *
4
+ * 这里只使用 DSH WebServer 支持的 JSON global 行,不在首页安装全局 setter。
5
+ * 页面可能由 Desktop Shell 在更晚的阶段注入自己的 Transport;因此找不到
6
+ * 已有 Transport 时必须保持不变,不能追加同名全局,否则会覆盖 Desktop 的
7
+ * `streamBaseUrl`,让 `/api/remote.mux` 退回 `ws://app`。
8
+ */
9
+ export function injectDshWebTransportOwnership(table) {
10
+ const index = table.findIndex((entry) => isTransportInjection(entry));
11
+ if (index < 0)
12
+ return;
13
+ const entry = table[index];
14
+ if (!isRecord(entry) || !isRecord(entry.value))
15
+ return;
16
+ table[index] = { ...entry, value: { ...entry.value, ownsHost: true } };
17
+ }
18
+ function isTransportInjection(value) {
19
+ return isRecord(value) && value.name === '__DSH_TRANSPORT__';
20
+ }
21
+ function isRecord(value) {
22
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
23
+ }
24
+ //# sourceMappingURL=index-injection.js.map
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { Context } from '@deepseek-ai/cordis';
9
9
  export declare function apply(ctx?: Context): void;
10
- export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, createGitManagementFeature, } from './features/index.js';
10
+ export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, createGitManagementFeature, createFileManagementFeature, createPeerHostFeature, } from './features/index.js';
11
11
  export type { CodingNsHostServices } from './features/index.js';
12
12
  export { CodingNsSettingsSchema, registerCodingNsSettings } from './settings.js';
13
13
  export { createCodingNsRpcHandler, createCodingNsSettingsRpcHandler, registerCodingNsRpc } from './rpc.js';
@@ -44,4 +44,7 @@ export type { LoginByEmailResponse, RefreshTokenRequest, RefreshTokenResponse, }
44
44
  export { LanAccessDshError, LanAccessDshProxy, FileLanAccessDshLoginStore, InMemoryLanAccessDshLoginStore, createLanAccessDshRpcHandler, createNodeLanAccessDshRuntime, normalizeLanAccessDshConfig, openLoginProtectionSession, verifyLoginProtectionSession, type LanAccessDshRuntime, type LanAccessDshStream, type LanAccessDshLoginRecord, type LanAccessDshLoginStore, } from './lan-access-dsh.js';
45
45
  export { CODINGNS_TUNNEL_DATA_CHANNEL_LABEL, FileHostDtlsIdentityStore, createRegisteredHostSignalingSocket, createWeriftPeerConnectionFactory, ensureHostDtlsIdentity, formatHostDtlsFingerprint, generateHostDtlsIdentity, startHostRelayRuntime, type HostDtlsIdentityMaterial, type HostDtlsIdentityStore, type HostRelayRuntime, type HostRelayRuntimeOptions, type HostRelaySession, type HostSignalingSocket, } from './relay-tunnel-runtime.js';
46
46
  export * from './terminal/index.js';
47
+ export { PeerHostWebSocketGateway, PEER_HOST_WS_PATH } from './modules/peer-host/peer-host-ws-gateway.js';
48
+ export { createPeerHostRelayConnector, PeerHostReconnectManager, type PeerHostRelayConnectorOptions, type PeerHostRelayTransportFactory, type PeerHostReconnectManagerOptions, type PeerHostReconnectSnapshot, type PeerHostReconnectState, } from './modules/peer-host/peer-host-relay.js';
49
+ export { createPeerHostDiagnosticSink, peerHostSafeError, toPeerHostDiagnosticSnapshot, type PeerHostDiagnosticSink, type PeerHostDiagnosticSnapshot, } from './modules/peer-host/peer-host-diagnostics.js';
47
50
  //# sourceMappingURL=index.d.ts.map
@@ -9,8 +9,9 @@ import { installTerminalController } from './terminal/startup.js';
9
9
  import { DebugWorkspaceService } from './debug.js';
10
10
  import { detectRuntimeDshVersion, DSH_VERSION_INJECTION_NAME } from './dsh-runtime-version.js';
11
11
  import { createDshCapabilityRegistry } from '../dsh-capabilities/index.js';
12
- import { debugInfo } from '../shared/debug.js';
12
+ import { debugInfo, debugWarn } from '../shared/debug.js';
13
13
  import { repairLegacySessionLogs } from './session-migration-repair.js';
14
+ import { injectDshWebTransportOwnership } from './index-injection.js';
14
15
  export function apply(ctx) {
15
16
  if (ctx === undefined)
16
17
  return;
@@ -58,14 +59,21 @@ export function apply(ctx) {
58
59
  hasWebServer: hostCtx.webServer !== undefined,
59
60
  });
60
61
  const webServerPort = hostCtx.webServer.port;
61
- // LAN 入口和中继 Web 页面可能使用非 loopback URL,但它们都已经经过
62
- // DSH Host 的认证边界并回到本机 Host。通过启动页注入 transport 所有权,
63
- // 让 DSH 原生 ui-settings 保持 host 模式,而不是错误降级为 memory 模式。
62
+ // 仅在 DSH 已提供 Transport 时补充 ownsHost;普通 Web 的 Codingns4DSH
63
+ // 设置由 Client RPC 桥接持久化,不能在这里追加同名全局覆盖 Desktop。
64
64
  const indexInjectionEvents = hostCtx;
65
65
  debugInfo('codingns4dsh: host index injection registration begin');
66
66
  indexInjectionEvents.on('webserver/index-inject', (table) => {
67
- table.push({ kind: 'global', name: '__DSH_TRANSPORT__', value: { ownsHost: true } });
68
- table.push({ kind: 'global', name: DSH_VERSION_INJECTION_NAME, value: dshVersion });
67
+ try {
68
+ injectDshWebTransportOwnership(table);
69
+ table.push({ kind: 'global', name: DSH_VERSION_INJECTION_NAME, value: dshVersion });
70
+ }
71
+ catch (error) {
72
+ // 首页认证不应因插件注入表异常变成 WebServer 的 HTTP 400。
73
+ debugWarn('codingns4dsh: host index injection skipped', {
74
+ error: error instanceof Error ? error.message : String(error),
75
+ });
76
+ }
69
77
  });
70
78
  debugInfo('codingns4dsh: host index injection registration ready');
71
79
  debugInfo('codingns4dsh: host settings registration begin');
@@ -90,12 +98,19 @@ export function apply(ctx) {
90
98
  nativeSessions: createCodingNsNativeSessionBridge(hostCtx, dshVersion),
91
99
  terminalProcesses: terminal.processService,
92
100
  resolveWorkspaceRoot: (workspaceId) => workspaceRoots.get(workspaceId) ?? resolveWorkspaceRoot(hostCtx, workspaceId),
101
+ listWorkspaceRoots: () => [...workspaceRoots.values(), ...readWorkspaceRoots(hostCtx)],
93
102
  registerDebugProxyRoute: (handler) => hostCtx.connection.fetch.register({
94
103
  path: '/api/codingns/debug-proxy',
95
104
  methods: ['GET', 'HEAD', 'POST'],
96
105
  requestBody: 'streaming',
97
106
  fetch: handler,
98
107
  }),
108
+ registerPeerHostHandshakeRoute: (handler) => hostCtx.connection.fetch.register({
109
+ path: '/api/public/host-handshake',
110
+ methods: ['GET'],
111
+ requestBody: 'buffered',
112
+ fetch: handler,
113
+ }),
99
114
  };
100
115
  const debug = new DebugWorkspaceService({
101
116
  resolveWorkspaceRoot: (workspaceId) => workspaceRoots.get(workspaceId) ?? resolveWorkspaceRoot(hostCtx, workspaceId),
@@ -163,7 +178,23 @@ function resolveWorkspaceRoot(ctx, workspaceId) {
163
178
  }
164
179
  return null;
165
180
  }
166
- export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, createGitManagementFeature, } from './features/index.js';
181
+ function readWorkspaceRoots(ctx) {
182
+ try {
183
+ const registry = ctx.get('workspaceRegistry');
184
+ return (registry?.list?.() ?? []).flatMap((entry) => {
185
+ for (const key of ['rootPath', 'path', 'cwd', 'directory']) {
186
+ const value = entry[key];
187
+ if (typeof value === 'string' && value.trim() !== '')
188
+ return [value.trim()];
189
+ }
190
+ return [];
191
+ });
192
+ }
193
+ catch {
194
+ return [];
195
+ }
196
+ }
197
+ export { HOST_FEATURES, createAuthFeature, createLanAccessDshFeature, createTerminalStatusFeature, createHostFeatures, createCliAdaptersFeature, createTerminalProcessFeature, createGitManagementFeature, createFileManagementFeature, createPeerHostFeature, } from './features/index.js';
167
198
  export { CodingNsSettingsSchema, registerCodingNsSettings } from './settings.js';
168
199
  export { createCodingNsRpcHandler, createCodingNsSettingsRpcHandler, registerCodingNsRpc } from './rpc.js';
169
200
  export { DebugWorkspaceService, NodeDebugPortInspector, } from './debug.js';
@@ -197,4 +228,7 @@ export { createLocalDshWebRuntimeProvider, createRemoteWebRuntimeFeature, } from
197
228
  export { LanAccessDshError, LanAccessDshProxy, FileLanAccessDshLoginStore, InMemoryLanAccessDshLoginStore, createLanAccessDshRpcHandler, createNodeLanAccessDshRuntime, normalizeLanAccessDshConfig, openLoginProtectionSession, verifyLoginProtectionSession, } from './lan-access-dsh.js';
198
229
  export { CODINGNS_TUNNEL_DATA_CHANNEL_LABEL, FileHostDtlsIdentityStore, createRegisteredHostSignalingSocket, createWeriftPeerConnectionFactory, ensureHostDtlsIdentity, formatHostDtlsFingerprint, generateHostDtlsIdentity, startHostRelayRuntime, } from './relay-tunnel-runtime.js';
199
230
  export * from './terminal/index.js';
231
+ export { PeerHostWebSocketGateway, PEER_HOST_WS_PATH } from './modules/peer-host/peer-host-ws-gateway.js';
232
+ export { createPeerHostRelayConnector, PeerHostReconnectManager, } from './modules/peer-host/peer-host-relay.js';
233
+ export { createPeerHostDiagnosticSink, peerHostSafeError, toPeerHostDiagnosticSnapshot, } from './modules/peer-host/peer-host-diagnostics.js';
200
234
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,4 @@
1
+ import { Transform, type TransformCallback } from 'node:stream';
1
2
  import type { SettingsScope } from '@deepseek-ai/dsh-settings';
2
3
  import type { CodingNsSettings, LanAccessDshLoginSettings, LoginProtectionScopes } from '../shared/contracts/config.js';
3
4
  import type { LanAccessDshConfig, LanAccessDshLoginConfig, LanAccessDshSnapshot } from '../shared/contracts/lan-access-dsh.js';
@@ -22,11 +23,22 @@ export interface LanAccessDshLoginStore {
22
23
  write(record: LanAccessDshLoginRecord): Promise<void>;
23
24
  clear(): Promise<void>;
24
25
  }
26
+ /**
27
+ * Cookie 不包含端口信息;多个 DSH 实例若共用名称,浏览器会在实例之间覆盖会话。
28
+ * 正式环境保留旧名称,开发环境由启动包装命令显式指定独立名称。
29
+ */
30
+ export declare const DEFAULT_LOGIN_PROTECTION_COOKIE_NAME = "dsh_codingns_session";
31
+ export declare function resolveLoginProtectionCookieName(configured?: string | undefined): string;
25
32
  /** 浏览器中继只携带短期签名票据,密码哈希和签名密钥始终留在 Host。 */
26
33
  export declare function openLoginProtectionSession(store: LanAccessDshLoginStore, username: string, password: string, scope: keyof LoginProtectionScopes): Promise<{
27
34
  token: string;
28
35
  expiresAt: string;
29
36
  }>;
37
+ /** 在旧票据仍有效时滚动续签;过期票据不能借此恢复登录。 */
38
+ export declare function refreshLoginProtectionSession(store: LanAccessDshLoginStore, token: string | undefined, scope: keyof LoginProtectionScopes): Promise<{
39
+ token: string;
40
+ expiresAt: string;
41
+ }>;
30
42
  /** 校验中继票据;配置关闭或未覆盖该范围时保持向后兼容,直接放行。 */
31
43
  export declare function verifyLoginProtectionSession(store: LanAccessDshLoginStore, token: string | undefined, scope: keyof LoginProtectionScopes): Promise<boolean>;
32
44
  /** 测试和嵌入式宿主使用的内存凭据存储。 */
@@ -56,7 +68,8 @@ export declare class LanAccessDshProxy {
56
68
  /** 当前进程内主动退出的会话;正常会话由签名令牌承载,可跨代理重建复用。 */
57
69
  private readonly revokedSessions;
58
70
  private upstreamCookie;
59
- constructor(runtime?: LanAccessDshRuntime, authenticatedUrl?: string | undefined);
71
+ constructor(runtime?: LanAccessDshRuntime, authenticatedUrl?: string | undefined, sessionCookieName?: string);
72
+ private readonly sessionCookieName;
60
73
  listenHosts(): readonly string[];
61
74
  detect(): Promise<readonly number[]>;
62
75
  start(input: Partial<LanAccessDshConfig>): Promise<LanAccessDshSnapshot>;
@@ -72,6 +85,26 @@ export declare class LanAccessDshProxy {
72
85
  /** DSH 启动时的认证 URL 只在 Host 内交换一次 Cookie,绝不下发到浏览器。 */
73
86
  private takeOverDshWebToken;
74
87
  }
88
+ /**
89
+ * 将局域网入口发往上游 DSH 的 HTTP 请求头改成上游自身的 authority。
90
+ *
91
+ * 代理是单用途的:每个普通 HTTP 请求都要求上游关闭连接,避免在不知道
92
+ * Content-Length/分块边界时误把 keep-alive 上的第二个请求当成正文。WebSocket
93
+ * 升级请求则保持 Upgrade,升级后的帧不再经过头部解析。
94
+ */
95
+ export declare class LanAccessDshRequestTransform extends Transform {
96
+ private readonly targetAuthority;
97
+ private readonly upstreamCookie;
98
+ private pending;
99
+ private pendingRequest;
100
+ private finished;
101
+ constructor(targetAuthority: string, upstreamCookie?: string | null);
102
+ _transform(chunk: Uint8Array, _encoding: string, callback: TransformCallback): void;
103
+ _flush(callback: TransformCallback): void;
104
+ private drainPending;
105
+ }
106
+ /** 兼容旧版 DSH Web Client 直接把 RPC 参数放入 payload 的请求格式。 */
107
+ export declare function normalizeLanAccessDshRpcBody(path: string, method: string, body: Uint8Array): Uint8Array;
75
108
  /** 改写请求中的 Host、Origin,并关闭普通 HTTP 上游连接。 */
76
109
  export declare function rewriteLanAccessDshRequestHeaders(input: Uint8Array, targetAuthority: string, upstreamCookie?: string): Uint8Array;
77
110
  export declare class LanAccessDshError extends Error {