@jingyi0605/codingns4dsh 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/README.md +1114 -0
- package/assets/provider-icons/claude-code.png +0 -0
- package/assets/provider-icons/codex.png +0 -0
- package/assets/provider-icons/command-code.svg +5 -0
- package/assets/provider-icons/deepseek-harness.svg +9 -0
- package/assets/provider-icons/gemini.png +0 -0
- package/assets/provider-icons/grok.png +0 -0
- package/assets/provider-icons/kimi.png +0 -0
- package/assets/provider-icons/opencode.png +0 -0
- package/assets/provider-icons/pi.svg +7 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +97 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.js +169 -0
- package/data/build/dist/bootstrap/index.d.ts +38 -0
- package/data/build/dist/bootstrap/index.js +44 -0
- package/data/build/dist/client/account-bar.d.ts +9 -0
- package/data/build/dist/client/account-bar.js +398 -0
- package/data/build/dist/client/bundle.js +26167 -0
- package/data/build/dist/client/cli-catalog.d.ts +24 -0
- package/data/build/dist/client/cli-catalog.js +108 -0
- package/data/build/dist/client/cli-slots.d.ts +38 -0
- package/data/build/dist/client/cli-slots.js +297 -0
- package/data/build/dist/client/debug/ui.d.ts +12 -0
- package/data/build/dist/client/debug/ui.js +451 -0
- package/data/build/dist/client/dsh-h5-bootstrap.d.ts +64 -0
- package/data/build/dist/client/dsh-h5-bootstrap.js +293 -0
- package/data/build/dist/client/dsh-runtime-version.d.ts +3 -0
- package/data/build/dist/client/dsh-runtime-version.js +11 -0
- package/data/build/dist/client/external-tool-stream.d.ts +24 -0
- package/data/build/dist/client/external-tool-stream.js +114 -0
- package/data/build/dist/client/features/cli-adapters.d.ts +7 -0
- package/data/build/dist/client/features/cli-adapters.js +215 -0
- package/data/build/dist/client/features/debug.d.ts +4 -0
- package/data/build/dist/client/features/debug.js +24 -0
- package/data/build/dist/client/features/index.d.ts +33 -0
- package/data/build/dist/client/features/index.js +44 -0
- package/data/build/dist/client/features/lan-access-panel.d.ts +5 -0
- package/data/build/dist/client/features/lan-access-panel.js +146 -0
- package/data/build/dist/client/features/lan-access.d.ts +9 -0
- package/data/build/dist/client/features/lan-access.js +30 -0
- package/data/build/dist/client/features/login-protection-panel.d.ts +5 -0
- package/data/build/dist/client/features/login-protection-panel.js +91 -0
- package/data/build/dist/client/features/login-protection-session.d.ts +6 -0
- package/data/build/dist/client/features/login-protection-session.js +25 -0
- package/data/build/dist/client/features/login-protection.d.ts +4 -0
- package/data/build/dist/client/features/login-protection.js +23 -0
- package/data/build/dist/client/features/reverse-proxy-panel.d.ts +9 -0
- package/data/build/dist/client/features/reverse-proxy-panel.js +159 -0
- package/data/build/dist/client/features/reverse-proxy.d.ts +12 -0
- package/data/build/dist/client/features/reverse-proxy.js +112 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.js +160 -0
- package/data/build/dist/client/features/terminal-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/terminal-enhancement.js +24 -0
- package/data/build/dist/client/features/types.d.ts +58 -0
- package/data/build/dist/client/features/types.js +2 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +66 -0
- package/data/build/dist/client/features/workspace-session-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/workspace-session-enhancement.js +101 -0
- package/data/build/dist/client/h5-bootstrap-entry.d.ts +2 -0
- package/data/build/dist/client/h5-bootstrap-entry.js +17 -0
- package/data/build/dist/client/index.d.ts +31 -0
- package/data/build/dist/client/index.js +98 -0
- package/data/build/dist/client/lan-access.d.ts +28 -0
- package/data/build/dist/client/lan-access.js +68 -0
- package/data/build/dist/client/locale.d.ts +20 -0
- package/data/build/dist/client/locale.js +393 -0
- package/data/build/dist/client/provider-icon-assets.d.ts +2 -0
- package/data/build/dist/client/provider-icon-assets.js +23 -0
- package/data/build/dist/client/provider-icons.d.ts +19 -0
- package/data/build/dist/client/provider-icons.js +39 -0
- package/data/build/dist/client/remote-web-context.d.ts +57 -0
- package/data/build/dist/client/remote-web-context.js +939 -0
- package/data/build/dist/client/session-adapter-cache.d.ts +18 -0
- package/data/build/dist/client/session-adapter-cache.js +84 -0
- package/data/build/dist/client/settings-bridge.d.ts +32 -0
- package/data/build/dist/client/settings-bridge.js +126 -0
- package/data/build/dist/client/settings-section.d.ts +30 -0
- package/data/build/dist/client/settings-section.js +93 -0
- package/data/build/dist/client/subscription-slot.d.ts +18 -0
- package/data/build/dist/client/subscription-slot.js +223 -0
- package/data/build/dist/client/terminal/index.d.ts +4 -0
- package/data/build/dist/client/terminal/index.js +3 -0
- package/data/build/dist/client/terminal/model.d.ts +116 -0
- package/data/build/dist/client/terminal/model.js +435 -0
- package/data/build/dist/client/terminal/styles.d.ts +21 -0
- package/data/build/dist/client/terminal/styles.js +51 -0
- package/data/build/dist/client/terminal/ui.d.ts +19 -0
- package/data/build/dist/client/terminal/ui.js +257 -0
- package/data/build/dist/client/terminal/xterm-view.d.ts +13 -0
- package/data/build/dist/client/terminal/xterm-view.js +213 -0
- package/data/build/dist/client/theme.d.ts +56 -0
- package/data/build/dist/client/theme.js +186 -0
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +29 -0
- package/data/build/dist/client/workspace-session-archive-dom.js +525 -0
- package/data/build/dist/client/workspace-session-fiber.d.ts +3 -0
- package/data/build/dist/client/workspace-session-fiber.js +33 -0
- package/data/build/dist/client/workspace-session-logo-dom.d.ts +26 -0
- package/data/build/dist/client/workspace-session-logo-dom.js +128 -0
- package/data/build/dist/features/index.d.ts +3 -0
- package/data/build/dist/features/index.js +2 -0
- package/data/build/dist/features/registry.d.ts +74 -0
- package/data/build/dist/features/registry.js +382 -0
- package/data/build/dist/features/resource-scope/index.d.ts +39 -0
- package/data/build/dist/features/resource-scope/index.js +161 -0
- package/data/build/dist/host/auth-session.d.ts +42 -0
- package/data/build/dist/host/auth-session.js +186 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +14 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js +103 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/codex-driver.js +534 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.js +394 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +19 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.js +126 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts +41 -0
- package/data/build/dist/host/cli-adapters/driver.js +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +37 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +180 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +48 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +373 -0
- package/data/build/dist/host/cli-adapters/feature.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/feature.js +335 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.js +427 -0
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/grok-driver.js +372 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.js +99 -0
- package/data/build/dist/host/cli-adapters/interaction-events.d.ts +11 -0
- package/data/build/dist/host/cli-adapters/interaction-events.js +80 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +65 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +256 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +21 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.js +382 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/model-catalog.js +89 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +55 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +728 -0
- package/data/build/dist/host/cli-adapters/pi-driver.d.ts +43 -0
- package/data/build/dist/host/cli-adapters/pi-driver.js +312 -0
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/process-utils.js +27 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +82 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.js +610 -0
- package/data/build/dist/host/cli-adapters/registry.d.ts +95 -0
- package/data/build/dist/host/cli-adapters/registry.js +659 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +119 -0
- package/data/build/dist/host/cli-adapters/session-probe.d.ts +20 -0
- package/data/build/dist/host/cli-adapters/session-probe.js +139 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +68 -0
- package/data/build/dist/host/cli-adapters/session-store.js +180 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +205 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +25 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +83 -0
- package/data/build/dist/host/cli-adapters/tool-observation.d.ts +9 -0
- package/data/build/dist/host/cli-adapters/tool-observation.js +39 -0
- package/data/build/dist/host/control-api-client.d.ts +84 -0
- package/data/build/dist/host/control-api-client.js +130 -0
- package/data/build/dist/host/credential-store.d.ts +54 -0
- package/data/build/dist/host/credential-store.js +114 -0
- package/data/build/dist/host/debug.d.ts +79 -0
- package/data/build/dist/host/debug.js +295 -0
- package/data/build/dist/host/dsh-device-runtime.d.ts +39 -0
- package/data/build/dist/host/dsh-device-runtime.js +118 -0
- package/data/build/dist/host/dsh-gateway-feature.d.ts +5 -0
- package/data/build/dist/host/dsh-gateway-feature.js +55 -0
- package/data/build/dist/host/dsh-runtime-version.d.ts +12 -0
- package/data/build/dist/host/dsh-runtime-version.js +69 -0
- package/data/build/dist/host/features/auth.d.ts +11 -0
- package/data/build/dist/host/features/auth.js +273 -0
- package/data/build/dist/host/features/debug.d.ts +5 -0
- package/data/build/dist/host/features/debug.js +90 -0
- package/data/build/dist/host/features/host-status.d.ts +12 -0
- package/data/build/dist/host/features/host-status.js +54 -0
- package/data/build/dist/host/features/index.d.ts +19 -0
- package/data/build/dist/host/features/index.js +29 -0
- package/data/build/dist/host/features/lan-access-dsh.d.ts +8 -0
- package/data/build/dist/host/features/lan-access-dsh.js +45 -0
- package/data/build/dist/host/features/terminal-process.d.ts +5 -0
- package/data/build/dist/host/features/terminal-process.js +110 -0
- package/data/build/dist/host/features/terminal-status.d.ts +18 -0
- package/data/build/dist/host/features/terminal-status.js +56 -0
- package/data/build/dist/host/features/types.d.ts +41 -0
- package/data/build/dist/host/features/types.js +2 -0
- package/data/build/dist/host/index.d.ts +43 -0
- package/data/build/dist/host/index.js +135 -0
- package/data/build/dist/host/lan-access-dsh.d.ts +83 -0
- package/data/build/dist/host/lan-access-dsh.js +831 -0
- package/data/build/dist/host/native-session-bridge.d.ts +130 -0
- package/data/build/dist/host/native-session-bridge.js +317 -0
- package/data/build/dist/host/relay-tunnel-runtime.d.ts +97 -0
- package/data/build/dist/host/relay-tunnel-runtime.js +361 -0
- package/data/build/dist/host/remote-web-runtime.d.ts +78 -0
- package/data/build/dist/host/remote-web-runtime.js +559 -0
- package/data/build/dist/host/rpc-table.d.ts +32 -0
- package/data/build/dist/host/rpc-table.js +47 -0
- package/data/build/dist/host/rpc.d.ts +18 -0
- package/data/build/dist/host/rpc.js +248 -0
- package/data/build/dist/host/settings.d.ts +19 -0
- package/data/build/dist/host/settings.js +80 -0
- package/data/build/dist/host/terminal/attachment-registry.d.ts +17 -0
- package/data/build/dist/host/terminal/attachment-registry.js +49 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.d.ts +39 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.js +160 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts +36 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.js +178 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.js +191 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.d.ts +27 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.js +154 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.d.ts +10 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.js +147 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.d.ts +92 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.js +134 -0
- package/data/build/dist/host/terminal/controller-factory.d.ts +37 -0
- package/data/build/dist/host/terminal/controller-factory.js +82 -0
- package/data/build/dist/host/terminal/index.d.ts +17 -0
- package/data/build/dist/host/terminal/index.js +17 -0
- package/data/build/dist/host/terminal/runtime-adapter.d.ts +77 -0
- package/data/build/dist/host/terminal/runtime-adapter.js +18 -0
- package/data/build/dist/host/terminal/runtime-manager.d.ts +41 -0
- package/data/build/dist/host/terminal/runtime-manager.js +179 -0
- package/data/build/dist/host/terminal/shell-detection.d.ts +23 -0
- package/data/build/dist/host/terminal/shell-detection.js +105 -0
- package/data/build/dist/host/terminal/startup.d.ts +33 -0
- package/data/build/dist/host/terminal/startup.js +100 -0
- package/data/build/dist/host/terminal/terminal-controller.d.ts +78 -0
- package/data/build/dist/host/terminal/terminal-controller.js +324 -0
- package/data/build/dist/host/terminal/terminal-process-service.d.ts +30 -0
- package/data/build/dist/host/terminal/terminal-process-service.js +194 -0
- package/data/build/dist/host/terminal/terminal-process-store.d.ts +45 -0
- package/data/build/dist/host/terminal/terminal-process-store.js +183 -0
- package/data/build/dist/host/terminal/terminal-service.d.ts +87 -0
- package/data/build/dist/host/terminal/terminal-service.js +528 -0
- package/data/build/dist/host/terminal/terminal-store.d.ts +49 -0
- package/data/build/dist/host/terminal/terminal-store.js +242 -0
- package/data/build/dist/index.d.ts +37 -0
- package/data/build/dist/index.js +26 -0
- package/data/build/dist/shared/contracts/auth.d.ts +122 -0
- package/data/build/dist/shared/contracts/auth.js +8 -0
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +192 -0
- package/data/build/dist/shared/contracts/cli-adapter.js +2 -0
- package/data/build/dist/shared/contracts/command-code.d.ts +5 -0
- package/data/build/dist/shared/contracts/command-code.js +2 -0
- package/data/build/dist/shared/contracts/config.d.ts +113 -0
- package/data/build/dist/shared/contracts/config.js +86 -0
- package/data/build/dist/shared/contracts/debug-config.d.ts +28 -0
- package/data/build/dist/shared/contracts/debug-config.js +78 -0
- package/data/build/dist/shared/contracts/debug.d.ts +75 -0
- package/data/build/dist/shared/contracts/debug.js +121 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +72 -0
- package/data/build/dist/shared/contracts/dsh-device.js +2 -0
- package/data/build/dist/shared/contracts/errors.d.ts +33 -0
- package/data/build/dist/shared/contracts/errors.js +41 -0
- package/data/build/dist/shared/contracts/feature.d.ts +71 -0
- package/data/build/dist/shared/contracts/feature.js +2 -0
- package/data/build/dist/shared/contracts/host-status.d.ts +14 -0
- package/data/build/dist/shared/contracts/host-status.js +2 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.d.ts +30 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.js +2 -0
- package/data/build/dist/shared/contracts/peer-host.d.ts +37 -0
- package/data/build/dist/shared/contracts/peer-host.js +9 -0
- package/data/build/dist/shared/contracts/signaling.d.ts +67 -0
- package/data/build/dist/shared/contracts/signaling.js +2 -0
- package/data/build/dist/shared/contracts/subscription.d.ts +69 -0
- package/data/build/dist/shared/contracts/subscription.js +2 -0
- package/data/build/dist/shared/contracts/terminal-process.d.ts +65 -0
- package/data/build/dist/shared/contracts/terminal-process.js +2 -0
- package/data/build/dist/shared/contracts/terminal.d.ts +127 -0
- package/data/build/dist/shared/contracts/terminal.js +2 -0
- package/data/build/dist/shared/contracts/transport.d.ts +45 -0
- package/data/build/dist/shared/contracts/transport.js +3 -0
- package/data/build/dist/shared/contracts/version.d.ts +19 -0
- package/data/build/dist/shared/contracts/version.js +74 -0
- package/data/build/dist/shared/index.d.ts +22 -0
- package/data/build/dist/shared/index.js +9 -0
- package/data/build/dist/transport/carrier.d.ts +38 -0
- package/data/build/dist/transport/carrier.js +268 -0
- package/data/build/dist/transport/debug.d.ts +22 -0
- package/data/build/dist/transport/debug.js +57 -0
- package/data/build/dist/transport/dsh-envelope.d.ts +41 -0
- package/data/build/dist/transport/dsh-envelope.js +107 -0
- package/data/build/dist/transport/dsh-gateway.d.ts +64 -0
- package/data/build/dist/transport/dsh-gateway.js +254 -0
- package/data/build/dist/transport/dsh-session.d.ts +55 -0
- package/data/build/dist/transport/dsh-session.js +256 -0
- package/data/build/dist/transport/dsh-transport.d.ts +57 -0
- package/data/build/dist/transport/dsh-transport.js +125 -0
- package/data/build/dist/transport/frame.d.ts +132 -0
- package/data/build/dist/transport/frame.js +168 -0
- package/data/build/dist/transport/index.d.ts +11 -0
- package/data/build/dist/transport/index.js +11 -0
- package/data/build/dist/transport/multiplexer.d.ts +63 -0
- package/data/build/dist/transport/multiplexer.js +323 -0
- package/data/build/dist/transport/webrtc-client.d.ts +65 -0
- package/data/build/dist/transport/webrtc-client.js +255 -0
- package/data/build/dist/transport/webrtc-host.d.ts +58 -0
- package/data/build/dist/transport/webrtc-host.js +252 -0
- package/data/build/dist/typert.host.d.ts +25 -0
- package/data/build/dist/typert.host.js +150 -0
- package/dsh.bundle.patch +12 -0
- package/package.json +135 -0
- package/profile/README.md +27 -0
- package/profile/cordis.patch.yml +2 -0
- package/profile/package.json +20 -0
- package/profile/scripts/check-dsh-install.mjs +71 -0
- package/profile/version.json +5 -0
- package/scripts/check-dsh-install.mjs +88 -0
- package/version.json +6 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** 登录保护会话只放在当前浏览器标签页,关闭标签页即失效。 */
|
|
2
|
+
export const LOGIN_PROTECTION_SESSION_KEY = 'codingns4dsh.login-protection.relay-token';
|
|
3
|
+
export const LOGIN_PROTECTION_SESSION_EVENT = 'codingns4dsh-login-protection-session';
|
|
4
|
+
let memoryToken;
|
|
5
|
+
export function readLoginProtectionSession() {
|
|
6
|
+
try {
|
|
7
|
+
const value = globalThis.sessionStorage.getItem(LOGIN_PROTECTION_SESSION_KEY)?.trim();
|
|
8
|
+
return value === undefined || value === '' ? memoryToken : value;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return memoryToken;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function writeLoginProtectionSession(token) {
|
|
15
|
+
memoryToken = token === undefined || token === '' ? undefined : token;
|
|
16
|
+
try {
|
|
17
|
+
if (memoryToken === undefined)
|
|
18
|
+
globalThis.sessionStorage.removeItem(LOGIN_PROTECTION_SESSION_KEY);
|
|
19
|
+
else
|
|
20
|
+
globalThis.sessionStorage.setItem(LOGIN_PROTECTION_SESSION_KEY, memoryToken);
|
|
21
|
+
}
|
|
22
|
+
catch { /* 浏览器隐私模式禁用存储时,使用当前页面内存中的票据。 */ }
|
|
23
|
+
globalThis.dispatchEvent(new Event(LOGIN_PROTECTION_SESSION_EVENT));
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=login-protection-session.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LoginProtectionPanel } from './login-protection-panel.js';
|
|
2
|
+
/** 独立的本地账号登录保护模块,位于局域网和中继访问模块之间。 */
|
|
3
|
+
export const loginProtectionFeature = {
|
|
4
|
+
descriptor: {
|
|
5
|
+
name: 'loginProtection',
|
|
6
|
+
version: '0.2.0',
|
|
7
|
+
enabledByDefault: true,
|
|
8
|
+
dependencies: [],
|
|
9
|
+
runtime: 'client',
|
|
10
|
+
ui: {
|
|
11
|
+
label: '登录保护',
|
|
12
|
+
description: '为局域网和中继访问设置统一的本地账号验证,本机回环地址始终放行。',
|
|
13
|
+
labelKey: 'feature.loginProtection.label',
|
|
14
|
+
descriptionKey: 'feature.loginProtection.description',
|
|
15
|
+
order: 15,
|
|
16
|
+
alwaysEnabled: true,
|
|
17
|
+
defaultOpen: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
start: () => undefined,
|
|
21
|
+
settingsPanel: LoginProtectionPanel,
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=login-protection.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { FeaturePanelProps } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 「中转访问服务」卡片的设置面板:Control API 地址、登录和 DSH 独立设备。
|
|
5
|
+
*
|
|
6
|
+
* 密码只在单次 RPC 中经过 Host,表单不保存它;refresh token 只存在于 Host。
|
|
7
|
+
*/
|
|
8
|
+
export declare function ReverseProxyPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
|
|
9
|
+
//# sourceMappingURL=reverse-proxy-panel.d.ts.map
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { createElement, useEffect, useState } from 'react';
|
|
2
|
+
import { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, DEFAULT_CODINGNS_CONTROL_BASE_URLS, } from '../../shared/contracts/config.js';
|
|
3
|
+
import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
|
|
4
|
+
import { dshFormRootStyle, dshPopupSurfaceStyle, dshSettingsButtonStyle, dshSettingsFieldLabelStyle, dshSettingsFieldStyle, dshSettingsNoteStyle, dshSettingsPrimaryButtonStyle, dshSettingsRowStyle, dshThemeColor, } from '../theme.js';
|
|
5
|
+
import { useCodingNsTranslator } from '../locale.js';
|
|
6
|
+
/**
|
|
7
|
+
* 「中转访问服务」卡片的设置面板:Control API 地址、登录和 DSH 独立设备。
|
|
8
|
+
*
|
|
9
|
+
* 密码只在单次 RPC 中经过 Host,表单不保存它;refresh token 只存在于 Host。
|
|
10
|
+
*/
|
|
11
|
+
export function ReverseProxyPanel({ services, enabled, snapshot }) {
|
|
12
|
+
const { settings, rpc } = services;
|
|
13
|
+
const t = useCodingNsTranslator(services.locale);
|
|
14
|
+
const disabled = !enabled;
|
|
15
|
+
const controlsDisabled = disabled || snapshot.status === 'loading' || !snapshot.writable;
|
|
16
|
+
const [email, setEmail] = useState('');
|
|
17
|
+
const [password, setPassword] = useState('');
|
|
18
|
+
const [controlBaseUrl, setControlBaseUrl] = useState(resolveControlBaseUrl(snapshot.value?.controlBaseUrl));
|
|
19
|
+
const [controlBaseUrls, setControlBaseUrls] = useState(() => uniqueControlBaseUrls(snapshot.value?.controlBaseUrls, snapshot.value?.controlBaseUrl));
|
|
20
|
+
const [newControlBaseUrl, setNewControlBaseUrl] = useState('');
|
|
21
|
+
const [addAddressOpen, setAddAddressOpen] = useState(false);
|
|
22
|
+
const [addressError, setAddressError] = useState('');
|
|
23
|
+
const [auth, setAuth] = useState(loggedOutSnapshot());
|
|
24
|
+
const [devices, setDevices] = useState(null);
|
|
25
|
+
const [selectedDeviceId, setSelectedDeviceId] = useState('');
|
|
26
|
+
const [busy, setBusy] = useState(false);
|
|
27
|
+
const [message, setMessage] = useState('');
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const saved = snapshot.value?.controlBaseUrl;
|
|
30
|
+
const resolved = resolveControlBaseUrl(saved);
|
|
31
|
+
setControlBaseUrls(uniqueControlBaseUrls(snapshot.value?.controlBaseUrls, resolved));
|
|
32
|
+
setControlBaseUrl(resolved);
|
|
33
|
+
if (snapshot.status === 'ready' && snapshot.writable && saved !== resolved) {
|
|
34
|
+
void settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, resolved).catch(() => undefined);
|
|
35
|
+
}
|
|
36
|
+
}, [settings, snapshot.status, snapshot.writable, snapshot.value?.controlBaseUrl, snapshot.value?.controlBaseUrls]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
void callCodingNsRpc(rpc, 'auth/snapshot', {})
|
|
39
|
+
.then(setAuth)
|
|
40
|
+
.catch(() => setAuth(loggedOutSnapshot()));
|
|
41
|
+
}, [rpc]);
|
|
42
|
+
// 设置页打开时 Host 可能正在恢复登录会话;认证快照变为 authenticated
|
|
43
|
+
// 后立即刷新设备列表,避免用户必须再点一次“刷新设备”。
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!enabled || auth.status !== 'authenticated')
|
|
46
|
+
return;
|
|
47
|
+
void loadDevicesInternal().catch((error) => {
|
|
48
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
49
|
+
});
|
|
50
|
+
}, [auth.status, enabled, rpc]);
|
|
51
|
+
const run = async (operation) => {
|
|
52
|
+
setBusy(true);
|
|
53
|
+
setMessage('');
|
|
54
|
+
try {
|
|
55
|
+
await operation();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
setBusy(false);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const login = () => run(async () => {
|
|
65
|
+
const selectedUrl = normalizeControlBaseUrl(controlBaseUrl);
|
|
66
|
+
const nextUrls = uniqueControlBaseUrls(controlBaseUrls, selectedUrl);
|
|
67
|
+
await settings.set(CODINGNS_CONTROL_BASE_URLS_FIELD, nextUrls);
|
|
68
|
+
await settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, selectedUrl);
|
|
69
|
+
const next = await callCodingNsRpc(rpc, 'auth/login', {
|
|
70
|
+
controlBaseUrl: selectedUrl,
|
|
71
|
+
email,
|
|
72
|
+
password,
|
|
73
|
+
});
|
|
74
|
+
setPassword('');
|
|
75
|
+
setAuth(next);
|
|
76
|
+
setMessage(t('relay.loginSuccess'));
|
|
77
|
+
});
|
|
78
|
+
const logout = () => run(async () => {
|
|
79
|
+
await callCodingNsRpc(rpc, 'auth/logout', {});
|
|
80
|
+
setAuth(loggedOutSnapshot());
|
|
81
|
+
setDevices(null);
|
|
82
|
+
setSelectedDeviceId('');
|
|
83
|
+
setMessage(t('relay.loggedOut'));
|
|
84
|
+
});
|
|
85
|
+
const loadDevices = () => run(async () => {
|
|
86
|
+
await loadDevicesInternal();
|
|
87
|
+
});
|
|
88
|
+
async function loadDevicesInternal() {
|
|
89
|
+
const next = await callCodingNsRpc(rpc, 'auth/dsh/device/list', {});
|
|
90
|
+
setDevices(next);
|
|
91
|
+
const preferred = next.devices.find((device) => device.online && device.status === 'active')?.dshDeviceId ?? '';
|
|
92
|
+
setSelectedDeviceId(preferred);
|
|
93
|
+
}
|
|
94
|
+
const addControlBaseUrl = async () => {
|
|
95
|
+
setBusy(true);
|
|
96
|
+
setAddressError('');
|
|
97
|
+
try {
|
|
98
|
+
const addedUrl = normalizeControlBaseUrl(newControlBaseUrl);
|
|
99
|
+
const nextUrls = uniqueControlBaseUrls(controlBaseUrls, addedUrl);
|
|
100
|
+
await settings.set(CODINGNS_CONTROL_BASE_URLS_FIELD, nextUrls);
|
|
101
|
+
await settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, addedUrl);
|
|
102
|
+
setControlBaseUrls(nextUrls);
|
|
103
|
+
setControlBaseUrl(addedUrl);
|
|
104
|
+
setNewControlBaseUrl('');
|
|
105
|
+
setAddAddressOpen(false);
|
|
106
|
+
setMessage(t('relay.add'));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
setAddressError(error instanceof Error ? error.message : String(error));
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
setBusy(false);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const chooseControlBaseUrl = (value) => {
|
|
116
|
+
setControlBaseUrl(value);
|
|
117
|
+
void settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, value).catch((error) => {
|
|
118
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const fieldStyle = dshSettingsFieldStyle;
|
|
122
|
+
const buttonStyle = dshSettingsButtonStyle;
|
|
123
|
+
const authenticated = auth.status === 'authenticated';
|
|
124
|
+
return createElement('div', { 'aria-disabled': controlsDisabled, style: { ...dshFormRootStyle, display: 'flex', flexDirection: 'column', gap: 16, opacity: controlsDisabled ? 0.5 : 1, pointerEvents: controlsDisabled ? 'none' : 'auto' } }, createElement('div', undefined, createElement('h3', { style: { margin: 0, fontSize: 17 } }, t('relay.settings')), createElement('p', { style: { margin: '6px 0 0', color: dshThemeColor.labelSecondary, fontSize: 13, lineHeight: 1.5 } }, t('relay.loginHint'))), createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.controlApi')), createElement('div', { style: dshSettingsRowStyle }, createElement('select', { value: controlBaseUrl, disabled: controlsDisabled || busy, onChange: (event) => chooseControlBaseUrl(event.currentTarget.value), style: { ...fieldStyle, flex: 1, minWidth: 0 } }, ...controlBaseUrls.map((url) => createElement('option', { key: url, value: url }, url))), createElement('button', { type: 'button', 'aria-haspopup': 'dialog', disabled: controlsDisabled || busy, onClick: () => { setAddressError(''); setAddAddressOpen(true); }, style: { ...buttonStyle, flex: '0 0 auto' } }, t('relay.add')))), addAddressOpen && createElement('div', { role: 'dialog', 'aria-modal': true, 'aria-labelledby': 'codingns-add-address-title', style: { position: 'fixed', inset: 0, zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, background: dshThemeColor.overlay } }, createElement('div', { style: { ...dshPopupSurfaceStyle, width: 'min(100%, 480px)', boxSizing: 'border-box', padding: 24, borderRadius: 8 } }, createElement('h3', { id: 'codingns-add-address-title', style: { margin: 0, fontSize: 18 } }, t('relay.addServer')), createElement('p', { style: { margin: '8px 0 16px', opacity: 0.7 } }, t('relay.addServerHint')), createElement('input', { type: 'url', autoFocus: true, value: newControlBaseUrl, placeholder: 'https://example.com:1443', disabled: disabled || busy, onChange: (event) => setNewControlBaseUrl(event.currentTarget.value), style: fieldStyle }), addressError && createElement('div', { role: 'alert', style: { marginTop: 8, color: dshThemeColor.error } }, addressError), createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 20 } }, createElement('button', { type: 'button', disabled: busy, onClick: () => { setAddAddressOpen(false); setAddressError(''); }, style: buttonStyle }, t('relay.cancel')), createElement('button', { type: 'button', disabled: busy || !newControlBaseUrl.trim(), onClick: () => void addControlBaseUrl(), style: buttonStyle }, busy ? t('relay.adding') : t('relay.add'))))), !authenticated && createElement('form', { onSubmit: (event) => { event.preventDefault(); void login(); }, style: { display: 'flex', flexDirection: 'column', gap: 12 } }, createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.email')), createElement('input', { type: 'email', autoComplete: 'username', value: email, disabled: controlsDisabled || busy, onChange: (event) => setEmail(event.currentTarget.value), style: fieldStyle })), createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.password')), createElement('input', { type: 'password', autoComplete: 'current-password', value: password, disabled: controlsDisabled || busy, onChange: (event) => setPassword(event.currentTarget.value), style: fieldStyle })), createElement('button', { type: 'submit', disabled: controlsDisabled || busy || !controlBaseUrl || !email || !password, style: dshSettingsPrimaryButtonStyle }, busy ? t('relay.loggingIn') : t('relay.login'))), authenticated && createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } }, createElement('div', { style: dshSettingsNoteStyle }, createElement('strong', undefined, auth.account?.email ?? t('relay.loggedIn')), createElement('div', { style: { marginTop: 6, opacity: 0.7 } }, t('relay.device', { value: devices?.devices.find((device) => device.dshDeviceId === selectedDeviceId)?.displayName ?? t('relay.unrecognized') })), createElement('div', { style: { marginTop: 4, opacity: 0.7 } }, t('relay.host', { value: selectedDeviceId || t('relay.unbound') }))), createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } }, createElement('button', { type: 'button', disabled: controlsDisabled || busy, onClick: () => void loadDevices(), style: buttonStyle }, t('relay.refreshDevices')), createElement('button', { type: 'button', disabled: controlsDisabled || busy, onClick: () => void logout(), style: buttonStyle }, t('relay.logout'))), devices && createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } }, createElement('strong', { style: dshSettingsFieldLabelStyle }, t('relay.dshDevices')), createElement('select', { value: selectedDeviceId, disabled: controlsDisabled || busy, onChange: (event) => setSelectedDeviceId(event.currentTarget.value), style: fieldStyle }, createElement('option', { value: '' }, t('relay.selectDevice')), ...devices.devices.map((device) => createElement('option', { key: device.dshDeviceId, value: device.dshDeviceId, disabled: !device.online || device.status !== 'active' }, `${device.displayName} · ${device.online ? t('relay.online') : t('relay.offline')}`))), createElement('div', { style: { fontSize: 13, opacity: 0.75 } }, t('relay.devicesSummary', { current: selectedDeviceId || t('relay.unknown'), count: devices.devices.length })))), message && createElement('div', { role: 'status', style: { color: message.includes('成功') ? dshThemeColor.success : dshThemeColor.error } }, message));
|
|
125
|
+
}
|
|
126
|
+
async function callCodingNsRpc(rpc, endpoint, payload) {
|
|
127
|
+
let response;
|
|
128
|
+
try {
|
|
129
|
+
response = await rpc.call(CODINGNS_RPC_CHANNEL, endpoint, payload);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
+
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
134
|
+
throw error;
|
|
135
|
+
response = await rpc.call('/api', `codingns/${endpoint}`, payload);
|
|
136
|
+
}
|
|
137
|
+
if (!response.ok)
|
|
138
|
+
throw new Error(response.error.message);
|
|
139
|
+
return response.value;
|
|
140
|
+
}
|
|
141
|
+
function loggedOutSnapshot() {
|
|
142
|
+
return { status: 'logged_out', account: null, currentDevice: null, binding: null, expiresAt: null, errorCode: null };
|
|
143
|
+
}
|
|
144
|
+
function uniqueControlBaseUrls(saved, selected) {
|
|
145
|
+
const values = [...(saved ?? DEFAULT_CODINGNS_CONTROL_BASE_URLS), selected ?? ''];
|
|
146
|
+
return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))];
|
|
147
|
+
}
|
|
148
|
+
function resolveControlBaseUrl(value) {
|
|
149
|
+
const trimmed = value?.trim();
|
|
150
|
+
return trimmed || DEFAULT_CODINGNS_CONTROL_BASE_URLS[0] || '';
|
|
151
|
+
}
|
|
152
|
+
function normalizeControlBaseUrl(value) {
|
|
153
|
+
const trimmed = value.trim();
|
|
154
|
+
const parsed = new URL(trimmed);
|
|
155
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
|
|
156
|
+
throw new TypeError('Control API 地址必须使用 HTTP(S)');
|
|
157
|
+
return parsed.toString().replace(/\/+$/u, '');
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=reverse-proxy-panel.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CodingNsClientFeatureModule } from './types.js';
|
|
2
|
+
import type { CodingNsRpcClient } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 中转访问服务模块。
|
|
5
|
+
*
|
|
6
|
+
* 它把 DSH 页面接入 DSH-Codingns4DSH 独立设备隧道。登录和设备列表由 Host RPC 提供,
|
|
7
|
+
* 连接状态和设备选择由设置面板承载;隧道本身在模块启用时建立。
|
|
8
|
+
*/
|
|
9
|
+
export declare const reverseProxyFeature: CodingNsClientFeatureModule;
|
|
10
|
+
/** 浏览器侧真实中继连接;refresh token 和 access token 只经过 Host RPC。 */
|
|
11
|
+
export declare function startBrowserRelayConnection(rpc: CodingNsRpcClient, signal: AbortSignal, loginProtectionToken?: string): Promise<() => Promise<void>>;
|
|
12
|
+
//# sourceMappingURL=reverse-proxy.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ReverseProxyPanel } from './reverse-proxy-panel.js';
|
|
2
|
+
import { startDshH5Bootstrap } from '../dsh-h5-bootstrap.js';
|
|
3
|
+
import { LOGIN_PROTECTION_SESSION_EVENT, readLoginProtectionSession } from './login-protection-session.js';
|
|
4
|
+
import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
|
|
5
|
+
function isRemoteWebContext() {
|
|
6
|
+
return globalThis.__CODINGNS4DSH_REMOTE_WEB_CONTEXT__ === true;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 中转访问服务模块。
|
|
10
|
+
*
|
|
11
|
+
* 它把 DSH 页面接入 DSH-Codingns4DSH 独立设备隧道。登录和设备列表由 Host RPC 提供,
|
|
12
|
+
* 连接状态和设备选择由设置面板承载;隧道本身在模块启用时建立。
|
|
13
|
+
*/
|
|
14
|
+
export const reverseProxyFeature = {
|
|
15
|
+
descriptor: {
|
|
16
|
+
name: 'reverseProxy',
|
|
17
|
+
version: '0.1.0',
|
|
18
|
+
enabledByDefault: false,
|
|
19
|
+
dependencies: [],
|
|
20
|
+
runtime: 'client',
|
|
21
|
+
ui: {
|
|
22
|
+
label: '中转访问服务',
|
|
23
|
+
description: '通过 DSH-Codingns4DSH 独立设备隧道访问 Host。',
|
|
24
|
+
labelKey: 'feature.reverseProxy.label',
|
|
25
|
+
descriptionKey: 'feature.reverseProxy.description',
|
|
26
|
+
order: 20,
|
|
27
|
+
defaultOpen: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* 隧道连接属于后续阶段,当前模块只提供配置面,因此这里不创建任何资源。
|
|
32
|
+
* 接入连接后,流与订阅必须登记到 context.resources,由停用自动清理。
|
|
33
|
+
*/
|
|
34
|
+
start(context) {
|
|
35
|
+
if (isRemoteWebContext()) {
|
|
36
|
+
// 远程 DSH Web 的外层已经拥有有效 Tunnel;这里只保留设置 UI 和其它
|
|
37
|
+
// 插件贡献,禁止同一页面重新申请票据并建立第二条中继连接。
|
|
38
|
+
return () => { };
|
|
39
|
+
}
|
|
40
|
+
const abort = new AbortController();
|
|
41
|
+
let disposeConnection;
|
|
42
|
+
let retryTimer;
|
|
43
|
+
let stopped = false;
|
|
44
|
+
let waitingForLogin = false;
|
|
45
|
+
const onLoginProtectionSession = () => {
|
|
46
|
+
waitingForLogin = false;
|
|
47
|
+
void attempt();
|
|
48
|
+
};
|
|
49
|
+
globalThis.addEventListener(LOGIN_PROTECTION_SESSION_EVENT, onLoginProtectionSession);
|
|
50
|
+
const attempt = async () => {
|
|
51
|
+
if (stopped || waitingForLogin)
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
const protection = await readLoginProtectionSettings(context.services.rpc);
|
|
55
|
+
const loginProtectionToken = readLoginProtectionSession();
|
|
56
|
+
if (protection.enabled && protection.scopes.relay && loginProtectionToken === undefined) {
|
|
57
|
+
waitingForLogin = true;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const dispose = await startBrowserRelayConnection(context.services.rpc, abort.signal, loginProtectionToken);
|
|
61
|
+
if (stopped || abort.signal.aborted) {
|
|
62
|
+
await dispose();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
disposeConnection = dispose;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (stopped || abort.signal.aborted)
|
|
69
|
+
return;
|
|
70
|
+
console.error('codingns4dsh: 中继连接建立失败,将在稍后重试', error);
|
|
71
|
+
retryTimer = setTimeout(() => { void attempt(); }, 5_000);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
void attempt();
|
|
75
|
+
return async () => {
|
|
76
|
+
stopped = true;
|
|
77
|
+
globalThis.removeEventListener(LOGIN_PROTECTION_SESSION_EVENT, onLoginProtectionSession);
|
|
78
|
+
abort.abort();
|
|
79
|
+
if (retryTimer !== undefined)
|
|
80
|
+
clearTimeout(retryTimer);
|
|
81
|
+
await disposeConnection?.();
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
settingsPanel: ReverseProxyPanel,
|
|
85
|
+
};
|
|
86
|
+
/** 浏览器侧真实中继连接;refresh token 和 access token 只经过 Host RPC。 */
|
|
87
|
+
export async function startBrowserRelayConnection(rpc, signal, loginProtectionToken) {
|
|
88
|
+
const bootstrap = await startDshH5Bootstrap({ rpc, signal, ...(loginProtectionToken === undefined ? {} : { loginProtectionToken }) });
|
|
89
|
+
const state = globalThis;
|
|
90
|
+
state.__CODINGNS4DSH_RELAY_MODE__ = bootstrap.relayMode;
|
|
91
|
+
return async () => {
|
|
92
|
+
if (state.__CODINGNS4DSH_RELAY_MODE__ === bootstrap.relayMode)
|
|
93
|
+
delete state.__CODINGNS4DSH_RELAY_MODE__;
|
|
94
|
+
await bootstrap.dispose();
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function readLoginProtectionSettings(rpc) {
|
|
98
|
+
let response;
|
|
99
|
+
try {
|
|
100
|
+
response = await rpc.call(CODINGNS_RPC_CHANNEL, 'lanAccessDsh/login/get', {});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
+
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
105
|
+
throw error;
|
|
106
|
+
response = await rpc.call('/api', 'codingns/lanAccessDsh/login/get', {});
|
|
107
|
+
}
|
|
108
|
+
if (!response.ok)
|
|
109
|
+
throw new Error(response.error.message);
|
|
110
|
+
return response.value;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=reverse-proxy.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { FeaturePanelProps } from './types.js';
|
|
3
|
+
/** 终端默认行为与外观设置;终端 UI 由独立 Sidebar 模块负责。 */
|
|
4
|
+
export declare function TerminalEnhancementPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
|
|
5
|
+
//# sourceMappingURL=terminal-enhancement-panel.d.ts.map
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createElement, useEffect, useState } from 'react';
|
|
2
|
+
import { CODINGNS_TERMINAL_ENHANCEMENT_FIELD, DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS, } from '../../shared/contracts/config.js';
|
|
3
|
+
import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
|
|
4
|
+
import { dshFormRootStyle, dshSettingsButtonStyle, dshSettingsFieldLabelStyle, dshSettingsFieldStyle, dshSettingsHelpStyle, dshSettingsNoteStyle, dshSettingsRowStyle, dshThemeColor, } from '../theme.js';
|
|
5
|
+
import { useCodingNsTranslator } from '../locale.js';
|
|
6
|
+
const profileLabels = {
|
|
7
|
+
system: 'terminal.systemRecommended',
|
|
8
|
+
zsh: 'zsh',
|
|
9
|
+
bash: 'bash',
|
|
10
|
+
powershell: 'PowerShell',
|
|
11
|
+
cmd: 'cmd',
|
|
12
|
+
'git-bash': 'Git Bash',
|
|
13
|
+
};
|
|
14
|
+
/** 终端默认行为与外观设置;终端 UI 由独立 Sidebar 模块负责。 */
|
|
15
|
+
export function TerminalEnhancementPanel({ services, enabled, snapshot }) {
|
|
16
|
+
const t = useCodingNsTranslator(services.locale);
|
|
17
|
+
const value = snapshot.value?.terminalEnhancement ?? DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS;
|
|
18
|
+
const [fontFamily, setFontFamily] = useState(value.appearance.fontFamily ?? '');
|
|
19
|
+
const [message, setMessage] = useState('');
|
|
20
|
+
const [hostStatus, setHostStatus] = useState(null);
|
|
21
|
+
const disabled = !enabled || snapshot.status === 'loading' || !snapshot.writable;
|
|
22
|
+
const customDisabled = disabled || value.appearance.theme !== 'custom';
|
|
23
|
+
useEffect(() => setFontFamily(value.appearance.fontFamily ?? ''), [value.appearance.fontFamily]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
let active = true;
|
|
26
|
+
void callTerminalStatus(services.rpc)
|
|
27
|
+
.then((status) => { if (active)
|
|
28
|
+
setHostStatus(status); })
|
|
29
|
+
.catch((error) => {
|
|
30
|
+
if (active)
|
|
31
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
32
|
+
});
|
|
33
|
+
return () => { active = false; };
|
|
34
|
+
}, [services.rpc]);
|
|
35
|
+
const save = (next) => {
|
|
36
|
+
setMessage('');
|
|
37
|
+
void services.settings.set(CODINGNS_TERMINAL_ENHANCEMENT_FIELD, next)
|
|
38
|
+
.then(() => setMessage(t('terminal.saved')))
|
|
39
|
+
.catch((error) => setMessage(error instanceof Error ? error.message : String(error)));
|
|
40
|
+
};
|
|
41
|
+
const updateAppearance = (patch) => {
|
|
42
|
+
save({ ...value, appearance: { ...value.appearance, ...patch } });
|
|
43
|
+
};
|
|
44
|
+
const saveFontFamily = () => {
|
|
45
|
+
try {
|
|
46
|
+
updateAppearance({ fontFamily: normalizeFontFamily(fontFamily) });
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return createElement('div', {
|
|
53
|
+
'aria-disabled': disabled,
|
|
54
|
+
style: { ...dshFormRootStyle, display: 'flex', flexDirection: 'column', gap: 16, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' },
|
|
55
|
+
}, createElement('div', { role: 'note', style: noteStyle }, t('terminal.restartNote')), createElement('div', { role: 'status', style: noteStyle }, hostStatus === null
|
|
56
|
+
? t('terminal.readingStatus')
|
|
57
|
+
: t('terminal.currentStatus', { status: hostStatus.effectiveEnabled ? t('terminal.enhanced') : t('terminal.basic'), platform: platformLabel(hostStatus.platform, t) })), createElement(Field, { label: t('terminal.newDefault') }, createElement('select', {
|
|
58
|
+
value: value.defaultProfile,
|
|
59
|
+
disabled,
|
|
60
|
+
onChange: (event) => save({ ...value, defaultProfile: event.currentTarget.value }),
|
|
61
|
+
style: fieldStyle,
|
|
62
|
+
}, ...profileOptions(value.defaultProfile, hostStatus, t).map((profile) => createElement('option', { key: profile.profileId, value: profile.profileId, disabled: !profile.available }, profile.label))), createElement('small', { style: helpStyle }, t('terminal.defaultProfileHelp')), hostStatus?.fallbackReason === undefined ? null : createElement('small', { style: helpStyle }, hostStatus.fallbackReason)), createElement(Field, { label: t('terminal.bindingScope') }, createElement('select', {
|
|
63
|
+
value: value.bindingScope ?? 'workspace',
|
|
64
|
+
disabled,
|
|
65
|
+
onChange: (event) => save({
|
|
66
|
+
...value,
|
|
67
|
+
bindingScope: event.currentTarget.value === 'session' ? 'session' : 'workspace',
|
|
68
|
+
}),
|
|
69
|
+
style: fieldStyle,
|
|
70
|
+
}, createElement('option', { value: 'workspace' }, t('terminal.workspaceBinding')), createElement('option', { value: 'session' }, t('terminal.sessionBinding'))), createElement('small', { style: helpStyle }, t('terminal.workspaceBindingHelp'))), createElement(Field, { label: t('terminal.theme') }, createElement('select', {
|
|
71
|
+
value: value.appearance.theme,
|
|
72
|
+
disabled,
|
|
73
|
+
onChange: (event) => updateAppearance({ theme: event.currentTarget.value === 'custom' ? 'custom' : 'inherit' }),
|
|
74
|
+
style: fieldStyle,
|
|
75
|
+
}, createElement('option', { value: 'inherit' }, t('terminal.inheritDshTheme')), createElement('option', { value: 'custom' }, t('terminal.custom')))), createElement(ColorField, { label: t('terminal.background'), value: value.appearance.background, disabled: customDisabled, onChange: (background) => updateAppearance({ background }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(ColorField, { label: t('terminal.foreground'), value: value.appearance.foreground, disabled: customDisabled, onChange: (foreground) => updateAppearance({ foreground }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(ColorField, { label: t('terminal.cursorColor'), value: value.appearance.cursorColor, disabled: customDisabled, onChange: (cursorColor) => updateAppearance({ cursorColor }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(Field, { label: t('terminal.font') }, createElement('div', { style: rowStyle }, createElement('input', {
|
|
76
|
+
type: 'text', value: fontFamily, maxLength: 128, disabled: customDisabled,
|
|
77
|
+
placeholder: t('terminal.inheritFont'),
|
|
78
|
+
onChange: (event) => setFontFamily(event.currentTarget.value),
|
|
79
|
+
onBlur: saveFontFamily,
|
|
80
|
+
style: { ...fieldStyle, flex: 1, minWidth: 0 },
|
|
81
|
+
}), createElement(ResetButton, { disabled: customDisabled || value.appearance.fontFamily === null, onClick: () => { setFontFamily(''); updateAppearance({ fontFamily: null }); }, label: t('terminal.resetInherit') }))), createElement(NumberField, { label: t('terminal.fontSize'), value: value.appearance.fontSize, min: 10, max: 32, step: 1, disabled: customDisabled, onChange: (fontSize) => updateAppearance({ fontSize }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(NumberField, { label: t('terminal.lineHeight'), value: value.appearance.lineHeight, min: 1, max: 2, step: 0.1, disabled: customDisabled, onChange: (lineHeight) => updateAppearance({ lineHeight }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(Field, { label: t('terminal.cursorShape') }, createElement('select', {
|
|
82
|
+
value: value.appearance.cursorStyle ?? '', disabled: customDisabled,
|
|
83
|
+
onChange: (event) => updateAppearance({ cursorStyle: parseCursorStyle(event.currentTarget.value) }),
|
|
84
|
+
style: fieldStyle,
|
|
85
|
+
}, createElement('option', { value: '' }, t('terminal.cursorInherit')), createElement('option', { value: 'block' }, t('terminal.cursorBlock')), createElement('option', { value: 'bar' }, t('terminal.cursorBar')), createElement('option', { value: 'underline' }, t('terminal.cursorUnderline')))), createElement(Field, { label: t('terminal.cursorBlink') }, createElement('div', { style: rowStyle }, createElement('label', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } }, createElement('input', {
|
|
86
|
+
type: 'checkbox', role: 'switch', 'aria-label': t('terminal.cursorBlink'),
|
|
87
|
+
checked: value.appearance.cursorBlink ?? false, disabled: customDisabled,
|
|
88
|
+
onChange: (event) => updateAppearance({ cursorBlink: event.currentTarget.checked }),
|
|
89
|
+
}), value.appearance.cursorBlink === null ? t('terminal.inherit') : (value.appearance.cursorBlink ? t('terminal.cursorOn') : t('terminal.cursorOff'))), createElement(ResetButton, { disabled: customDisabled || value.appearance.cursorBlink === null, onClick: () => updateAppearance({ cursorBlink: null }), label: t('terminal.resetInherit') }))), createElement(NumberField, { label: t('terminal.scrollback'), value: value.appearance.scrollback, min: 1000, max: 100000, step: 1000, disabled, onChange: (scrollback) => updateAppearance({ scrollback }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), message && createElement('div', { role: 'status', style: { color: message.includes('已保存') ? dshThemeColor.success : dshThemeColor.error } }, message));
|
|
90
|
+
}
|
|
91
|
+
function Field({ label, children }) {
|
|
92
|
+
return createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, label), children);
|
|
93
|
+
}
|
|
94
|
+
function ColorField({ label, value, disabled, onChange, inheritLabel, resetLabel }) {
|
|
95
|
+
return createElement(Field, { label }, createElement('div', { style: rowStyle }, createElement('input', { type: 'color', value: value ?? '#000000', disabled, 'aria-label': label, onChange: (event) => onChange(event.currentTarget.value), style: { width: 48, height: 34 } }), createElement('code', { style: { flex: 1 } }, value ?? inheritLabel), createElement(ResetButton, { disabled: disabled || value === null, onClick: () => onChange(null), label: resetLabel })));
|
|
96
|
+
}
|
|
97
|
+
function NumberField({ label, value, min, max, step, disabled, onChange, inheritLabel, resetLabel }) {
|
|
98
|
+
return createElement(Field, { label }, createElement('div', { style: rowStyle }, createElement('input', {
|
|
99
|
+
type: 'number', value: value ?? '', min, max, step, disabled, placeholder: inheritLabel,
|
|
100
|
+
onChange: (event) => onChange(event.currentTarget.value === '' ? null : Number(event.currentTarget.value)),
|
|
101
|
+
style: { ...fieldStyle, flex: 1, minWidth: 0 },
|
|
102
|
+
}), createElement(ResetButton, { disabled: disabled || value === null, onClick: () => onChange(null), label: resetLabel })));
|
|
103
|
+
}
|
|
104
|
+
function ResetButton({ disabled, onClick, label }) {
|
|
105
|
+
return createElement('button', { type: 'button', disabled, onClick, style: buttonStyle }, label);
|
|
106
|
+
}
|
|
107
|
+
function profileOptions(selected, status, t) {
|
|
108
|
+
if (status === null)
|
|
109
|
+
return [{ profileId: 'system', label: t(profileLabels.system), available: true }];
|
|
110
|
+
const profiles = status.profiles.map((profile) => ({
|
|
111
|
+
profileId: profile.profileId,
|
|
112
|
+
label: profile.name,
|
|
113
|
+
available: true,
|
|
114
|
+
}));
|
|
115
|
+
const options = [{ profileId: 'system', label: t(profileLabels.system), available: profiles.length > 0 }, ...profiles];
|
|
116
|
+
if (options.some((profile) => profile.profileId === selected))
|
|
117
|
+
return options;
|
|
118
|
+
return [...options, { profileId: selected, label: t('terminal.profileUnavailable', { profile: t(profileLabels[selected]) }), available: false }];
|
|
119
|
+
}
|
|
120
|
+
function platformLabel(platform, t) {
|
|
121
|
+
if (platform === 'darwin')
|
|
122
|
+
return 'macOS';
|
|
123
|
+
if (platform === 'win32')
|
|
124
|
+
return 'Windows';
|
|
125
|
+
if (platform === 'linux')
|
|
126
|
+
return 'Linux';
|
|
127
|
+
return t('terminal.unsupportedPlatform');
|
|
128
|
+
}
|
|
129
|
+
async function callTerminalStatus(rpc) {
|
|
130
|
+
let response;
|
|
131
|
+
try {
|
|
132
|
+
response = await rpc.call(CODINGNS_RPC_CHANNEL, 'terminal/status', {});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
136
|
+
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
137
|
+
throw error;
|
|
138
|
+
response = await rpc.call('/api', 'codingns/terminal/status', {});
|
|
139
|
+
}
|
|
140
|
+
if (!response.ok)
|
|
141
|
+
throw new Error(response.error.message);
|
|
142
|
+
return response.value;
|
|
143
|
+
}
|
|
144
|
+
function normalizeFontFamily(value) {
|
|
145
|
+
const trimmed = value.trim();
|
|
146
|
+
if (trimmed === '')
|
|
147
|
+
return null;
|
|
148
|
+
if (trimmed.length > 128 || /[\u0000-\u001F\u007F]/u.test(trimmed))
|
|
149
|
+
throw new TypeError('字体名称格式无效');
|
|
150
|
+
return trimmed;
|
|
151
|
+
}
|
|
152
|
+
function parseCursorStyle(value) {
|
|
153
|
+
return value === 'block' || value === 'bar' || value === 'underline' ? value : null;
|
|
154
|
+
}
|
|
155
|
+
const fieldStyle = dshSettingsFieldStyle;
|
|
156
|
+
const buttonStyle = { ...dshSettingsButtonStyle, flex: '0 0 auto', minWidth: 92 };
|
|
157
|
+
const rowStyle = dshSettingsRowStyle;
|
|
158
|
+
const helpStyle = dshSettingsHelpStyle;
|
|
159
|
+
const noteStyle = dshSettingsNoteStyle;
|
|
160
|
+
//# sourceMappingURL=terminal-enhancement-panel.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TerminalEnhancementPanel } from './terminal-enhancement-panel.js';
|
|
2
|
+
/** “终端强化”只切换 backend;插件 controller 与 Sidebar UI 始终存在。 */
|
|
3
|
+
export const terminalEnhancementFeature = {
|
|
4
|
+
descriptor: {
|
|
5
|
+
name: 'terminalEnhancement',
|
|
6
|
+
version: '0.1.0',
|
|
7
|
+
enabledByDefault: false,
|
|
8
|
+
dependencies: [],
|
|
9
|
+
runtime: 'client',
|
|
10
|
+
activation: 'restart',
|
|
11
|
+
ui: {
|
|
12
|
+
label: '终端强化',
|
|
13
|
+
description: '配置持久终端的默认 shell 与插件终端外观。启用和禁用均需重启 DSH。',
|
|
14
|
+
labelKey: 'feature.terminal.label',
|
|
15
|
+
descriptionKey: 'feature.terminal.description',
|
|
16
|
+
order: 40,
|
|
17
|
+
defaultOpen: true,
|
|
18
|
+
legacyFallback: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
start: () => undefined,
|
|
22
|
+
settingsPanel: TerminalEnhancementPanel,
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=terminal-enhancement.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
4
|
+
import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
5
|
+
import type { FeatureModule } from '../../shared/contracts/feature.js';
|
|
6
|
+
import type { CodingNsSettings } from '../../shared/contracts/config.js';
|
|
7
|
+
import type { CodingNsLocale } from '../locale.js';
|
|
8
|
+
/** 一次 Codingns4DSH RPC 的结果,与 DSH Connection 的结果形状一致。 */
|
|
9
|
+
export type CodingNsRpcResult = {
|
|
10
|
+
readonly ok: true;
|
|
11
|
+
readonly value: unknown;
|
|
12
|
+
} | {
|
|
13
|
+
readonly ok: false;
|
|
14
|
+
readonly error: {
|
|
15
|
+
readonly code: string;
|
|
16
|
+
readonly message: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/** Client 侧 RPC 调用句柄,由 DSH Connection 提供。 */
|
|
20
|
+
export interface CodingNsRpcClient {
|
|
21
|
+
call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<CodingNsRpcResult>;
|
|
22
|
+
}
|
|
23
|
+
/** Client 侧功能模块在 start 中取用的服务集合。 */
|
|
24
|
+
export interface CodingNsClientServices {
|
|
25
|
+
/** 当前 DSH 实际运行版本,由 Host 注入。 */
|
|
26
|
+
readonly dshVersion: string;
|
|
27
|
+
readonly settings: SettingsScope<CodingNsSettings>;
|
|
28
|
+
readonly rpc: CodingNsRpcClient;
|
|
29
|
+
/** DSH Typert Remote;归档会话模块只通过运行时探测调用可选方法。 */
|
|
30
|
+
readonly remote?: unknown;
|
|
31
|
+
/** DSH 语言运行时;所有 Client 文案都从 Codingns4DSH 命名空间读取。 */
|
|
32
|
+
readonly locale: CodingNsLocale;
|
|
33
|
+
/** DSH 对话装配服务;用于注册不写入 Session 的流式临时节点。 */
|
|
34
|
+
readonly uiConversation?: unknown;
|
|
35
|
+
/** 对话工具栏 Slot 服务;测试和非 Web 宿主可以不提供。 */
|
|
36
|
+
readonly slots?: SlotRegistry;
|
|
37
|
+
/** 当前 Client Cordis 上下文;只供需要注册 DSH UI Slot 的功能模块使用。 */
|
|
38
|
+
readonly uiContext?: Context;
|
|
39
|
+
}
|
|
40
|
+
/** 设置卡片传给模块面板的属性。 */
|
|
41
|
+
export interface FeaturePanelProps {
|
|
42
|
+
/** 宿主注入的服务,与模块 start 中拿到的是同一份。 */
|
|
43
|
+
readonly services: CodingNsClientServices;
|
|
44
|
+
/** 当前是否启用;未启用时面板需要自行灰显并禁用输入。 */
|
|
45
|
+
readonly enabled: boolean;
|
|
46
|
+
/** 设置快照,用于读取表单初值和可写状态。 */
|
|
47
|
+
readonly snapshot: SettingsScopeSnapshot<CodingNsSettings>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 可在浏览器侧启停、并可出现在设置页的功能模块。
|
|
51
|
+
*
|
|
52
|
+
* 模块自带设置面板,设置页只负责遍历注册表渲染,因此新增模块不需要修改设置页。
|
|
53
|
+
*/
|
|
54
|
+
export interface CodingNsClientFeatureModule extends FeatureModule<CodingNsClientServices> {
|
|
55
|
+
/** 卡片内容;不提供时该模块只显示标题栏开关。 */
|
|
56
|
+
readonly settingsPanel?: (props: FeaturePanelProps) => ReactElement | null;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { FeaturePanelProps } from './types.js';
|
|
3
|
+
/** 工作区会话增强的单列设置面板。 */
|
|
4
|
+
export declare function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
|
|
5
|
+
//# sourceMappingURL=workspace-session-enhancement-panel.d.ts.map
|