@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,451 @@
|
|
|
1
|
+
import { createElement, useEffect, useState } from 'react';
|
|
2
|
+
import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
|
|
3
|
+
import { dshButtonStyle, dshFieldStyle, dshFormRootStyle, dshThemeColor } from '../theme.js';
|
|
4
|
+
export const DEBUG_KIND = 'debug';
|
|
5
|
+
export const DEBUG_PROVIDER_ID = 'codingns4dsh/debug';
|
|
6
|
+
const PORT_CHECK_INTERVAL_MS = 5_000;
|
|
7
|
+
/** 注册最小 Debug 页面;页面只负责展示和发送用户意图。 */
|
|
8
|
+
export function registerDebugUi(ctx, rpc, remote) {
|
|
9
|
+
const disposers = [];
|
|
10
|
+
disposers.push(ctx.sidebarRightTabs.register({
|
|
11
|
+
id: DEBUG_PROVIDER_ID,
|
|
12
|
+
kind: DEBUG_KIND,
|
|
13
|
+
multiple: false,
|
|
14
|
+
priority: 'extension',
|
|
15
|
+
title: () => '调试',
|
|
16
|
+
guide: [{ id: 'debug', order: 30, title: () => '调试', description: () => '启动工作区命令、检查端口并访问服务', icon: DebugIcon }],
|
|
17
|
+
}));
|
|
18
|
+
disposers.push(ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
19
|
+
name: 'sidebar.right.pane.tab', key: DEBUG_PROVIDER_ID,
|
|
20
|
+
inject: () => ({ rpc, remote, sidebarRight: ctx.sidebarRight }),
|
|
21
|
+
}, DebugBody)));
|
|
22
|
+
return () => { for (const dispose of disposers.reverse())
|
|
23
|
+
dispose(); };
|
|
24
|
+
}
|
|
25
|
+
function DebugBody({ sessionId, rpc, remote, sidebarRight }) {
|
|
26
|
+
const [workspaceId, setWorkspaceId] = useState(null);
|
|
27
|
+
const [config, setConfig] = useState(null);
|
|
28
|
+
const [instances, setInstances] = useState([]);
|
|
29
|
+
const [bindings, setBindings] = useState([]);
|
|
30
|
+
const [portChecks, setPortChecks] = useState({});
|
|
31
|
+
const [terminalStatus, setTerminalStatus] = useState(null);
|
|
32
|
+
const [draft, setDraft] = useState(null);
|
|
33
|
+
const [message, setMessage] = useState('正在读取工作区…');
|
|
34
|
+
const [busy, setBusy] = useState(false);
|
|
35
|
+
const load = async (currentWorkspaceId) => {
|
|
36
|
+
const scope = { sessionId: String(sessionId), workspaceId: currentWorkspaceId, generation: 0 };
|
|
37
|
+
const next = await call(rpc, 'debug/config/get', scope);
|
|
38
|
+
const running = await call(rpc, 'debug/runtime/list', scope);
|
|
39
|
+
setConfig(next);
|
|
40
|
+
setInstances(running);
|
|
41
|
+
setMessage('');
|
|
42
|
+
};
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
let disposed = false;
|
|
45
|
+
setPortChecks({});
|
|
46
|
+
const terminal = remote?.terminal;
|
|
47
|
+
void call(rpc, 'terminal/status', {}).then((status) => {
|
|
48
|
+
if (!disposed)
|
|
49
|
+
setTerminalStatus(status);
|
|
50
|
+
}).catch(() => { });
|
|
51
|
+
void (async () => {
|
|
52
|
+
try {
|
|
53
|
+
const environment = await terminal?.environment?.(String(sessionId));
|
|
54
|
+
const id = readWorkspaceId(environment);
|
|
55
|
+
if (id === null)
|
|
56
|
+
throw new Error('当前 Session 没有关联 Workspace');
|
|
57
|
+
if (disposed)
|
|
58
|
+
return;
|
|
59
|
+
setWorkspaceId(id);
|
|
60
|
+
await load(id);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (!disposed)
|
|
64
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
return () => { disposed = true; };
|
|
68
|
+
}, [sessionId]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (workspaceId === null || config === null)
|
|
71
|
+
return;
|
|
72
|
+
if (!config.profiles.some((profile) => profile.port !== null))
|
|
73
|
+
return;
|
|
74
|
+
let disposed = false;
|
|
75
|
+
let checking = false;
|
|
76
|
+
const checkPorts = async () => {
|
|
77
|
+
if (checking)
|
|
78
|
+
return;
|
|
79
|
+
checking = true;
|
|
80
|
+
try {
|
|
81
|
+
const portProfiles = config.profiles.filter((profile) => profile.port !== null);
|
|
82
|
+
const results = await Promise.all(portProfiles.map(async (profile) => {
|
|
83
|
+
try {
|
|
84
|
+
return { profileId: profile.id, result: await requestPortCheck(profile) };
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
if (disposed)
|
|
91
|
+
return;
|
|
92
|
+
setPortChecks((current) => {
|
|
93
|
+
const next = {};
|
|
94
|
+
for (const profile of portProfiles) {
|
|
95
|
+
const result = results.find((item) => item?.profileId === profile.id)?.result;
|
|
96
|
+
if (result !== undefined)
|
|
97
|
+
next[profile.id] = result;
|
|
98
|
+
else {
|
|
99
|
+
const previous = current[profile.id];
|
|
100
|
+
if (previous !== undefined)
|
|
101
|
+
next[profile.id] = previous;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return next;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
checking = false;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
void checkPorts();
|
|
112
|
+
const timer = setInterval(() => { void checkPorts(); }, PORT_CHECK_INTERVAL_MS);
|
|
113
|
+
return () => { disposed = true; clearInterval(timer); };
|
|
114
|
+
}, [workspaceId, config, sessionId]);
|
|
115
|
+
if (workspaceId === null)
|
|
116
|
+
return createElement('section', { style: panelStyle }, createElement('div', { style: loadingStyle }, createElement('span', { style: loadingDotStyle }), message));
|
|
117
|
+
const profiles = config?.profiles ?? [];
|
|
118
|
+
return createElement('section', { style: panelStyle }, createElement('header', { style: headerStyle }, createElement('div', { style: titleBlockStyle }, createElement('div', { style: eyebrowStyle }, '工作区工具'), createElement('h2', { style: titleStyle }, '工作区调试'), createElement('div', { style: workspaceStyle, title: workspaceId }, createElement('span', { style: workspaceDotStyle }), workspaceId)), createElement('div', { style: headerActionsStyle }, createElement('span', { style: countStyle }, `${profiles.length} 个配置`), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy || draft !== null, onClick: () => setDraft(createProfileDraft()), style: primaryButtonStyle }, createElement('span', { 'aria-hidden': true }, '+'), ' 添加配置'))), message && createElement('div', { role: 'status', 'aria-live': 'polite', style: statusStyle(statusTone(message)) }, createElement('span', { style: statusIconStyle }, statusTone(message) === 'success' ? '✓' : statusTone(message) === 'error' ? '!' : 'i'), message), draft === null ? null : createProfileForm(draft), profiles.length === 0 && draft === null ? createElement('div', { style: emptyStyle }, createElement('div', { style: emptyIconStyle }, createElement(DebugIcon, { size: 22 })), createElement('strong', { style: emptyTitleStyle }, '还没有启动配置'), createElement('p', { style: emptyTextStyle }, '为当前 Workspace 添加一个命令,即可从这里启动终端、检查端口和访问服务。'), createElement('button', { type: 'button', 'aria-label': '添加启动配置', disabled: busy, onClick: () => setDraft(createProfileDraft()), style: secondaryButtonStyle }, '+ 添加第一个配置')) : null, profiles.map((profile) => {
|
|
119
|
+
const running = runningInstances(profile, instances);
|
|
120
|
+
const portCheck = portChecks[profile.id];
|
|
121
|
+
return createElement('article', { key: profile.id, style: itemStyle }, createElement('div', { style: itemHeaderStyle }, createElement('div', { style: itemTitleBlockStyle }, createElement('strong', { style: itemTitleStyle }, profile.name), createElement('span', { style: itemCommandStyle }, formatCommand(profile))), createElement('span', { style: running > 0 ? runningBadgeStyle : stoppedBadgeStyle }, running > 0 ? `${running} 个运行中` : '未运行')), createElement('div', { style: metaStyle }, createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '目录'), profile.cwdRelative || '.'), profile.port === null ? null : createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '端口'), String(profile.port)), createElement('span', { style: metaItemStyle }, createElement('span', { style: metaKeyStyle }, '运行方式'), profile.runtimeType), profile.proxy.enabled ? createElement('span', { style: proxyBadgeStyle }, '代理已启用') : null), profile.port === null ? null : createElement('div', { style: portStatusStyle }, portCheck === undefined
|
|
122
|
+
? createElement('span', { style: portUnknownStyle }, '端口状态尚未检查')
|
|
123
|
+
: portCheck.listening
|
|
124
|
+
? createElement('span', { style: portListeningStyle }, `端口 ${profile.port} 正在监听${portCheck.process?.pid === undefined ? '' : ` · PID ${portCheck.process.pid}`}`)
|
|
125
|
+
: createElement('span', { style: portStoppedStyle }, `端口 ${profile.port} 未监听`)), createElement('div', { style: actionsStyle }, createElement('button', { type: 'button', disabled: busy, onClick: () => void launch(profile), style: primaryButtonStyle }, '启动'), profile.port === null ? null : createElement('button', { type: 'button', disabled: busy, onClick: () => void inspect(profile), style: secondaryButtonStyle }, '检查端口'), portCheck?.listening === true && portCheck.process !== null ? createElement('button', { type: 'button', title: '只结束端口监听进程,保留终端窗口', disabled: busy, onClick: () => void terminatePort(profile, portCheck), style: dangerButtonStyle }, '结束进程') : null, instances.filter((instance) => instance.profileId === profile.id && instance.state === 'running').map((instance) => createElement('button', { key: instance.id, type: 'button', title: '停止并关闭终端窗口', disabled: busy, onClick: () => void stop(instance), style: dangerButtonStyle }, '停止')), createElement('span', { style: actionDividerStyle }), createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(createProfileDraft(profile)), style: quietButtonStyle }, '编辑'), createElement('button', { type: 'button', disabled: busy, onClick: () => void deleteProfile(profile), style: dangerQuietButtonStyle }, '删除'), instances.filter((instance) => instance.profileId === profile.id && instance.state === 'running' && profile.proxy.enabled).map((instance) => {
|
|
126
|
+
const binding = bindings.find((item) => item.instanceId === instance.id);
|
|
127
|
+
return binding === undefined
|
|
128
|
+
? createElement('button', { key: `proxy-${instance.id}`, type: 'button', disabled: busy, onClick: () => void enableProxy(profile, instance), style: secondaryButtonStyle }, '开启代理')
|
|
129
|
+
: createElement('span', { key: `proxy-${instance.id}`, style: proxyActionsStyle }, createElement('a', { href: binding.url, target: '_blank', rel: 'noreferrer', style: linkStyle }, '打开代理'), createElement('button', { type: 'button', disabled: busy, onClick: () => void disableProxy(binding), style: quietButtonStyle }, '关闭'));
|
|
130
|
+
})));
|
|
131
|
+
}));
|
|
132
|
+
async function launch(profile) {
|
|
133
|
+
await withBusy(async () => {
|
|
134
|
+
const result = await call(rpc, 'debug/profile/launch', { sessionId: String(sessionId), workspaceId, generation: 0, profileId: profile.id, cols: 120, rows: 32 });
|
|
135
|
+
setInstances((current) => [...current.filter((item) => item.id !== result.instance.id), result.instance]);
|
|
136
|
+
sidebarRight.openTabIn(String(sessionId), 'terminal', { params: { terminalId: result.terminal.id } });
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function createProfileForm(value) {
|
|
140
|
+
const field = (label, key, type = 'text', placeholder, layout) => createElement('label', { style: { ...fieldStyle, ...layout } }, createElement('span', { style: fieldLabelStyle }, label), createElement('input', {
|
|
141
|
+
type,
|
|
142
|
+
value: typeof value[key] === 'boolean' ? undefined : value[key],
|
|
143
|
+
placeholder,
|
|
144
|
+
required: key === 'name' || key === 'commandLine',
|
|
145
|
+
min: key === 'port' ? 1 : undefined,
|
|
146
|
+
max: key === 'port' ? 65535 : undefined,
|
|
147
|
+
step: key === 'port' ? 1 : undefined,
|
|
148
|
+
autoComplete: 'off',
|
|
149
|
+
onChange: (event) => setDraft({ ...value, [key]: event.currentTarget.value }),
|
|
150
|
+
style: inputStyle,
|
|
151
|
+
}));
|
|
152
|
+
const selectField = (label, key, options) => createElement('label', { style: selectFieldStyle }, createElement('span', { style: fieldLabelStyle }, label), createElement('select', { value: value[key], onChange: (event) => setDraft({ ...value, [key]: event.currentTarget.value }), style: inputStyle }, ...options.map((option) => createElement('option', { key: option.value, value: option.value }, option.label))));
|
|
153
|
+
return createElement('div', { role: 'dialog', 'aria-modal': true, 'aria-labelledby': 'debug-shortcut-title', style: formOverlayStyle }, createElement('form', { style: formStyle, onSubmit: (event) => { event.preventDefault(); void saveProfile(value); } }, createElement('div', { style: formHeaderStyle }, createElement('div', undefined, createElement('h3', { id: 'debug-shortcut-title', style: formTitleStyle }, value.id === null ? '添加快捷启动项' : '编辑快捷启动项'), createElement('p', { style: formHintStyle }, '保存一组可重复使用的启动参数,之后可以一键打开终端。')), createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(null), style: closeButtonStyle, 'aria-label': '关闭快捷启动项' }, '×')), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '启动入口'), createElement('span', { style: formSectionHintStyle }, '直接输入完整命令,例如 pnpm run dev --host 0.0.0.0。')), createElement('div', { style: formGridStyle }, field('名称', 'name', 'text', '例如:前端开发'), field('启动目录(Workspace 内相对路径)', 'cwdRelative', 'text', '留空使用根目录'), field('完整启动命令', 'commandLine', 'text', '例如:pnpm run dev --host 0.0.0.0', { gridColumn: '1 / -1' }))), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '执行环境(可选)'), createElement('span', { style: formSectionHintStyle }, '只显示 Host 实际检测到的 Shell。')), createElement('div', { style: formGridStyle }, selectField('终端 Shell', 'shellProfileId', shellOptions(terminalStatus))), createElement('p', { style: runtimeHintStyle }, `运行方式由 Host 平台自动选择:${terminalRuntimeLabel(terminalStatus?.platform)}。`)), createElement('section', { style: formSectionStyle }, createElement('div', { style: formSectionHeaderStyle }, createElement('strong', { style: formSectionTitleStyle }, '服务检查'), createElement('span', { style: formSectionHintStyle }, '端口每 5 秒自动检查,也可手动刷新。')), createElement('div', { style: formGridStyle }, field('监听端口(可选)', 'port', 'number', '1 - 65535'), createElement('label', { style: proxyFieldStyle }, createElement('span', { style: fieldLabelStyle }, '反向代理'), createElement('span', { style: proxyToggleStyle }, createElement('span', undefined, '允许访问已确认的服务'), createElement('input', { type: 'checkbox', 'aria-label': '启用服务代理', checked: value.proxyEnabled, onChange: (event) => setDraft({ ...value, proxyEnabled: event.currentTarget.checked }), style: { accentColor: dshThemeColor.accent } }))))), createElement('div', { style: formActionsStyle }, createElement('button', { type: 'button', disabled: busy, onClick: () => setDraft(null), style: secondaryButtonStyle }, '关闭'), createElement('button', { type: 'submit', 'aria-label': value.id === null ? '保存配置并创建快捷启动项' : '保存快捷启动项修改', disabled: busy || value.commandLine.trim() === '', style: primaryButtonStyle }, busy ? '保存中…' : value.id === null ? '保存为快捷启动' : '保存修改'))));
|
|
154
|
+
}
|
|
155
|
+
async function saveProfile(value) {
|
|
156
|
+
await withBusy(async () => {
|
|
157
|
+
const name = value.name.trim();
|
|
158
|
+
const commandParts = splitCommandLine(value.commandLine);
|
|
159
|
+
const portText = value.port.trim();
|
|
160
|
+
if (name === '' || commandParts.length === 0)
|
|
161
|
+
throw new Error('名称和完整启动命令不能为空');
|
|
162
|
+
const port = portText === '' ? null : Number(portText);
|
|
163
|
+
if (port !== null && (!Number.isSafeInteger(port) || port < 1 || port > 65535))
|
|
164
|
+
throw new Error('监听端口必须是 1 到 65535 的整数');
|
|
165
|
+
const shell = resolveShell(value.shellProfileId, terminalStatus);
|
|
166
|
+
const profile = {
|
|
167
|
+
id: value.id ?? createProfileId(),
|
|
168
|
+
name,
|
|
169
|
+
cwdRelative: value.cwdRelative.trim() || '.',
|
|
170
|
+
command: commandParts[0],
|
|
171
|
+
args: commandParts.slice(1),
|
|
172
|
+
env: {},
|
|
173
|
+
shell,
|
|
174
|
+
runtimeType: runtimeTypeFor(terminalStatus?.platform, shell.profileId),
|
|
175
|
+
port,
|
|
176
|
+
proxy: { enabled: value.proxyEnabled },
|
|
177
|
+
};
|
|
178
|
+
const endpoint = value.id === null ? 'debug/config/save' : 'debug/config/update';
|
|
179
|
+
const payload = value.id === null
|
|
180
|
+
? { sessionId: String(sessionId), workspaceId, generation: 0, config: { version: 1, profiles: [...profiles, profile] } }
|
|
181
|
+
: { sessionId: String(sessionId), workspaceId, generation: 0, profileId: value.id, profile };
|
|
182
|
+
const next = await call(rpc, endpoint, payload);
|
|
183
|
+
setConfig(next);
|
|
184
|
+
setDraft(null);
|
|
185
|
+
setMessage(value.id === null ? '启动配置已保存' : '启动配置已更新');
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
async function deleteProfile(profile) {
|
|
189
|
+
if (!window.confirm(`确认删除“${profile.name}”启动配置?`))
|
|
190
|
+
return;
|
|
191
|
+
await withBusy(async () => {
|
|
192
|
+
const next = await call(rpc, 'debug/config/delete', { sessionId: String(sessionId), workspaceId, generation: 0, profileId: profile.id });
|
|
193
|
+
setConfig(next);
|
|
194
|
+
setPortChecks((current) => { const { [profile.id]: _removed, ...rest } = current; return rest; });
|
|
195
|
+
setBindings((current) => current.filter((item) => item.profileId !== profile.id));
|
|
196
|
+
setMessage('启动配置已删除');
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function inspect(profile) {
|
|
200
|
+
await withBusy(async () => {
|
|
201
|
+
const result = await requestPortCheck(profile);
|
|
202
|
+
setPortChecks((current) => ({ ...current, [profile.id]: result }));
|
|
203
|
+
setMessage(result.listening ? `端口 ${profile.port ?? ''} 正在监听` : `端口 ${profile.port ?? ''} 未监听`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function requestPortCheck(profile) {
|
|
207
|
+
return call(rpc, 'debug/port/check', { sessionId: String(sessionId), workspaceId, generation: 0, profileId: profile.id });
|
|
208
|
+
}
|
|
209
|
+
async function terminatePort(profile, check) {
|
|
210
|
+
if (!check.listening || check.process === null || !window.confirm(`确认结束端口 ${profile.port ?? ''} 的监听进程?`))
|
|
211
|
+
return;
|
|
212
|
+
await withBusy(async () => {
|
|
213
|
+
await call(rpc, 'debug/port/kill', { sessionId: String(sessionId), workspaceId, generation: 0, checkId: check.id });
|
|
214
|
+
setPortChecks((current) => ({ ...current, [profile.id]: { ...check, listening: false, process: null } }));
|
|
215
|
+
setMessage('监听进程已结束');
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function stop(instance) {
|
|
219
|
+
await withBusy(async () => {
|
|
220
|
+
await call(rpc, 'debug/runtime/stop', { sessionId: String(sessionId), workspaceId, generation: 0, instanceId: instance.id });
|
|
221
|
+
setInstances((current) => current.map((item) => item.id === instance.id ? { ...item, state: 'exited' } : item));
|
|
222
|
+
setBindings((current) => current.filter((item) => item.instanceId !== instance.id));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
async function enableProxy(profile, instance) {
|
|
226
|
+
await withBusy(async () => {
|
|
227
|
+
const binding = await call(rpc, 'debug/proxy/enable', { sessionId: String(sessionId), workspaceId, generation: 0, profileId: profile.id, instanceId: instance.id });
|
|
228
|
+
setBindings((current) => [...current.filter((item) => item.id !== binding.id && item.instanceId !== binding.instanceId), binding]);
|
|
229
|
+
setMessage(`代理已开启:${binding.url}`);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async function disableProxy(binding) {
|
|
233
|
+
await withBusy(async () => {
|
|
234
|
+
await call(rpc, 'debug/proxy/disable', { sessionId: String(sessionId), workspaceId, generation: 0, bindingId: binding.id });
|
|
235
|
+
setBindings((current) => current.filter((item) => item.id !== binding.id));
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async function withBusy(action) {
|
|
239
|
+
setBusy(true);
|
|
240
|
+
try {
|
|
241
|
+
await action();
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
setMessage(error instanceof Error ? error.message : String(error));
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
setBusy(false);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async function call(rpc, endpoint, payload) {
|
|
252
|
+
let result;
|
|
253
|
+
try {
|
|
254
|
+
result = await rpc.call(CODINGNS_RPC_CHANNEL, endpoint, payload);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
258
|
+
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
259
|
+
throw error;
|
|
260
|
+
result = await rpc.call('/api', `codingns/${endpoint}`, payload);
|
|
261
|
+
}
|
|
262
|
+
if (!result.ok)
|
|
263
|
+
throw new Error(result.error.message);
|
|
264
|
+
return result.value;
|
|
265
|
+
}
|
|
266
|
+
function readWorkspaceId(value) {
|
|
267
|
+
if (typeof value !== 'object' || value === null)
|
|
268
|
+
return null;
|
|
269
|
+
const candidate = value.value ?? value;
|
|
270
|
+
if (typeof candidate !== 'object' || candidate === null)
|
|
271
|
+
return null;
|
|
272
|
+
const id = candidate.workspaceId;
|
|
273
|
+
return typeof id === 'string' && id.trim() !== '' ? id : null;
|
|
274
|
+
}
|
|
275
|
+
function DebugIcon({ size = 22, className }) {
|
|
276
|
+
return createElement('svg', { width: size, height: size, className, viewBox: '0 0 24 24', fill: 'none', 'aria-hidden': true }, createElement('path', { d: 'M5 5h14v14H5zM8 9h8M8 12h5M8 15h8', stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round' }));
|
|
277
|
+
}
|
|
278
|
+
const panelStyle = { ...dshFormRootStyle, boxSizing: 'border-box', display: 'flex', flexDirection: 'column', gap: 16, padding: '18px 20px 28px', minHeight: '100%', overflow: 'auto', background: dshThemeColor.pageBackground };
|
|
279
|
+
const headerStyle = { display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-start', paddingBottom: 16, borderBottom: `1px solid ${dshThemeColor.border}` };
|
|
280
|
+
const titleBlockStyle = { minWidth: 0, display: 'flex', flexDirection: 'column', gap: 3 };
|
|
281
|
+
const eyebrowStyle = { color: dshThemeColor.labelTertiary, fontSize: 10, fontWeight: 600 };
|
|
282
|
+
const titleStyle = { margin: 0, color: dshThemeColor.labelPrimary, fontSize: 22, lineHeight: 1.25, fontWeight: 650 };
|
|
283
|
+
const workspaceStyle = { display: 'flex', alignItems: 'center', minWidth: 0, maxWidth: 260, overflow: 'hidden', color: dshThemeColor.labelTertiary, fontSize: 11, textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
284
|
+
const workspaceDotStyle = { width: 6, height: 6, flex: '0 0 auto', marginRight: 6, borderRadius: '50%', background: dshThemeColor.accent };
|
|
285
|
+
const headerActionsStyle = { display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end', gap: 8 };
|
|
286
|
+
const countStyle = { color: dshThemeColor.labelTertiary, fontSize: 11, whiteSpace: 'nowrap' };
|
|
287
|
+
const itemStyle = { border: `1px solid ${dshThemeColor.border}`, borderRadius: 8, padding: 16, display: 'flex', flexDirection: 'column', gap: 12, background: dshThemeColor.menuBackground, boxShadow: dshThemeColor.subtleShadow };
|
|
288
|
+
const itemHeaderStyle = { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 10 };
|
|
289
|
+
const itemTitleBlockStyle = { minWidth: 0, display: 'flex', flexDirection: 'column', gap: 3 };
|
|
290
|
+
const itemTitleStyle = { color: dshThemeColor.labelPrimary, fontSize: 15, fontWeight: 600 };
|
|
291
|
+
const itemCommandStyle = { overflow: 'hidden', color: dshThemeColor.labelSecondary, fontFamily: 'var(--dsw-font-family-mono, ui-monospace, monospace)', fontSize: 12, textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
292
|
+
const runningBadgeStyle = { flex: '0 0 auto', padding: '3px 7px', borderRadius: 10, color: dshThemeColor.success, background: 'color-mix(in srgb, currentColor 10%, transparent)', fontSize: 11, whiteSpace: 'nowrap' };
|
|
293
|
+
const stoppedBadgeStyle = { flex: '0 0 auto', padding: '3px 7px', borderRadius: 10, color: dshThemeColor.labelTertiary, background: `${dshThemeColor.border}`, fontSize: 11, whiteSpace: 'nowrap' };
|
|
294
|
+
const metaStyle = { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: '5px 10px', color: dshThemeColor.labelSecondary, fontSize: 11 };
|
|
295
|
+
const metaItemStyle = { display: 'inline-flex', alignItems: 'center', gap: 4, minWidth: 0, maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
|
|
296
|
+
const metaKeyStyle = { color: dshThemeColor.labelCaption, fontSize: 10 };
|
|
297
|
+
const proxyBadgeStyle = { padding: '2px 6px', borderRadius: 4, color: dshThemeColor.accent, background: 'color-mix(in srgb, currentColor 10%, transparent)', fontSize: 10 };
|
|
298
|
+
const portStatusStyle = { display: 'flex', alignItems: 'center', minHeight: 24, padding: '4px 8px', borderRadius: 5, background: dshThemeColor.menuBackground, fontSize: 11 };
|
|
299
|
+
const portUnknownStyle = { color: dshThemeColor.labelTertiary };
|
|
300
|
+
const portListeningStyle = { color: dshThemeColor.success };
|
|
301
|
+
const portStoppedStyle = { color: dshThemeColor.labelTertiary };
|
|
302
|
+
const actionsStyle = { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 7, paddingTop: 4 };
|
|
303
|
+
const actionDividerStyle = { width: 1, height: 18, margin: '0 2px', background: dshThemeColor.border };
|
|
304
|
+
const proxyActionsStyle = { display: 'inline-flex', alignItems: 'center', gap: 6, marginLeft: 'auto' };
|
|
305
|
+
const linkStyle = { color: dshThemeColor.accent, fontSize: 12, textDecoration: 'none' };
|
|
306
|
+
const buttonBaseStyle = { ...dshButtonStyle, minHeight: 30, padding: '5px 10px', borderRadius: 6, cursor: 'pointer', fontSize: 12, lineHeight: '18px' };
|
|
307
|
+
const primaryButtonStyle = { ...buttonBaseStyle, color: '#fff', borderColor: dshThemeColor.accent, background: dshThemeColor.accent, fontWeight: 600 };
|
|
308
|
+
const secondaryButtonStyle = { ...buttonBaseStyle, color: dshThemeColor.labelSecondary };
|
|
309
|
+
const quietButtonStyle = { ...buttonBaseStyle, minHeight: 26, padding: '3px 7px', color: dshThemeColor.labelTertiary, border: 0, background: 'transparent' };
|
|
310
|
+
const dangerButtonStyle = { ...buttonBaseStyle, color: dshThemeColor.error, borderColor: dshThemeColor.error, background: 'transparent' };
|
|
311
|
+
const dangerQuietButtonStyle = { ...quietButtonStyle, color: dshThemeColor.error };
|
|
312
|
+
const statusIconStyle = { display: 'inline-flex', width: 16, height: 16, alignItems: 'center', justifyContent: 'center', flex: '0 0 auto', borderRadius: '50%', fontSize: 10, fontWeight: 700 };
|
|
313
|
+
const emptyStyle = { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, padding: '28px 20px', border: `1px dashed ${dshThemeColor.border}`, borderRadius: 8, textAlign: 'center' };
|
|
314
|
+
const emptyIconStyle = { display: 'flex', width: 42, height: 42, alignItems: 'center', justifyContent: 'center', marginBottom: 2, borderRadius: 12, color: dshThemeColor.accent, background: 'color-mix(in srgb, currentColor 10%, transparent)' };
|
|
315
|
+
const emptyTitleStyle = { color: dshThemeColor.labelPrimary, fontSize: 14 };
|
|
316
|
+
const emptyTextStyle = { maxWidth: 300, margin: 0, color: dshThemeColor.labelTertiary, fontSize: 12, lineHeight: 1.55 };
|
|
317
|
+
const loadingStyle = { display: 'flex', alignItems: 'center', gap: 8, padding: 12, color: dshThemeColor.labelTertiary, fontSize: 13 };
|
|
318
|
+
const loadingDotStyle = { width: 7, height: 7, borderRadius: '50%', background: dshThemeColor.accent };
|
|
319
|
+
const formOverlayStyle = { position: 'fixed', inset: 0, zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 18, overflow: 'auto', background: dshThemeColor.overlay };
|
|
320
|
+
const formStyle = { display: 'flex', flexDirection: 'column', gap: 16, width: 'min(100%, 720px)', maxHeight: 'calc(100vh - 36px)', boxSizing: 'border-box', overflow: 'auto', padding: 20, border: `1px solid ${dshThemeColor.border}`, borderRadius: 10, background: dshThemeColor.menuBackground, boxShadow: dshThemeColor.prominentShadow };
|
|
321
|
+
const formHeaderStyle = { gridColumn: '1 / -1', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, paddingBottom: 10, borderBottom: `1px solid ${dshThemeColor.border}` };
|
|
322
|
+
const formSectionStyle = { display: 'flex', flexDirection: 'column', gap: 9, paddingTop: 2 };
|
|
323
|
+
const formSectionHeaderStyle = { display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10 };
|
|
324
|
+
const formSectionTitleStyle = { color: dshThemeColor.labelPrimary, fontSize: 13, fontWeight: 650 };
|
|
325
|
+
const formSectionHintStyle = { color: dshThemeColor.labelTertiary, fontSize: 11, textAlign: 'right' };
|
|
326
|
+
const formTitleStyle = { margin: 0, color: dshThemeColor.labelPrimary, fontSize: 20, lineHeight: 1.3, fontWeight: 650 };
|
|
327
|
+
const formHintStyle = { margin: '4px 0 0', color: dshThemeColor.labelTertiary, fontSize: 11 };
|
|
328
|
+
const closeButtonStyle = { ...quietButtonStyle, minHeight: 24, padding: '0 5px', fontSize: 20, lineHeight: 1 };
|
|
329
|
+
const fieldStyle = { display: 'flex', flexDirection: 'column', gap: 5, minWidth: 0 };
|
|
330
|
+
const selectFieldStyle = { ...fieldStyle };
|
|
331
|
+
const fieldLabelStyle = { color: dshThemeColor.labelSecondary, fontSize: 12, fontWeight: 500 };
|
|
332
|
+
const inputStyle = { ...dshFieldStyle, width: '100%', boxSizing: 'border-box', minHeight: 32, padding: '6px 8px', borderRadius: 6, fontSize: 12 };
|
|
333
|
+
const runtimeHintStyle = { margin: '-5px 0 0', color: dshThemeColor.labelTertiary, fontSize: 11 };
|
|
334
|
+
const formGridStyle = { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 14 };
|
|
335
|
+
const proxyFieldStyle = { ...fieldStyle };
|
|
336
|
+
const proxyToggleStyle = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: 32, boxSizing: 'border-box', padding: '0 2px', border: 0, borderRadius: 0, color: dshThemeColor.labelSecondary, background: 'transparent', fontSize: 12, lineHeight: '18px' };
|
|
337
|
+
const formActionsStyle = { display: 'flex', justifyContent: 'flex-end', gap: 6, paddingTop: 4 };
|
|
338
|
+
function statusTone(message) { return /失败|错误|不能为空|无效/u.test(message) ? 'error' : /已保存|已更新|已删除|已开启|已结束/u.test(message) ? 'success' : 'info'; }
|
|
339
|
+
function statusStyle(tone) { const color = tone === 'success' ? dshThemeColor.success : tone === 'error' ? dshThemeColor.error : dshThemeColor.labelSecondary; return { display: 'flex', alignItems: 'center', gap: 7, padding: '8px 10px', border: `1px solid ${color}`, borderRadius: 6, color, background: 'color-mix(in srgb, currentColor 7%, transparent)', fontSize: 12 }; }
|
|
340
|
+
function runningInstances(profile, instances) { return instances.filter((instance) => instance.profileId === profile.id && instance.state === 'running').length; }
|
|
341
|
+
function formatCommand(profile) { return [profile.command, ...profile.args].join(' '); }
|
|
342
|
+
function createProfileDraft(profile) {
|
|
343
|
+
return {
|
|
344
|
+
id: profile?.id ?? null,
|
|
345
|
+
name: profile?.name ?? '',
|
|
346
|
+
cwdRelative: profile?.cwdRelative === '.' ? '' : profile?.cwdRelative ?? '',
|
|
347
|
+
commandLine: profile === undefined ? '' : formatCommand(profile),
|
|
348
|
+
shellProfileId: profile === undefined ? 'system' : profile.shell.profileId,
|
|
349
|
+
port: profile?.port === null || profile?.port === undefined ? '' : String(profile.port),
|
|
350
|
+
proxyEnabled: profile?.proxy.enabled ?? false,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function shellOptions(status) {
|
|
354
|
+
const detected = status?.profiles ?? [];
|
|
355
|
+
return [
|
|
356
|
+
{ value: 'system', label: status === null ? '系统默认' : '系统默认(自动选择)' },
|
|
357
|
+
...detected.map((profile) => ({ value: profile.profileId, label: profile.name })),
|
|
358
|
+
];
|
|
359
|
+
}
|
|
360
|
+
function resolveShell(profileId, status) {
|
|
361
|
+
const resolvedId = profileId === 'system' ? status?.resolvedProfileId ?? defaultShellForPlatform(status?.platform) : profileId;
|
|
362
|
+
const detected = status?.profiles.find((profile) => profile.profileId === resolvedId);
|
|
363
|
+
if (status !== null && detected === undefined)
|
|
364
|
+
throw new Error('所选终端 Shell 当前不可用,请重新选择');
|
|
365
|
+
return {
|
|
366
|
+
profileId: resolvedId,
|
|
367
|
+
path: detected?.path ?? shellPathFor(resolvedId),
|
|
368
|
+
args: shellArgsFor(resolvedId),
|
|
369
|
+
name: detected?.name ?? resolvedId,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function shellPathFor(profileId) {
|
|
373
|
+
if (profileId === 'powershell')
|
|
374
|
+
return 'powershell.exe';
|
|
375
|
+
if (profileId === 'cmd')
|
|
376
|
+
return 'cmd.exe';
|
|
377
|
+
if (profileId === 'bash')
|
|
378
|
+
return '/bin/bash';
|
|
379
|
+
if (profileId === 'git-bash')
|
|
380
|
+
return 'bash.exe';
|
|
381
|
+
return '/bin/zsh';
|
|
382
|
+
}
|
|
383
|
+
function shellArgsFor(profileId) {
|
|
384
|
+
return profileId === 'powershell' ? ['-NoLogo'] : profileId === 'cmd' ? [] : ['-i'];
|
|
385
|
+
}
|
|
386
|
+
function defaultShellForPlatform(platform) {
|
|
387
|
+
return platform === 'win32' ? 'powershell' : 'zsh';
|
|
388
|
+
}
|
|
389
|
+
function runtimeTypeFor(platform, shellProfileId) {
|
|
390
|
+
if (platform !== 'win32' && platform !== 'darwin' && platform !== 'linux')
|
|
391
|
+
throw new Error('Host 平台不支持自动选择终端运行方式');
|
|
392
|
+
if (platform !== 'win32')
|
|
393
|
+
return 'tmux';
|
|
394
|
+
if (shellProfileId === 'cmd')
|
|
395
|
+
return 'conpty-cmd';
|
|
396
|
+
if (shellProfileId === 'git-bash')
|
|
397
|
+
return 'conpty-git-bash';
|
|
398
|
+
return 'conpty-powershell';
|
|
399
|
+
}
|
|
400
|
+
function terminalRuntimeLabel(platform) {
|
|
401
|
+
if (platform === 'win32')
|
|
402
|
+
return 'Windows 使用 ConPTY';
|
|
403
|
+
if (platform === 'darwin' || platform === 'linux')
|
|
404
|
+
return `${platform === 'darwin' ? 'macOS' : 'Linux'} 使用 tmux`;
|
|
405
|
+
return '等待 Host 平台状态';
|
|
406
|
+
}
|
|
407
|
+
function splitCommandLine(value) {
|
|
408
|
+
const parts = [];
|
|
409
|
+
let current = '';
|
|
410
|
+
let quote = null;
|
|
411
|
+
let escaping = false;
|
|
412
|
+
for (const character of value.trim()) {
|
|
413
|
+
if (escaping) {
|
|
414
|
+
current += character;
|
|
415
|
+
escaping = false;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (character === '\\' && quote !== 'single') {
|
|
419
|
+
escaping = true;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (quote === null && (character === '"' || character === "'")) {
|
|
423
|
+
quote = character === '"' ? 'double' : 'single';
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if ((quote === 'double' && character === '"') || (quote === 'single' && character === "'")) {
|
|
427
|
+
quote = null;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (quote === null && /\s/u.test(character)) {
|
|
431
|
+
if (current !== '') {
|
|
432
|
+
parts.push(current);
|
|
433
|
+
current = '';
|
|
434
|
+
}
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
current += character;
|
|
438
|
+
}
|
|
439
|
+
if (escaping)
|
|
440
|
+
current += '\\';
|
|
441
|
+
if (current !== '')
|
|
442
|
+
parts.push(current);
|
|
443
|
+
if (quote !== null)
|
|
444
|
+
throw new Error('完整启动命令包含未闭合的引号');
|
|
445
|
+
return parts;
|
|
446
|
+
}
|
|
447
|
+
function createProfileId() {
|
|
448
|
+
const id = typeof globalThis.crypto?.randomUUID === 'function' ? globalThis.crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
449
|
+
return `debug-${id.slice(0, 12)}`;
|
|
450
|
+
}
|
|
451
|
+
//# sourceMappingURL=ui.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { DshDeviceListResponse, DshRelaySignalingTicket } from '../shared/contracts/dsh-device.js';
|
|
2
|
+
import { installDshTransport } from '../bootstrap/dsh-connection-adapter.js';
|
|
3
|
+
import { DshSession } from '../transport/dsh-session.js';
|
|
4
|
+
import type { DshHostScope } from '../transport/dsh-envelope.js';
|
|
5
|
+
import { DshCodingNsTransport } from '../transport/dsh-transport.js';
|
|
6
|
+
import type { CodingNsRpcClient } from './features/types.js';
|
|
7
|
+
import { RemoteDshWebContext, type RemoteDshWebContextOptions } from './remote-web-context.js';
|
|
8
|
+
/** H5 启动器需要的最小参数;不依赖 Node 或 Cordis 私有实现。 */
|
|
9
|
+
export interface DshH5BootstrapOptions {
|
|
10
|
+
readonly rpc: CodingNsRpcClient;
|
|
11
|
+
readonly dshDeviceId?: string;
|
|
12
|
+
/** 登录保护开启中继范围时使用的短期 Host 签名票据。 */
|
|
13
|
+
readonly loginProtectionToken?: string;
|
|
14
|
+
readonly signal?: AbortSignal;
|
|
15
|
+
readonly boot?: () => void | Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export interface DshH5BootstrapResult {
|
|
18
|
+
readonly dshDeviceId: string;
|
|
19
|
+
/** 当前 WebRTC ICE 策略,all 表示允许直连,relay 表示强制中转。 */
|
|
20
|
+
readonly relayMode: 'direct' | 'relay';
|
|
21
|
+
readonly registration: ReturnType<typeof installDshTransport>;
|
|
22
|
+
readonly dispose: () => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export interface DshH5BrowserControlApi {
|
|
25
|
+
listDevices(signal?: AbortSignal): Promise<DshDeviceListResponse>;
|
|
26
|
+
/** 可复用 sessionId,刷新页面时让 Relay 顶掉旧的同会话连接。 */
|
|
27
|
+
createClientTicket(dshDeviceId: string, signal?: AbortSignal, sessionId?: string): Promise<DshRelaySignalingTicket>;
|
|
28
|
+
}
|
|
29
|
+
export interface DshH5BrowserBootstrapOptions {
|
|
30
|
+
readonly controlApi: DshH5BrowserControlApi;
|
|
31
|
+
readonly dshDeviceId?: string;
|
|
32
|
+
readonly signal?: AbortSignal;
|
|
33
|
+
readonly webContext?: Omit<RemoteDshWebContextOptions, 'transport'>;
|
|
34
|
+
readonly generation?: number;
|
|
35
|
+
/** 浏览器侧稳定的 Relay Client 会话标识;不携带账号凭据。 */
|
|
36
|
+
readonly clientSessionId?: string;
|
|
37
|
+
/** 页面可用的阶段提示;不携带任何凭据或业务正文。 */
|
|
38
|
+
readonly onStatus?: (status: DshH5BootstrapStatus) => void;
|
|
39
|
+
}
|
|
40
|
+
export type DshH5BootstrapStatus = 'ticket' | 'webrtc' | 'session-ready' | 'remote-web';
|
|
41
|
+
export interface DshH5BrowserBootstrapResult {
|
|
42
|
+
readonly dshDeviceId: string;
|
|
43
|
+
readonly transport: DshCodingNsTransport;
|
|
44
|
+
readonly session: DshSession;
|
|
45
|
+
readonly webContext?: RemoteDshWebContext;
|
|
46
|
+
readonly dispose: () => Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* DSH H5 的最小入口:选择独立 DSH 设备、申请票据、建立 WebRTC,最后才启动远程 DSH。
|
|
50
|
+
* 账号凭据只在 Host RPC 内部使用,浏览器只看到设备摘要和短期票据。
|
|
51
|
+
*/
|
|
52
|
+
export declare function startDshH5Bootstrap(options: DshH5BootstrapOptions): Promise<DshH5BootstrapResult>;
|
|
53
|
+
/** 独立 H5 页面使用的入口;Control API 会话通过 HttpOnly Cookie 提供。 */
|
|
54
|
+
export declare function startDshH5BrowserBootstrap(options: DshH5BrowserBootstrapOptions): Promise<DshH5BrowserBootstrapResult>;
|
|
55
|
+
/** 使用同源或跨域 HttpOnly Cookie 调用控制站,不读取 Cookie 内容。 */
|
|
56
|
+
export declare function createHttpDshH5ControlApi(baseUrl?: string): DshH5BrowserControlApi;
|
|
57
|
+
export declare function chooseDshDevice(response: DshDeviceListResponse, requested?: string): import("../shared/index.js").DshDeviceSummary;
|
|
58
|
+
/**
|
|
59
|
+
* HostScope 是线上 Tunnel 的共享身份,不是两端各自的视角。
|
|
60
|
+
* Host Runtime 当前以 local 作为 canonical kind;旧 Control API 未下发
|
|
61
|
+
* hostScope 时也必须回退到同一值,否则首个 session.hello 会被 Host 拒绝。
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveDshHostScope(ticket: DshRelaySignalingTicket): DshHostScope;
|
|
64
|
+
//# sourceMappingURL=dsh-h5-bootstrap.d.ts.map
|