dsh-remote-local-plugin 0.8.0 → 0.8.1

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 CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  Remote development that appears as ordinary local sessions.
4
4
 
5
+ ## Supported environment
6
+
7
+ This plugin targets the official **DSH Desktop** application. Tested/supported versions:
8
+
9
+ - [DSH Desktop](https://github.com/deepseek-ai/deepseek-harness) [`0.2.1-rc.2`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-rc.2)
10
+ - Plugin [`0.8.1`](https://www.npmjs.com/package/dsh-remote-local-plugin)
11
+ - Remote DSH [`0.2.1-rc.2`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-rc.2)
12
+ - Remote [Node.js `22.19+`](https://nodejs.org/en/download) on Linux/macOS
13
+
14
+ Other DSH GUI plugins, forks, modified clients, and other DSH versions have not been tested. The
15
+ package dependency range is permissive for installation only; it is not a support guarantee.
16
+
5
17
  ## What it does
6
18
 
7
19
  - Durable host registry at `$DSH_HOME/remote/hosts.json` (mode `0600`).
Binary file
package/lib/client.js CHANGED
@@ -133,6 +133,26 @@ var DETAIL_KEYS = {
133
133
  "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026": "detail.connecting",
134
134
  "\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026": "detail.reprovisioning",
135
135
  "SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026": "detail.reconnecting",
136
+ "\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026": "detail.preparing",
137
+ "\u6B63\u5728\u4E0A\u4F20\u8FDC\u7AEF agent \u5B89\u88C5\u5305\u2026": "detail.uploadingAgent",
138
+ "\u5B89\u88C5\u5305\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5F00\u59CB\u8FDC\u7AEF\u5B89\u88C5\u2026": "detail.uploaded",
139
+ "\u6B63\u5728\u4E0B\u8F7D Node.js\u2026": "detail.nodeDownloading",
140
+ "Node.js \u5B89\u88C5\u5B8C\u6210\uFF0C\u7EE7\u7EED\u5B89\u88C5 pnpm/DSH\u2026": "detail.nodeInstalled",
141
+ "Node.js \u5DF2\u5C31\u7EEA\uFF0C\u51C6\u5907\u5B89\u88C5 pnpm/DSH\u2026": "detail.nodeReady",
142
+ "\u6B63\u5728\u5B89\u88C5 pnpm\u2026": "detail.pnpmInstalling",
143
+ "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF DSH\uFF08\u4E0B\u8F7D\u4F9D\u8D56\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF09\u2026": "detail.dshInstalling",
144
+ "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF agent\u2026": "detail.agentInstalling",
145
+ "\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026": "detail.webStarting",
146
+ "\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u6587\u4EF6\u2026": "detail.waitingState",
147
+ "\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u2026": "detail.waitingAgent",
148
+ "\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026": "detail.agentReady",
149
+ "\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\u2026": "detail.installed",
150
+ "\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u7B49\u5F85 DSH web \u542F\u52A8\u2026": "detail.waitingWeb",
151
+ "\u4ECD\u5728\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026": "detail.stillWaiting",
152
+ "\u6B63\u5728\u8BFB\u53D6\u8FDC\u7AEF agent \u72B6\u6001\u2026": "detail.readingState",
153
+ "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u2026": "detail.tunnel",
154
+ "\u6B63\u5728\u6821\u9A8C\u8FDC\u7AEF agent\u2026": "detail.checkingAgent",
155
+ "\u6B63\u5728\u540C\u6B65\u8FDC\u7AEF\u6A21\u578B\u73AF\u5883\u2026": "detail.reconcilingModel",
136
156
  "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1": "detail.connectedProxy",
137
157
  "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek": "detail.connectedDirect",
138
158
  "\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key": "detail.connectedRemote"
@@ -762,6 +782,26 @@ var zh = {
762
782
  "detail.connectedProxy": "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1",
763
783
  "detail.connectedDirect": "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek",
764
784
  "detail.connectedRemote": "\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key",
785
+ "detail.preparing": "\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026",
786
+ "detail.uploadingAgent": "\u6B63\u5728\u4E0A\u4F20\u8FDC\u7AEF agent \u5B89\u88C5\u5305\u2026",
787
+ "detail.uploaded": "\u5B89\u88C5\u5305\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5F00\u59CB\u8FDC\u7AEF\u5B89\u88C5\u2026",
788
+ "detail.nodeDownloading": "\u6B63\u5728\u4E0B\u8F7D Node.js\u2026",
789
+ "detail.nodeInstalled": "Node.js \u5B89\u88C5\u5B8C\u6210\uFF0C\u7EE7\u7EED\u5B89\u88C5 pnpm/DSH\u2026",
790
+ "detail.nodeReady": "Node.js \u5DF2\u5C31\u7EEA\uFF0C\u51C6\u5907\u5B89\u88C5 pnpm/DSH\u2026",
791
+ "detail.pnpmInstalling": "\u6B63\u5728\u5B89\u88C5 pnpm\u2026",
792
+ "detail.dshInstalling": "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF DSH\uFF08\u4E0B\u8F7D\u4F9D\u8D56\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF09\u2026",
793
+ "detail.agentInstalling": "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF agent\u2026",
794
+ "detail.webStarting": "\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026",
795
+ "detail.waitingState": "\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u6587\u4EF6\u2026",
796
+ "detail.waitingAgent": "\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u2026",
797
+ "detail.agentReady": "\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026",
798
+ "detail.installed": "\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\u2026",
799
+ "detail.waitingWeb": "\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u7B49\u5F85 DSH web \u542F\u52A8\u2026",
800
+ "detail.stillWaiting": "\u4ECD\u5728\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026",
801
+ "detail.readingState": "\u6B63\u5728\u8BFB\u53D6\u8FDC\u7AEF agent \u72B6\u6001\u2026",
802
+ "detail.tunnel": "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u2026",
803
+ "detail.checkingAgent": "\u6B63\u5728\u6821\u9A8C\u8FDC\u7AEF agent\u2026",
804
+ "detail.reconcilingModel": "\u6B63\u5728\u540C\u6B65\u8FDC\u7AEF\u6A21\u578B\u73AF\u5883\u2026",
765
805
  "dialog.title": "\u65B0\u5EFA\u5DE5\u4F5C\u533A",
766
806
  "dialog.close": "\u5173\u95ED",
767
807
  "dialog.name": "\u5DE5\u4F5C\u533A\u540D\u79F0",
@@ -864,6 +904,26 @@ var en = {
864
904
  "detail.connectedProxy": "Connected; model requests go through the local credential proxy",
865
905
  "detail.connectedDirect": "Connected; the remote host calls DeepSeek directly",
866
906
  "detail.connectedRemote": "Connected; the remote host uses its own credentials or has no model key",
907
+ "detail.preparing": "Preparing the remote installation environment\u2026",
908
+ "detail.uploadingAgent": "Uploading the remote agent package\u2026",
909
+ "detail.uploaded": "Package uploaded; starting the remote installation\u2026",
910
+ "detail.nodeDownloading": "Downloading Node.js\u2026",
911
+ "detail.nodeInstalled": "Node.js installed; continuing with pnpm/DSH\u2026",
912
+ "detail.nodeReady": "Node.js is ready; preparing pnpm/DSH\u2026",
913
+ "detail.pnpmInstalling": "Installing pnpm\u2026",
914
+ "detail.dshInstalling": "Installing remote DSH (dependency download may take several minutes)\u2026",
915
+ "detail.agentInstalling": "Installing the remote agent\u2026",
916
+ "detail.webStarting": "Starting the remote DSH Web service\u2026",
917
+ "detail.waitingState": "Waiting for the remote agent state file\u2026",
918
+ "detail.waitingAgent": "Waiting for the remote agent\u2026",
919
+ "detail.agentReady": "Remote agent is ready; establishing the connection\u2026",
920
+ "detail.installed": "Remote installation complete\u2026",
921
+ "detail.waitingWeb": "Remote installation complete; waiting for DSH Web\u2026",
922
+ "detail.stillWaiting": "Still waiting for remote DSH Web\u2026",
923
+ "detail.readingState": "Reading the remote agent state\u2026",
924
+ "detail.tunnel": "Establishing the SSH tunnel\u2026",
925
+ "detail.checkingAgent": "Checking the remote agent\u2026",
926
+ "detail.reconcilingModel": "Synchronizing the remote model environment\u2026",
867
927
  "dialog.title": "New workspace",
868
928
  "dialog.close": "Close",
869
929
  "dialog.name": "Workspace name",
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client/index.tsx", "../src/host-api/contract.ts", "../src/client/api.ts", "../src/client/RemoteDevelopmentPanel.tsx", "../src/client/panel.css", "../src/client/routing.ts", "../src/client/remote-files.ts", "../src/client/locale.ts", "../src/client/workspace-add.ts"],
4
- "sourcesContent": ["/**\n * dsh-remote-local-plugin browser entry.\n *\n * Registers one Settings section. The component talks only to the injected\n * API closure; the Cordis context stays in `apply`.\n *\n * @module dsh-remote-local/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport { createHostsApi } from './api.ts'\nimport { RemoteDevelopmentPanel } from './RemoteDevelopmentPanel.tsx'\nimport { installNativeWorkspaceRouting } from './routing.ts'\nimport { installRemoteFiles } from './remote-files.ts'\nimport { NS, en, zh, type LocaleFace } from './locale.ts'\nimport { installWorkspaceAddMenu } from './workspace-add.ts'\n\n/** The Settings shell's slot registry and the authenticated connection. */\nexport const inject = ['slots', 'uiWorkspace', 'locale']\n\n/** Register the \u8FDC\u7A0B\u5F00\u53D1 page. */\nexport function apply(ctx: Context): void {\n const api = createHostsApi(ctx)\n const locale = (ctx as unknown as { locale: LocaleFace }).locale\n ctx.effect(() => locale.register(NS, { zh, en }), 'dsh-remote: locale dictionaries')\n const t = locale.bind(NS)\n installNativeWorkspaceRouting(ctx, api)\n installWorkspaceAddMenu(ctx, api, t)\n ctx.inject(['remote.workspaceFiles'], child => {\n installRemoteFiles(child, api)\n })\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'remote-development',\n order: 76,\n label: () => t('settings.title'),\n locale: NS,\n inject: () => ({ api }),\n }, RemoteDevelopmentPanel))\n}\n", "/**\n * Host API contract shared by the host handler and the browser Settings page.\n *\n * The route is registered inside DSH Connection's authenticated `/api` fence,\n * so it works through the web HTTP carrier and the desktop IPC carrier without\n * a second authentication scheme.\n *\n * @module dsh-remote-local/host-api/contract\n */\n\nimport type { HostView } from '../hosts/types.ts'\nimport type {\n AcceptedResult,\n CreateSessionResult,\n HostInfo,\n PromptDeliveryMode,\n RemoteSessionSummary,\n RemoteWorkspace,\n SessionSnapshot,\n} from '@dsh-remote/protocol'\n\n/** Exact authenticated Fetch route owned by the local plugin. */\nexport const HOST_API_PATH = '/api/dsh-remote'\n\n/** Endpoint names. */\nexport type HostApiEndpoint =\n | 'state'\n | 'target/validate'\n | 'host/add'\n | 'host/remove'\n | 'host/connect'\n | 'host/disconnect'\n | 'host/refresh'\n | 'host/provision'\n | 'host/workspaces'\n | 'host/workspace/create'\n | 'host/directory/list'\n | 'local/directory/list'\n | 'local/workspace/create'\n | 'host/sessions'\n | 'host/session'\n | 'host/session/create'\n | 'host/session/prompt'\n | 'host/session/cancel'\n | 'host/web-url'\n | 'host/files/list'\n | 'host/files/read'\n | 'host/files/stat'\n | 'host/files/bytes'\n\n/** Browser-safe state snapshot. */\nexport interface RemoteHostsState {\n readonly protocolVersion: number\n readonly hosts: readonly HostView[]\n}\n\n/** Result of parsing a destination. */\nexport interface ValidatedTarget {\n readonly canonical: string\n readonly host: string\n readonly user: string | null\n readonly port: number | null\n}\n\n/** Result of listing workspaces. */\nexport interface HostWorkspace extends RemoteWorkspace {\n /** Local mirror workspace id, when it has been synced. */\n readonly localWorkspaceId?: string\n}\n\nexport interface WorkspacesResult {\n readonly workspaces: readonly HostWorkspace[]\n}\n\n/** Result of listing sessions. */\nexport interface SessionsResult {\n readonly sessions: readonly RemoteSessionSummary[]\n}\n\nexport interface HostCreateSessionResult extends CreateSessionResult {\n /** Local mirror session that opens in the ordinary Conversation UI. */\n readonly localSessionId?: string\n}\n\nexport interface MirrorFileResult {\n readonly mirrored: boolean\n readonly value?: unknown\n}\n\n/** Result of resolving the remote Web UI. */\nexport interface WebUrlResult {\n readonly url: string\n}\n\n/** One request body. */\nexport interface HostApiRequest {\n readonly endpoint: HostApiEndpoint\n readonly payload?: unknown\n}\n\n/** Success/failure result returned by the route. */\nexport type HostApiResult<T> =\n | { readonly ok: true; readonly value: T }\n | {\n readonly ok: false\n readonly error: {\n readonly code: string\n readonly message: string\n readonly details: Record<string, unknown>\n }\n }\n\n/** Payloads accepted by each endpoint. */\nexport interface HostApiPayloads {\n state: undefined\n 'target/validate': { sshTarget: string }\n 'host/add': {\n sshTarget: string\n label?: string\n token?: string\n remoteAgentPort?: number\n autoConnect?: boolean\n }\n 'host/remove': { hostId: string }\n 'host/connect': { hostId: string }\n 'host/disconnect': { hostId: string }\n 'host/refresh': { hostId: string }\n 'host/provision': { hostId: string }\n 'host/workspaces': { hostId: string }\n 'host/workspace/create': { hostId: string; path: string; title?: string }\n 'host/directory/list': { hostId: string; path?: string }\n 'local/directory/list': { path?: string }\n 'local/workspace/create': { path: string; title?: string }\n 'host/sessions': { hostId: string; workspaceId?: string }\n 'host/session': { hostId: string; sessionId: string; limit?: number }\n 'host/session/create': { hostId: string; workspaceId?: string; cwd?: string; agentPreset?: string }\n 'host/session/prompt': {\n hostId: string\n sessionId: string\n text: string\n mode?: PromptDeliveryMode\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n 'host/session/cancel': { hostId: string; sessionId: string }\n 'host/web-url': { hostId: string }\n 'host/files/list': { sessionId: string; path: string }\n 'host/files/read': { sessionId: string; path: string; offset?: number; limit?: number }\n 'host/files/stat': { sessionId: string; path: string }\n 'host/files/bytes': { sessionId: string; path: string; offset?: number; length?: number }\n}\n\n/** Values returned by each endpoint. */\nexport interface HostApiValues {\n state: RemoteHostsState\n 'target/validate': ValidatedTarget\n 'host/add': HostView\n 'host/remove': { removed: true }\n 'host/connect': HostView\n 'host/disconnect': HostView\n 'host/refresh': HostView\n 'host/provision': HostView\n 'host/workspaces': WorkspacesResult\n 'host/workspace/create': RemoteWorkspace\n 'host/directory/list': { path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }\n 'local/directory/list': { path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }\n 'local/workspace/create': { workspaceId: string }\n 'host/sessions': SessionsResult\n 'host/session': SessionSnapshot\n 'host/session/create': HostCreateSessionResult\n 'host/session/prompt': AcceptedResult\n 'host/session/cancel': AcceptedResult\n 'host/web-url': WebUrlResult\n 'host/files/list': MirrorFileResult\n 'host/files/read': MirrorFileResult\n 'host/files/stat': MirrorFileResult\n 'host/files/bytes': MirrorFileResult\n}\n\n/** Informational type for the Settings page. */\nexport interface HostSummary extends Pick<HostView, 'id' | 'label' | 'sshTarget' | 'phase'> {\n readonly hostInfo: HostInfo | null\n}\n", "/**\n * Browser-side API wrapper around the host's authenticated Fetch route.\n * Components never see `ctx`.\n *\n * @module dsh-remote-local/client/api\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { AcceptedResult, PromptDeliveryMode, RemoteWorkspace, SessionSnapshot } from '@dsh-remote/protocol'\nimport type { HostCreateSessionResult, MirrorFileResult } from '../host-api/contract.ts'\nimport {\n HOST_API_PATH,\n type HostApiPayloads,\n type HostApiValues,\n type RemoteHostsState,\n type SessionsResult,\n type ValidatedTarget,\n type WebUrlResult,\n type WorkspacesResult,\n} from '../host-api/contract.ts'\nimport type { HostView } from '../hosts/types.ts'\n\n/** Error surfaced by one host call. */\nexport class RemoteUiError extends Error {\n readonly code: string\n readonly details: Record<string, unknown>\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message)\n this.name = 'RemoteUiError'\n this.code = code\n this.details = details\n }\n}\n\n/** API surface injected into the Settings component. */\nexport interface HostsApi {\n readonly state: () => Promise<RemoteHostsState>\n readonly validateTarget: (sshTarget: string) => Promise<ValidatedTarget>\n readonly addHost: (input: HostApiPayloads['host/add']) => Promise<HostView>\n readonly removeHost: (hostId: string) => Promise<void>\n readonly connect: (hostId: string) => Promise<HostView>\n readonly disconnect: (hostId: string) => Promise<HostView>\n readonly refresh: (hostId: string) => Promise<HostView>\n readonly provision: (hostId: string) => Promise<HostView>\n readonly workspaces: (hostId: string) => Promise<WorkspacesResult>\n readonly listDirectories: (hostId: string, path?: string) => Promise<{ path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }>\n readonly listLocalDirectories: (path?: string) => Promise<{ path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }>\n readonly createLocalWorkspace: (input: { path: string; title?: string }) => Promise<{ workspaceId: string }>\n readonly createWorkspace: (hostId: string, input: { path: string; title?: string }) => Promise<RemoteWorkspace>\n readonly sessions: (hostId: string, workspaceId?: string) => Promise<SessionsResult>\n readonly webUrl: (hostId: string) => Promise<WebUrlResult>\n readonly snapshot: (hostId: string, sessionId: string, limit?: number) => Promise<SessionSnapshot>\n readonly createSession: (hostId: string, input: { workspaceId?: string; cwd?: string; agentPreset?: string }) => Promise<HostCreateSessionResult>\n readonly openSession: (sessionId: string) => void\n readonly fileList: (sessionId: string, path: string) => Promise<MirrorFileResult>\n readonly fileRead: (sessionId: string, path: string, offset?: number, limit?: number) => Promise<MirrorFileResult>\n readonly fileStat: (sessionId: string, path: string) => Promise<MirrorFileResult>\n readonly fileBytes: (sessionId: string, path: string, offset?: number, length?: number) => Promise<MirrorFileResult>\n readonly prompt: (hostId: string, sessionId: string, text: string, mode?: PromptDeliveryMode, selection?: { provider: string; model: string; reasoningEffort?: string }) => Promise<AcceptedResult>\n readonly cancel: (hostId: string, sessionId: string) => Promise<AcceptedResult>\n}\n\n/** Build the API closure once per plugin mount. */\nexport function createHostsApi(ctx: Context): HostsApi {\n return {\n state: () => call('state', undefined),\n validateTarget: sshTarget => call('target/validate', { sshTarget }),\n addHost: input => call('host/add', input),\n removeHost: async hostId => { await call('host/remove', { hostId }) },\n connect: hostId => call('host/connect', { hostId }),\n disconnect: hostId => call('host/disconnect', { hostId }),\n refresh: hostId => call('host/refresh', { hostId }),\n provision: hostId => call('host/provision', { hostId }),\n workspaces: hostId => call('host/workspaces', { hostId }),\n listDirectories: (hostId, path) => call('host/directory/list', { hostId, ...path === undefined ? {} : { path } }),\n listLocalDirectories: path => call('local/directory/list', { ...path === undefined ? {} : { path } }),\n createLocalWorkspace: input => call('local/workspace/create', input),\n createWorkspace: (hostId, input) => call('host/workspace/create', { hostId, ...input }),\n sessions: (hostId, workspaceId) => call('host/sessions', {\n hostId,\n ...workspaceId === undefined ? {} : { workspaceId },\n }),\n webUrl: hostId => call('host/web-url', { hostId }),\n snapshot: (hostId, sessionId, limit) => call('host/session', {\n hostId,\n sessionId,\n ...limit === undefined ? {} : { limit },\n }),\n createSession: (hostId, input) => call('host/session/create', { hostId, ...input }),\n openSession: sessionId => { ctx.uiWorkspace.openSession(sessionId as never) },\n fileList: (sessionId, path) => call('host/files/list', { sessionId, path }),\n fileRead: (sessionId, path, offset, limit) => call('host/files/read', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...limit === undefined ? {} : { limit },\n }),\n fileStat: (sessionId, path) => call('host/files/stat', { sessionId, path }),\n fileBytes: (sessionId, path, offset, length) => call('host/files/bytes', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...length === undefined ? {} : { length },\n }),\n prompt: (hostId, sessionId, text, mode, selection) => call('host/session/prompt', {\n hostId,\n sessionId,\n text,\n ...mode === undefined ? {} : { mode },\n ...selection === undefined ? {} : selection,\n }),\n cancel: (hostId, sessionId) => call('host/session/cancel', { hostId, sessionId }),\n }\n}\n\nasync function call<K extends keyof HostApiPayloads & string>(\n endpoint: K,\n payload: HostApiPayloads[K],\n): Promise<HostApiValues[K]> {\n const response = await fetch(HOST_API_PATH, {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload === undefined ? { endpoint } : { endpoint, payload }),\n })\n if (!response.ok) {\n throw new RemoteUiError('request/invalid', `host API returned HTTP ${response.status}`)\n }\n const result = await response.json() as\n | { ok: true; value: HostApiValues[K] }\n | { ok: false; error: { code: string; message: string; details?: Record<string, unknown> } }\n if (!result.ok) {\n throw new RemoteUiError(result.error.code, result.error.message, result.error.details ?? {})\n }\n return result.value\n}\n", "/**\n * Settings \u2192 \u8FDC\u7A0B\u5F00\u53D1 page.\n *\n * The page is a projection over the host plugin's authenticated RPC channel.\n * It owns only view state (form fields, which host is expanded); every\n * durable fact and every tunnel lives in the host process.\n *\n * @module dsh-remote-local/client/panel\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport type { RemoteSessionSummary, RemoteWorkspace } from '@dsh-remote/protocol'\nimport type { HostView } from '../hosts/types.ts'\nimport type { RemoteHostsState } from '../host-api/contract.ts'\nimport type { HostsApi } from './api.ts'\nimport type { CopyKey, Translate } from './locale.ts'\nimport panelCss from './panel.css'\n\n/** Props injected by the Settings page. */\ninterface PanelProps {\n readonly api: HostsApi\n readonly t: Translate\n}\n\nfunction phaseLabel(t: Translate, phase: HostView['phase']): string {\n return t(`phase.${phase}` as CopyKey)\n}\n\nconst DETAIL_KEYS: Record<string, CopyKey> = {\n '\u6B63\u5728\u901A\u8FC7 SSH \u53D1\u73B0\u8FDC\u7AEF agent\u2026': 'detail.discovering',\n '\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026': 'detail.installing',\n '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026': 'detail.connecting',\n '\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026': 'detail.reprovisioning',\n 'SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026': 'detail.reconnecting',\n '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1': 'detail.connectedProxy',\n '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek': 'detail.connectedDirect',\n '\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key': 'detail.connectedRemote',\n}\n\nfunction detailLabel(t: Translate, detail: string): string {\n const key = DETAIL_KEYS[detail]\n return key === undefined ? detail : t(key)\n}\n\nconst POLL_MS = 2_500\n\n/** Render the remote-development settings page. */\nexport function RemoteDevelopmentPanel({ api, t }: PanelProps) {\n const [state, setState] = useState<RemoteHostsState | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [notice, setNotice] = useState<string | null>(null)\n const [busy, setBusy] = useState<string | null>(null)\n const [expandedHost, setExpandedHost] = useState<string | null>(null)\n const [workspaces, setWorkspaces] = useState<Record<string, RemoteWorkspace[] | undefined>>({})\n const [openWorkspace, setOpenWorkspace] = useState<Record<string, boolean>>({})\n const [sessions, setSessions] = useState<Record<string, RemoteSessionSummary[] | undefined>>({})\n const [workspaceDraft, setWorkspaceDraft] = useState('')\n const [form, setForm] = useState({\n sshTarget: '',\n label: '',\n })\n\n useInjectedStyle(panelCss)\n\n const hosts = useMemo(() => state?.hosts ?? [], [state])\n\n useEffect(() => {\n let cancelled = false\n const load = async (): Promise<void> => {\n try {\n const next = await api.state()\n if (!cancelled) {\n setState(next)\n setError(current => current === t('settings.unavailable') ? null : current)\n }\n } catch (failure) {\n if (!cancelled) setError(messageOf(failure, t('settings.unavailable')))\n }\n }\n void load()\n const timer = window.setInterval(() => { void load() }, POLL_MS)\n const onFocus = (): void => { void load() }\n window.addEventListener('focus', onFocus)\n return () => {\n cancelled = true\n window.clearInterval(timer)\n window.removeEventListener('focus', onFocus)\n }\n }, [api, t])\n\n useEffect(() => {\n const host = hosts.find(item => item.id === expandedHost)\n if (host?.phase === 'connected' && workspaces[host.id] === undefined) {\n void loadWorkspaces(host.id)\n }\n }, [hosts, expandedHost, workspaces])\n\n async function refresh(): Promise<void> {\n setState(await api.state())\n }\n\n async function run(key: string, task: () => Promise<void>): Promise<void> {\n setBusy(key)\n setError(null)\n setNotice(null)\n try {\n await task()\n await refresh()\n } catch (failure) {\n setError(messageOf(failure, t('settings.operationFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n async function onAdd(): Promise<void> {\n const sshTarget = form.sshTarget.trim()\n if (sshTarget === '') {\n setError(t('settings.sshRequired'))\n return\n }\n await run('add', async () => {\n await api.validateTarget(sshTarget)\n const host = await api.addHost({\n sshTarget,\n ...form.label.trim() === '' ? {} : { label: form.label.trim() },\n autoConnect: true,\n })\n setForm({ sshTarget: '', label: '' })\n setExpandedHost(host.id)\n setNotice(t('settings.hostSaved'))\n })\n }\n\n async function toggleHost(host: HostView): Promise<void> {\n if (expandedHost === host.id) {\n setExpandedHost(null)\n return\n }\n setExpandedHost(host.id)\n await loadWorkspaces(host.id)\n }\n\n async function loadWorkspaces(hostId: string): Promise<void> {\n setWorkspaces(current => ({ ...current, [hostId]: undefined }))\n try {\n const result = await api.workspaces(hostId)\n setWorkspaces(current => ({ ...current, [hostId]: [...result.workspaces] }))\n } catch (failure) {\n setWorkspaces(current => ({ ...current, [hostId]: [] }))\n setError(messageOf(failure, t('settings.readWorkspacesFailed')))\n }\n }\n\n async function toggleWorkspace(hostId: string, workspaceId: string): Promise<void> {\n const key = `${hostId}:${workspaceId}`\n const next = !(openWorkspace[key] ?? false)\n setOpenWorkspace(current => ({ ...current, [key]: next }))\n if (!next || sessions[key] !== undefined) return\n setSessions(current => ({ ...current, [key]: undefined }))\n try {\n const result = await api.sessions(hostId, workspaceId)\n setSessions(current => ({ ...current, [key]: [...result.sessions] }))\n } catch (failure) {\n setSessions(current => ({ ...current, [key]: [] }))\n setError(messageOf(failure, t('settings.readSessionsFailed')))\n }\n }\n\n async function addWorkspace(host: HostView): Promise<void> {\n const path = workspaceDraft.trim()\n if (path === '') {\n setError(t('settings.pathRequired'))\n return\n }\n setBusy(`workspace:${host.id}`)\n setError(null)\n try {\n await api.createWorkspace(host.id, { path })\n setWorkspaceDraft('')\n await loadWorkspaces(host.id)\n setExpandedHost(host.id)\n } catch (failure) {\n setError(messageOf(failure, t('settings.addWorkspaceFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n\n\n async function newSession(host: HostView, workspaceId: string): Promise<void> {\n setBusy(`create:${host.id}:${workspaceId}`)\n setError(null)\n try {\n const created = await api.createSession(host.id, { workspaceId })\n const key = `${host.id}:${workspaceId}`\n setOpenWorkspace(current => ({ ...current, [key]: true }))\n if (created.localSessionId !== undefined) {\n api.openSession(created.localSessionId)\n setNotice(t('settings.sessionOpening'))\n window.setTimeout(() => {\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }, 80)\n } else {\n setNotice(t('settings.sessionSyncing'))\n }\n const result = await api.sessions(host.id, workspaceId)\n setSessions(current => ({ ...current, [key]: [...result.sessions] }))\n const refreshed = await api.workspaces(host.id)\n setWorkspaces(current => ({ ...current, [host.id]: [...refreshed.workspaces] }))\n } catch (failure) {\n setError(messageOf(failure, t('settings.createSessionFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n\n\n async function onRemove(host: HostView): Promise<void> {\n if (!window.confirm(`${t('settings.confirmRemovePrefix')}${host.label}${t('settings.confirmRemoveSuffix')}`)) return\n await run(`remove:${host.id}`, async () => {\n await api.removeHost(host.id)\n if (expandedHost === host.id) setExpandedHost(null)\n })\n }\n\n return (\n <div className=\"dshr-root\">\n <header className=\"dshr-header\">\n <div>\n <h2>{t('settings.title')}</h2>\n <p className=\"dshr-subtitle\">\n {t('settings.subtitle.lead')}\n {' '}\n {t('settings.subtitle.body')}\n </p>\n </div>\n <button\n type=\"button\"\n className=\"dshr-button\"\n onClick={() => { void refresh().catch(failure => setError(messageOf(failure, t('settings.refreshFailed')))) }}\n disabled={busy !== null}\n >\n {t('settings.refresh')}\n </button>\n </header>\n\n {error !== null && <p className=\"dshr-banner dshr-banner-error\" role=\"alert\">{error}</p>}\n {notice !== null && <p className=\"dshr-banner dshr-banner-info\" role=\"status\">{notice}</p>}\n\n <section className=\"dshr-card\">\n <h3>{t('settings.addHost')}</h3>\n <form\n className=\"dshr-form\"\n onSubmit={event => {\n event.preventDefault()\n void onAdd()\n }}\n >\n <label className=\"dshr-field dshr-field-wide\">\n <span>{t('settings.sshTarget')}</span>\n <input\n value={form.sshTarget}\n onChange={event => setForm(current => ({ ...current, sshTarget: event.target.value }))}\n placeholder={t('settings.sshPlaceholder')}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </label>\n <label className=\"dshr-field\">\n <span>{t('settings.label')}</span>\n <input\n value={form.label}\n onChange={event => setForm(current => ({ ...current, label: event.target.value }))}\n placeholder={t('settings.labelPlaceholder')}\n />\n </label>\n <div className=\"dshr-form-actions\">\n <button type=\"submit\" className=\"dshr-button dshr-button-primary\" disabled={busy !== null}>\n {busy === 'add' ? t('settings.adding') : t('settings.saveConnect')}\n </button>\n <span className=\"dshr-hint\">{t('settings.sshHint')}</span>\n </div>\n </form>\n </section>\n\n <section className=\"dshr-card\">\n <div className=\"dshr-section-title\">\n <h3>{t('settings.hosts')}</h3>\n <span className=\"dshr-hint\">{hosts.length} {t('settings.hostsUnit')}</span>\n </div>\n {hosts.length === 0 && <p className=\"dshr-empty\">{t('settings.noHosts')}</p>}\n <div className=\"dshr-hosts\">\n {hosts.map(host => (\n <article key={host.id} className=\"dshr-host\">\n <header className=\"dshr-host-head\">\n <div className=\"dshr-host-title\">\n <strong>{host.label}</strong>\n <code>{host.sshTarget}</code>\n </div>\n <span className={`dshr-phase dshr-phase-${host.phase}`}>{phaseLabel(t, host.phase)}</span>\n </header>\n\n <dl className=\"dshr-meta\">\n <div><dt>{t('settings.remotePort')}</dt><dd>{host.remoteAgentPort ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.localTunnel')}</dt><dd>{host.localPort ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.workspaces')}</dt><dd>{host.workspaceCount ?? '\u2014'}</dd></div>\n <div><dt>Agent</dt><dd>{host.hostInfo?.agentVersion ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.token')}</dt><dd>{host.hasToken ? t('settings.saved') : t('settings.unsaved')}</dd></div>\n <div><dt>{t('settings.modelProxy')}</dt><dd>{host.modelProxy ? t('settings.localProxy') : host.modelDirect ? t('settings.direct') : t('settings.remoteDefault')}</dd></div>\n </dl>\n\n {host.detail !== null && <p className=\"dshr-detail\">{detailLabel(t, host.detail)}</p>}\n {host.error !== null && <p className=\"dshr-host-error\" role=\"alert\">{host.error}</p>}\n\n <div className=\"dshr-actions\">\n {host.phase === 'connected'\n ? (\n <>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void toggleHost(host) }}\n >\n {expandedHost === host.id ? t('settings.collapseWorkspaces') : t('settings.expandWorkspaces')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`refresh:${host.id}`, async () => { await api.refresh(host.id) }) }}\n >\n {t('settings.rediscover')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`disconnect:${host.id}`, async () => { await api.disconnect(host.id) }) }}\n >\n {t('settings.disconnect')}\n </button>\n </>\n )\n : (\n <>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-primary\"\n disabled={busy !== null || host.phase === 'installing' || host.phase === 'discovering' || host.phase === 'connecting'}\n onClick={() => { void run(`connect:${host.id}`, async () => { await api.connect(host.id) }) }}\n >\n {host.phase === 'installing' ? t('settings.installing') : host.phase === 'connecting' ? t('settings.connecting') : t('settings.connect')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`refresh:${host.id}`, async () => { await api.refresh(host.id) }) }}\n >\n {t('settings.rediscover')}\n </button>\n </>\n )}\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null || host.phase === 'installing'}\n onClick={() => { void run(`provision:${host.id}`, async () => { await api.provision(host.id) }) }}\n >\n {t('settings.reprovision')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-danger\"\n disabled={busy !== null}\n onClick={() => { void onRemove(host) }}\n >\n {t('settings.remove')}\n </button>\n </div>\n\n {expandedHost === host.id && (\n <div className=\"dshr-workspaces\">\n <div className=\"dshr-workspace-add\">\n <input\n value={workspaceDraft}\n onChange={event => setWorkspaceDraft(event.target.value)}\n placeholder={t('settings.workspacePathPlaceholder')}\n spellCheck={false}\n onKeyDown={event => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void addWorkspace(host)\n }\n }}\n />\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-small\"\n disabled={busy !== null}\n onClick={() => { void addWorkspace(host) }}\n >\n {busy === `workspace:${host.id}` ? t('settings.addingWorkspace') : t('settings.addWorkspace')}\n </button>\n </div>\n {workspaces[host.id] === undefined\n ? <p className=\"dshr-empty\">{t('settings.readingWorkspaces')}</p>\n : workspaces[host.id]?.length === 0\n ? <p className=\"dshr-empty\">{t('settings.noWorkspaces')}</p>\n : workspaces[host.id]?.map(workspace => {\n const workspaceKey = `${host.id}:${workspace.workspaceId}`\n const open = openWorkspace[workspaceKey] ?? false\n return (\n <div key={workspace.workspaceId} className=\"dshr-workspace\">\n <div className=\"dshr-workspace-row\">\n <button\n type=\"button\"\n className=\"dshr-workspace-head\"\n aria-expanded={open}\n onClick={() => { void toggleWorkspace(host.id, workspace.workspaceId) }}\n >\n <span className=\"dshr-caret\">{open ? '\u25BE' : '\u25B8'}</span>\n <span>{workspace.title}</span>\n <code>{workspace.path}</code>\n <span className=\"dshr-count\">{workspace.sessionIds.length} {t('settings.sessionsUnit')}</span>\n </button>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-small\"\n disabled={busy !== null}\n onClick={() => { void newSession(host, workspace.workspaceId) }}\n >\n {busy === `create:${host.id}:${workspace.workspaceId}` ? t('settings.creating') : t('settings.newSession')}\n </button>\n </div>\n {open && (\n <div className=\"dshr-sessions\">\n {sessions[workspaceKey] === undefined\n ? <p className=\"dshr-empty\">{t('settings.readingSessions')}</p>\n : sessions[workspaceKey]?.length === 0\n ? <p className=\"dshr-empty\">{t('settings.noSessions')}</p>\n : sessions[workspaceKey]?.map(session => (\n <div key={session.sessionId} className=\"dshr-session\">\n <span className={session.live ? 'dshr-live' : 'dshr-idle'} />\n <code>{session.sessionId}</code>\n <span>{session.cwd ?? '\u2014'}</span>\n <span className=\"dshr-count\">{session.eventCount ?? '\u2014'} {t('settings.eventsUnit')}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </article>\n ))}\n </div>\n </section>\n\n </div>\n )\n}\n\nfunction useInjectedStyle(css: string): void {\n useEffect(() => {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-remote-local'\n tag.textContent = css\n document.head.appendChild(tag)\n return () => { tag.remove() }\n }, [css])\n}\n\nfunction messageOf(value: unknown, fallback: string): string {\n return value instanceof Error && value.message !== '' ? value.message : fallback\n}\n", ".dshr-root {\n --dshr-accent: #4f7cff;\n --dshr-danger: #d24b4b;\n --dshr-success: #2f9e63;\n --dshr-warning: #c98417;\n --dshr-border: color-mix(in srgb, currentColor 14%, transparent);\n --dshr-soft: color-mix(in srgb, currentColor 4%, transparent);\n --dshr-softer: color-mix(in srgb, currentColor 2.5%, transparent);\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px 2px 32px;\n font-size: 13px;\n line-height: 1.5;\n}\n\n.dshr-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.dshr-header h2 {\n margin: 0 0 4px;\n font-size: 17px;\n font-weight: 650;\n}\n\n.dshr-subtitle {\n max-width: 760px;\n margin: 0;\n opacity: 0.72;\n}\n\n.dshr-subtitle code,\n.dshr-host-title code,\n.dshr-workspace-head code,\n.dshr-session code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 0.92em;\n}\n\n.dshr-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--dshr-border);\n border-radius: 12px;\n background: var(--dshr-softer);\n}\n\n.dshr-card h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 650;\n}\n\n.dshr-section-title {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n}\n\n.dshr-form {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n\n.dshr-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.dshr-field-wide {\n grid-column: 1 / -1;\n}\n\n.dshr-field > span {\n font-size: 12px;\n opacity: 0.72;\n}\n\n.dshr-field input {\n width: 100%;\n box-sizing: border-box;\n padding: 7px 9px;\n border: 1px solid var(--dshr-border);\n border-radius: 8px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n outline: none;\n}\n\n.dshr-field input:focus {\n border-color: var(--dshr-accent);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dshr-accent) 22%, transparent);\n}\n\n.dshr-check {\n display: flex;\n align-items: center;\n gap: 7px;\n font-size: 12px;\n}\n\n.dshr-form-actions {\n display: flex;\n grid-column: 1 / -1;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.dshr-button {\n padding: 6px 12px;\n border: 1px solid var(--dshr-border);\n border-radius: 8px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.dshr-button:hover:not(:disabled) {\n background: color-mix(in srgb, currentColor 8%, transparent);\n}\n\n.dshr-button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n\n.dshr-button-primary {\n border-color: color-mix(in srgb, var(--dshr-accent) 65%, transparent);\n background: var(--dshr-accent);\n color: #fff;\n}\n\n.dshr-button-primary:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dshr-accent) 84%, #000);\n}\n\n.dshr-button-danger {\n border-color: color-mix(in srgb, var(--dshr-danger) 45%, transparent);\n color: var(--dshr-danger);\n}\n\n.dshr-hint {\n font-size: 12px;\n opacity: 0.62;\n}\n\n.dshr-empty {\n margin: 0;\n padding: 10px 0;\n opacity: 0.62;\n}\n\n.dshr-banner {\n margin: 0;\n padding: 9px 12px;\n border-radius: 9px;\n border: 1px solid var(--dshr-border);\n}\n\n.dshr-banner-error {\n border-color: color-mix(in srgb, var(--dshr-danger) 45%, transparent);\n background: color-mix(in srgb, var(--dshr-danger) 10%, transparent);\n color: var(--dshr-danger);\n}\n\n.dshr-banner-info {\n border-color: color-mix(in srgb, var(--dshr-accent) 35%, transparent);\n background: color-mix(in srgb, var(--dshr-accent) 9%, transparent);\n}\n\n.dshr-hosts {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.dshr-host {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n border: 1px solid var(--dshr-border);\n border-radius: 10px;\n background: var(--dshr-soft);\n}\n\n.dshr-host-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n\n.dshr-host-title {\n display: flex;\n align-items: baseline;\n gap: 10px;\n flex-wrap: wrap;\n min-width: 0;\n}\n\n.dshr-host-title strong {\n font-size: 14px;\n}\n\n.dshr-phase {\n flex: none;\n padding: 1px 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n border: 1px solid currentColor;\n opacity: 0.9;\n}\n\n.dshr-phase-connected {\n color: var(--dshr-success);\n}\n\n.dshr-phase-discovering,\n.dshr-phase-connecting {\n color: var(--dshr-warning);\n}\n\n.dshr-phase-error {\n color: var(--dshr-danger);\n}\n\n.dshr-phase-idle,\n.dshr-phase-disconnected {\n opacity: 0.62;\n}\n\n.dshr-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 6px 20px;\n margin: 0;\n font-size: 12px;\n}\n\n.dshr-meta > div {\n display: flex;\n gap: 5px;\n}\n\n.dshr-meta dt {\n opacity: 0.58;\n}\n\n.dshr-meta dd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n\n.dshr-host-error {\n margin: 0;\n color: var(--dshr-danger);\n font-size: 12px;\n overflow-wrap: anywhere;\n}\n\n.dshr-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.dshr-workspaces {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding-top: 4px;\n border-top: 1px dashed var(--dshr-border);\n}\n\n.dshr-workspace {\n border-radius: 8px;\n background: var(--dshr-softer);\n}\n\n.dshr-workspace-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n width: 100%;\n box-sizing: border-box;\n padding: 8px 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n flex-wrap: wrap;\n}\n\n.dshr-workspace-head:hover {\n background: var(--dshr-soft);\n}\n\n.dshr-workspace-head code {\n opacity: 0.6;\n}\n\n.dshr-caret {\n width: 12px;\n opacity: 0.6;\n}\n\n.dshr-count {\n margin-left: auto;\n font-size: 11px;\n opacity: 0.55;\n white-space: nowrap;\n}\n\n.dshr-sessions {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 0 10px 8px 30px;\n}\n\n.dshr-session {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 3px 0;\n font-size: 12px;\n flex-wrap: wrap;\n}\n\n.dshr-session > span:not(.dshr-live):not(.dshr-idle) {\n opacity: 0.62;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.dshr-live,\n.dshr-idle {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n transform: translateY(-1px);\n}\n\n.dshr-live {\n background: var(--dshr-success);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dshr-success) 20%, transparent);\n}\n\n.dshr-idle {\n background: color-mix(in srgb, currentColor 28%, transparent);\n}\n\n@media (max-width: 720px) {\n .dshr-form {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshr-header {\n flex-direction: column;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshr-button {\n transition: none;\n }\n}\n\n.dshr-detail {\n margin: 0;\n font-size: 12px;\n color: var(--dshr-warning);\n}\n\n.dshr-workspace-row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding-right: 6px;\n}\n\n.dshr-workspace-row .dshr-workspace-head {\n flex: 1;\n}\n\n.dshr-button-small {\n flex: none;\n padding: 3px 8px;\n font-size: 11px;\n}\n\n.dshr-workspace-add {\n display: flex;\n gap: 6px;\n padding: 4px 0 6px;\n}\n\n.dshr-workspace-add input {\n flex: 1;\n min-width: 0;\n padding: 5px 8px;\n border: 1px solid var(--dshr-border);\n border-radius: 7px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n font-size: 12px;\n outline: none;\n}\n\n.dshr-workspace-add input:focus {\n border-color: var(--dshr-accent);\n}\n", "/**\n * Route ordinary sidebar \"new session\" actions for mirrored workspaces to the\n * remote agent instead of creating a local Agent in the mirror anchor.\n *\n * The Settings page has its own `+ \u65B0\u4F1A\u8BDD` flow. This adapter covers the\n * standard workspace-row action so the native sidebar behaves the same way.\n *\n * @module dsh-remote-local/client/routing\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostsApi } from './api.ts'\n\ninterface StartSessionOptions {\n readonly agentPreset?: string\n}\n\ninterface WorkspaceService {\n startSession(workspaceId?: unknown, options?: StartSessionOptions): void\n}\n\n/** Install the sidebar routing adapter once per client mount. */\nexport function installNativeWorkspaceRouting(ctx: Context, api: HostsApi): void {\n const workspace = ctx.uiWorkspace as unknown as WorkspaceService | undefined\n if (workspace === undefined || typeof workspace.startSession !== 'function') return\n const original = workspace.startSession.bind(workspace)\n workspace.startSession = (workspaceId, options) => {\n if (workspaceId === undefined) {\n original(workspaceId, options)\n return\n }\n void route(api, String(workspaceId)).then(routed => {\n if (!routed) original(workspaceId, options)\n }).catch(() => { original(workspaceId, options) })\n }\n}\n\nasync function route(api: HostsApi, localWorkspaceId: string): Promise<boolean> {\n const state = await api.state()\n for (const host of state.hosts) {\n if (host.phase !== 'connected') continue\n const result = await api.workspaces(host.id)\n const workspace = result.workspaces.find(item => item.localWorkspaceId === localWorkspaceId)\n if (workspace === undefined) continue\n const created = await api.createSession(host.id, { workspaceId: workspace.workspaceId })\n if (created.localSessionId !== undefined) {\n api.openSession(created.localSessionId)\n return true\n }\n }\n return false\n}\n", "/**\n * Client proxy for workspace-files calls on mirrored Sessions.\n *\n * The Host's generated Remote methods bind the stock workspace-files service\n * before an instance adapter can replace them. The sidebar tree and document\n * preview therefore call this proxy, which asks the local plugin's host API to\n * resolve the mirror and read the remote workspace.\n *\n * @module dsh-remote-local/client/remote-files\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostsApi } from './api.ts'\n\ninterface RemoteFace {\n list(sessionId: string, path: string, signal: AbortSignal): Promise<unknown>\n read(sessionId: string, path: string, range: { offset?: number; limit?: number }, signal: AbortSignal): Promise<unknown>\n stat(sessionId: string, path: string, signal: AbortSignal): Promise<unknown>\n readBytes(sessionId: string, path: string, options: { range?: { offset?: number; length?: number }; baseFile?: string }, signal: AbortSignal): Promise<unknown>\n changes(sessionId: string, path: string, signal: AbortSignal): AsyncIterable<unknown>\n}\n\n/** Install the proxy on the client Remote face. */\nexport function installRemoteFiles(ctx: Context, api: HostsApi): void {\n const remote = ctx.remote as unknown as { workspaceFiles?: RemoteFace } | undefined\n const face = remote?.workspaceFiles\n if (face === undefined) return\n\n const original = {\n list: face.list.bind(face),\n read: face.read.bind(face),\n stat: face.stat.bind(face),\n readBytes: face.readBytes.bind(face),\n }\n\n face.list = async (sessionId, path, signal) => {\n const result = await api.fileList(sessionId, path)\n return result.mirrored ? { ok: true, value: result.value } : original.list(sessionId, path, signal)\n }\n face.read = async (sessionId, path, range, signal) => {\n const result = await api.fileRead(sessionId, path, range.offset, range.limit)\n return result.mirrored ? { ok: true, value: result.value } : original.read(sessionId, path, range, signal)\n }\n face.stat = async (sessionId, path, signal) => {\n const result = await api.fileStat(sessionId, path)\n return result.mirrored ? { ok: true, value: result.value } : original.stat(sessionId, path, signal)\n }\n face.readBytes = async (sessionId, path, options, signal) => {\n const result = await api.fileBytes(sessionId, path, options.range?.offset, options.range?.length)\n return result.mirrored ? { ok: true, value: result.value } : original.readBytes(sessionId, path, options, signal)\n }\n}\n", "/**\n * Client copy for the remote-development surfaces.\n *\n * The dictionaries are registered into DSH's locale service; `ctx.locale.bind`\n * follows the language selected in DSH. Settings components also receive the\n * framework `t` seat, so language switches re-render them.\n */\n\nexport const NS = 'dshRemote'\n\nexport const zh = {\n 'settings.title': '\u8FDC\u7A0B\u5F00\u53D1',\n 'settings.subtitle.lead': '\u53EA\u9700\u586B\u5199 SSH \u76EE\u6807\u3002\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u8FDC\u7AEF agent\uFF0C\u5EFA\u7ACB\u96A7\u9053\u3002',\n 'settings.subtitle.body': '\u8FDE\u63A5\u540E\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u548C\u4F1A\u8BDD\u4F1A\u540C\u6B65\u5230\u5DE6\u4FA7\uFF0C\u50CF\u672C\u5730\u4F1A\u8BDD\u4E00\u6837\u6253\u5F00\uFF1B\u6A21\u578B\u51ED\u636E\u9ED8\u8BA4\u5199\u5165\u8FDC\u7AEF\u670D\u52A1\u73AF\u5883\uFF1B\u4E5F\u53EF\u542F\u7528\u672C\u5730\u4EE3\u7406\u6A21\u5F0F\u3002',\n 'settings.refresh': '\u5237\u65B0',\n 'settings.addHost': '\u6DFB\u52A0\u4E3B\u673A',\n 'settings.sshTarget': 'SSH \u76EE\u6807',\n 'settings.sshPlaceholder': 'user@host \u6216 ssh://user@host:22',\n 'settings.label': '\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09',\n 'settings.labelPlaceholder': '\u53EF\u9009\uFF0C\u4F8B\u5982 \u5F00\u53D1\u673A',\n 'settings.saveConnect': '\u4FDD\u5B58\u5E76\u8FDE\u63A5',\n 'settings.adding': '\u6DFB\u52A0\u4E2D\u2026',\n 'settings.sshHint': '\u9700\u8981\u5BC6\u94A5\u6216 ssh-agent \u514D\u4EA4\u4E92\u767B\u5F55\uFF1B\u9996\u6B21\u81EA\u52A8\u5B89\u88C5\u7EA6 1\u20133 \u5206\u949F\u3002',\n 'settings.hosts': '\u4E3B\u673A',\n 'settings.hostsUnit': '\u53F0',\n 'settings.noHosts': '\u8FD8\u6CA1\u6709\u8FDC\u7A0B\u4E3B\u673A\u3002\u5148\u5728\u4E0A\u65B9\u6DFB\u52A0\u4E00\u53F0\u3002',\n 'settings.refreshFailed': '\u5237\u65B0\u5931\u8D25',\n 'settings.unavailable': '\u65E0\u6CD5\u8FDE\u63A5\u672C\u5730\u8FDC\u7A0B\u5BBF\u4E3B\u670D\u52A1',\n 'settings.operationFailed': '\u64CD\u4F5C\u5931\u8D25',\n 'settings.sshRequired': '\u8BF7\u586B\u5199 SSH \u76EE\u6807\uFF0C\u4F8B\u5982 user@host \u6216 ssh://user@host:22',\n 'settings.hostSaved': '\u4E3B\u673A\u5DF2\u4FDD\u5B58\u3002\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u8FDC\u7AEF agent\uFF1B\u5B89\u88C5\u8FDB\u5EA6\u4F1A\u663E\u793A\u5728\u4E3B\u673A\u5361\u7247\u4E0A\u3002',\n 'settings.readWorkspacesFailed': '\u8BFB\u53D6\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'settings.readSessionsFailed': '\u8BFB\u53D6\u8FDC\u7AEF\u4F1A\u8BDD\u5931\u8D25',\n 'settings.pathRequired': '\u8BF7\u586B\u5199\u8FDC\u7AEF\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/user/project',\n 'settings.addWorkspaceFailed': '\u6DFB\u52A0\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'settings.sessionOpening': '\u8FDC\u7AEF\u4F1A\u8BDD\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u666E\u901A\u4F1A\u8BDD\u754C\u9762\u6253\u5F00\u2026',\n 'settings.sessionSyncing': '\u8FDC\u7AEF\u4F1A\u8BDD\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u540C\u6B65\u5230\u4FA7\u8FB9\u680F\u2026',\n 'settings.createSessionFailed': '\u521B\u5EFA\u8FDC\u7AEF\u4F1A\u8BDD\u5931\u8D25',\n 'settings.confirmRemovePrefix': '\u5220\u9664\u4E3B\u673A\u300C',\n 'settings.confirmRemoveSuffix': '\u300D\uFF1F\u8FDC\u7AEF\u6587\u4EF6\u4E0E\u4F1A\u8BDD\u4E0D\u4F1A\u88AB\u5220\u9664\u3002',\n 'settings.remotePort': '\u8FDC\u7AEF\u7AEF\u53E3',\n 'settings.localTunnel': '\u672C\u5730\u96A7\u9053',\n 'settings.workspaces': '\u5DE5\u4F5C\u533A',\n 'settings.token': 'Token',\n 'settings.saved': '\u5DF2\u4FDD\u5B58',\n 'settings.unsaved': '\u672A\u4FDD\u5B58',\n 'settings.modelProxy': '\u6A21\u578B\u4EE3\u7406',\n 'settings.localProxy': '\u672C\u5730\u4EE3\u7406',\n 'settings.direct': '\u8FDC\u7AEF\u76F4\u8FDE',\n 'settings.remoteDefault': '\u8FDC\u7AEF\u9ED8\u8BA4',\n 'settings.collapseWorkspaces': '\u6536\u8D77\u5DE5\u4F5C\u533A',\n 'settings.expandWorkspaces': '\u5DE5\u4F5C\u533A',\n 'settings.rediscover': '\u91CD\u65B0\u53D1\u73B0',\n 'settings.disconnect': '\u65AD\u5F00',\n 'settings.connect': '\u8FDE\u63A5',\n 'settings.installing': '\u6B63\u5728\u5B89\u88C5\u2026',\n 'settings.connecting': '\u6B63\u5728\u8FDE\u63A5\u2026',\n 'settings.reprovision': '\u91CD\u65B0\u5B89\u88C5',\n 'settings.remove': '\u5220\u9664',\n 'settings.workspacePathPlaceholder': '\u8FDC\u7AEF\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/user/project',\n 'settings.addWorkspace': '\u6DFB\u52A0\u5DE5\u4F5C\u533A',\n 'settings.addingWorkspace': '\u6DFB\u52A0\u4E2D\u2026',\n 'settings.readingWorkspaces': '\u6B63\u5728\u8BFB\u53D6\u5DE5\u4F5C\u533A\u2026',\n 'settings.noWorkspaces': '\u8FDC\u7AEF\u8FD8\u6CA1\u6709\u6CE8\u518C\u5DE5\u4F5C\u533A\u3002\u53EF\u5728\u8FDC\u7AEF DSH \u91CC\u6DFB\u52A0\u76EE\u5F55\u540E\u5237\u65B0\u3002',\n 'settings.sessionsUnit': '\u4E2A\u4F1A\u8BDD',\n 'settings.newSession': '+ \u65B0\u4F1A\u8BDD',\n 'settings.creating': '\u521B\u5EFA\u4E2D\u2026',\n 'settings.readingSessions': '\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026',\n 'settings.noSessions': '\u8BE5\u5DE5\u4F5C\u533A\u6682\u65E0\u4F1A\u8BDD\u3002\u70B9\u201C+ \u65B0\u4F1A\u8BDD\u201D\u5F00\u59CB\u3002',\n 'settings.eventsUnit': '\u4E8B\u4EF6',\n 'phase.idle': '\u672A\u8FDE\u63A5',\n 'phase.discovering': '\u53D1\u73B0\u4E2D',\n 'phase.installing': '\u5B89\u88C5\u4E2D',\n 'phase.connecting': '\u8FDE\u63A5\u4E2D',\n 'phase.connected': '\u5DF2\u8FDE\u63A5',\n 'phase.disconnected': '\u5DF2\u65AD\u5F00',\n 'phase.error': '\u5F02\u5E38',\n 'detail.discovering': '\u6B63\u5728\u901A\u8FC7 SSH \u53D1\u73B0\u8FDC\u7AEF agent\u2026',\n 'detail.installing': '\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026',\n 'detail.connecting': '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026',\n 'detail.reprovisioning': '\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026',\n 'detail.reconnecting': 'SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026',\n 'detail.connectedProxy': '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1',\n 'detail.connectedDirect': '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek',\n 'detail.connectedRemote': '\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key',\n 'dialog.title': '\u65B0\u5EFA\u5DE5\u4F5C\u533A',\n 'dialog.close': '\u5173\u95ED',\n 'dialog.name': '\u5DE5\u4F5C\u533A\u540D\u79F0',\n 'dialog.namePlaceholder': '\u5DE5\u4F5C\u533A\u540D\u79F0\uFF08\u53EF\u9009\uFF09',\n 'dialog.source': '\u6E90\u76EE\u5F55',\n 'dialog.addToPrefix': '\u5C06\u76EE\u5F55\u6DFB\u52A0\u5230 ',\n 'dialog.addToSuffix': '',\n 'dialog.add': '\u6DFB\u52A0',\n 'dialog.local': '\u672C\u673A',\n 'dialog.remoteDevices': '\u8FDC\u7AEF\u8BBE\u5907',\n 'dialog.addRemote': '+ \u6DFB\u52A0\u8FDC\u7AEF\u2026',\n 'dialog.addRemoteHint': '\u8BF7\u5148\u5728 Settings \u2192 \u8FDC\u7A0B\u5F00\u53D1 \u6DFB\u52A0\u5E76\u8FDE\u63A5\u8FDC\u7AEF\u4E3B\u673A\u3002',\n 'dialog.cancel': '\u53D6\u6D88',\n 'dialog.create': '\u521B\u5EFA\u5DE5\u4F5C\u533A',\n 'dialog.creating': '\u521B\u5EFA\u4E2D\u2026',\n 'dialog.createFailed': '\u65B0\u5EFA\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'dialog.removeFolder': '\u79FB\u9664\u76EE\u5F55',\n 'dialog.selectedFallback': '\u8FDC\u7AEF workspace \u5DF2\u521B\u5EFA\uFF0C\u4F46\u672C\u5730\u955C\u50CF\u8FD8\u6CA1\u6709\u540C\u6B65\u5B8C\u6210',\n 'dialog.folderTitle': '\u9009\u62E9\u6E90\u76EE\u5F55',\n 'dialog.up': '\u4E0A\u4E00\u7EA7',\n 'dialog.selectFolder': '\u9009\u62E9\u5F53\u524D\u76EE\u5F55',\n 'dialog.reading': '\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026',\n 'dialog.emptyFolder': '\u6B64\u76EE\u5F55\u6CA1\u6709\u5B50\u76EE\u5F55',\n 'dialog.readFailed': '\u8BFB\u53D6\u76EE\u5F55\u5931\u8D25',\n 'dialog.nativePickerFailed': '\u65E0\u6CD5\u6253\u5F00\u7CFB\u7EDF\u76EE\u5F55\u9009\u62E9\u5668',\n 'dialog.useFolder': '\u4F7F\u7528\u6B64\u76EE\u5F55',\n} as const\n\nexport type CopyKey = keyof typeof zh\n\nexport const en: Record<CopyKey, string> = {\n 'settings.title': 'Remote development',\n 'settings.subtitle.lead': 'Enter an SSH target. The plugin installs and starts the remote agent and opens a tunnel.',\n 'settings.subtitle.body': 'Remote workspaces and sessions sync into the normal sidebar and open like local sessions. The model key is written into the remote service environment by default; local proxying remains optional.',\n 'settings.refresh': 'Refresh',\n 'settings.addHost': 'Add host',\n 'settings.sshTarget': 'SSH target',\n 'settings.sshPlaceholder': 'user@host or ssh://user@host:22',\n 'settings.label': 'Display name (optional)',\n 'settings.labelPlaceholder': 'Optional, e.g. dev box',\n 'settings.saveConnect': 'Save and connect',\n 'settings.adding': 'Adding\u2026',\n 'settings.sshHint': 'Requires key or ssh-agent authentication. First install takes about 1\u20133 minutes.',\n 'settings.hosts': 'Hosts',\n 'settings.hostsUnit': '',\n 'settings.noHosts': 'No remote hosts yet. Add one above.',\n 'settings.refreshFailed': 'Refresh failed',\n 'settings.unavailable': 'Cannot reach the local remote-host service',\n 'settings.operationFailed': 'Operation failed',\n 'settings.sshRequired': 'Enter an SSH target, e.g. user@host or ssh://user@host:22',\n 'settings.hostSaved': 'Host saved. The plugin will install and start the remote agent; progress appears on the host card.',\n 'settings.readWorkspacesFailed': 'Could not read remote workspaces',\n 'settings.readSessionsFailed': 'Could not read remote sessions',\n 'settings.pathRequired': 'Enter an absolute remote directory, e.g. /home/user/project',\n 'settings.addWorkspaceFailed': 'Could not add the remote workspace',\n 'settings.sessionOpening': 'Remote session created; opening it in the conversation view\u2026',\n 'settings.sessionSyncing': 'Remote session created; syncing it into the sidebar\u2026',\n 'settings.createSessionFailed': 'Could not create the remote session',\n 'settings.confirmRemovePrefix': 'Remove host \"',\n 'settings.confirmRemoveSuffix': '\"? Remote files and sessions are not deleted.',\n 'settings.remotePort': 'Remote port',\n 'settings.localTunnel': 'Local tunnel',\n 'settings.workspaces': 'Workspaces',\n 'settings.token': 'Token',\n 'settings.saved': 'Saved',\n 'settings.unsaved': 'Not saved',\n 'settings.modelProxy': 'Model credentials',\n 'settings.localProxy': 'Local proxy',\n 'settings.direct': 'Remote direct',\n 'settings.remoteDefault': 'Remote default',\n 'settings.collapseWorkspaces': 'Hide workspaces',\n 'settings.expandWorkspaces': 'Workspaces',\n 'settings.rediscover': 'Rediscover',\n 'settings.disconnect': 'Disconnect',\n 'settings.connect': 'Connect',\n 'settings.installing': 'Installing\u2026',\n 'settings.connecting': 'Connecting\u2026',\n 'settings.reprovision': 'Reinstall',\n 'settings.remove': 'Remove',\n 'settings.workspacePathPlaceholder': 'Absolute remote path, e.g. /home/user/project',\n 'settings.addWorkspace': 'Add workspace',\n 'settings.addingWorkspace': 'Adding\u2026',\n 'settings.readingWorkspaces': 'Reading workspaces\u2026',\n 'settings.noWorkspaces': 'No remote workspaces registered yet. Add a directory in the remote DSH, then refresh.',\n 'settings.sessionsUnit': 'sessions',\n 'settings.newSession': '+ New session',\n 'settings.creating': 'Creating\u2026',\n 'settings.readingSessions': 'Reading sessions\u2026',\n 'settings.noSessions': 'No sessions in this workspace. Start one with \u201C+ New session\u201D.',\n 'settings.eventsUnit': 'events',\n 'phase.idle': 'Idle',\n 'phase.discovering': 'Discovering',\n 'phase.installing': 'Installing',\n 'phase.connecting': 'Connecting',\n 'phase.connected': 'Connected',\n 'phase.disconnected': 'Disconnected',\n 'phase.error': 'Error',\n 'detail.discovering': 'Discovering the remote agent over SSH\u2026',\n 'detail.installing': 'Installing DSH and dsh-remote-agent remotely; this takes about 1\u20133 minutes\u2026',\n 'detail.connecting': 'Opening the SSH tunnel and checking the remote agent\u2026',\n 'detail.reprovisioning': 'Reinstalling the remote agent\u2026',\n 'detail.reconnecting': 'SSH tunnel closed; waiting to reconnect\u2026',\n 'detail.connectedProxy': 'Connected; model requests go through the local credential proxy',\n 'detail.connectedDirect': 'Connected; the remote host calls DeepSeek directly',\n 'detail.connectedRemote': 'Connected; the remote host uses its own credentials or has no model key',\n 'dialog.title': 'New workspace',\n 'dialog.close': 'Close',\n 'dialog.name': 'Workspace name',\n 'dialog.namePlaceholder': 'Workspace name (optional)',\n 'dialog.source': 'Source folder',\n 'dialog.addToPrefix': 'Add a folder on ',\n 'dialog.addToSuffix': '',\n 'dialog.add': 'Add',\n 'dialog.local': 'This computer',\n 'dialog.remoteDevices': 'Remote devices',\n 'dialog.addRemote': '+ Add remote\u2026',\n 'dialog.addRemoteHint': 'Add and connect a remote host in Settings \u2192 Remote development first.',\n 'dialog.cancel': 'Cancel',\n 'dialog.create': 'Create workspace',\n 'dialog.creating': 'Creating\u2026',\n 'dialog.createFailed': 'Could not create the workspace',\n 'dialog.removeFolder': 'Remove folder',\n 'dialog.selectedFallback': 'The remote workspace was created, but the local mirror has not synced yet',\n 'dialog.folderTitle': 'Choose a source folder',\n 'dialog.up': 'Parent folder',\n 'dialog.selectFolder': 'Select current folder',\n 'dialog.reading': 'Reading folder\u2026',\n 'dialog.emptyFolder': 'No subfolders here',\n 'dialog.readFailed': 'Could not read the folder',\n 'dialog.nativePickerFailed': 'Could not open the system folder picker',\n 'dialog.useFolder': 'Use folder',\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n dshRemote: CopyKey\n }\n}\n\nexport type Translate = (key: CopyKey) => string\n\nexport interface LocaleFace {\n register(namespace: string, dictionaries: { zh: Record<string, string>; en: Record<string, string> }): () => void\n bind(namespace: string): Translate\n}\n", "/**\n * Codex-style unified workspace creation dialog for the sidebar + button.\n *\n * One modal owns the destination (local or a connected remote host), the\n * optional workspace name, and the source folder browser. It is intentionally\n * not a separate local/remote menu.\n *\n * @module dsh-remote-local/client/workspace-add\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostView } from '../hosts/types.ts'\nimport type { HostsApi } from './api.ts'\nimport type { Translate } from './locale.ts'\n\nconst ADD_LABELS = new Set(['Add workspace', '\u6DFB\u52A0\u5DE5\u4F5C\u533A'])\nconst NATIVE_CLICK_FLAG = 'dshrNativeClick'\nconst DIALOG_CLASS = 'dshr-workspace-add-dialog'\nlet closeDialog: (() => void) | undefined\nlet closeBrowser: (() => void) | undefined\nlet closeMenu: (() => void) | undefined\n\ninterface WorkspaceFlowContext {\n readonly ctx: Context\n readonly api: HostsApi\n readonly t: Translate\n}\n\ntype Destination = { readonly kind: 'local' } | { readonly kind: 'remote'; readonly host: HostView }\n\ninterface HostDirectoryPicker {\n pick(): Promise<\n | { readonly ok: true; readonly value: string | null }\n | { readonly ok: false; readonly error: { readonly message: string } }\n >\n}\n\ninterface DesktopDirectoryPicker {\n pick(): Promise<string | null>\n}\n\nasync function pickLocalDirectory(ctx: Context): Promise<string | null> {\n // DSH Desktop installs this bridge before the client mounts; the stock\n // native directory-flow plugin uses exactly the same entry point.\n const desktop = (globalThis as unknown as { __DSH_DIRECTORY_PICKER__?: DesktopDirectoryPicker }).__DSH_DIRECTORY_PICKER__\n if (desktop !== undefined) return desktop.pick()\n const uiWorkspace = (ctx as unknown as { uiWorkspace?: { pickDirectory?: () => Promise<string | null> } }).uiWorkspace\n if (uiWorkspace?.pickDirectory !== undefined) return uiWorkspace.pickDirectory()\n const remote = (ctx as unknown as { remote?: { directoryPicker?: HostDirectoryPicker } }).remote\n if (remote?.directoryPicker === undefined) throw new Error('directory picker is unavailable')\n const result = await remote.directoryPicker.pick()\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\n\ninterface DirectoryListing {\n readonly path: string\n readonly parent: string\n readonly entries: readonly { readonly name: string; readonly path: string }[]\n readonly truncated: boolean\n}\n\n/** Install the sidebar add-workspace dialog once per client mount. */\nexport function installWorkspaceAddMenu(ctx: Context, api: HostsApi, t: Translate): void {\n if (typeof document === 'undefined') return\n ensureStyles()\n\n ctx.effect(() => {\n const onClick = (event: MouseEvent): void => {\n const target = event.target\n if (!(target instanceof Element)) return\n const button = target.closest<HTMLButtonElement>('button[aria-label]')\n if (button === null) return\n const label = button.getAttribute('aria-label') ?? ''\n if (!ADD_LABELS.has(label)) return\n if (button.closest('[class*=\"headerActions\"]') === null) return\n if (button.dataset[NATIVE_CLICK_FLAG] === '1') {\n delete button.dataset[NATIVE_CLICK_FLAG]\n return\n }\n event.preventDefault()\n event.stopImmediatePropagation()\n void openCreateDialog({ ctx, api, t }, button)\n }\n document.addEventListener('click', onClick, true)\n return () => {\n document.removeEventListener('click', onClick, true)\n closeTransient()\n }\n }, 'dsh-remote: workspace add dialog')\n}\n\nasync function openCreateDialog(flow: WorkspaceFlowContext, anchor: HTMLButtonElement): Promise<void> {\n closeTransient()\n const overlay = document.createElement('div')\n overlay.className = `${DIALOG_CLASS}-overlay`\n overlay.setAttribute('role', 'presentation')\n const dialog = document.createElement('div')\n dialog.className = DIALOG_CLASS\n dialog.setAttribute('role', 'dialog')\n dialog.setAttribute('aria-modal', 'true')\n dialog.setAttribute('aria-label', flow.t('dialog.title'))\n\n const title = document.createElement('h2')\n title.className = `${DIALOG_CLASS}-title`\n title.textContent = flow.t('dialog.title')\n const header = document.createElement('div')\n header.className = `${DIALOG_CLASS}-header`\n const closeButton = document.createElement('button')\n closeButton.type = 'button'\n closeButton.className = `${DIALOG_CLASS}-close`\n closeButton.setAttribute('aria-label', flow.t('dialog.close'))\n closeButton.textContent = '\u00D7'\n header.append(title, closeButton)\n\n const nameLabel = document.createElement('label')\n nameLabel.className = `${DIALOG_CLASS}-field`\n const nameText = document.createElement('span')\n nameText.textContent = flow.t('dialog.name')\n const nameRow = document.createElement('div')\n nameRow.className = `${DIALOG_CLASS}-input-row`\n const nameIcon = document.createElement('span')\n nameIcon.className = `${DIALOG_CLASS}-input-icon`\n setFolderIcon(nameIcon)\n const nameInput = document.createElement('input')\n nameInput.className = `${DIALOG_CLASS}-input`\n nameInput.placeholder = flow.t('dialog.namePlaceholder')\n nameInput.autocomplete = 'off'\n nameRow.append(nameIcon, nameInput)\n nameLabel.append(nameText, nameRow)\n\n const sourceTitle = document.createElement('h3')\n sourceTitle.className = `${DIALOG_CLASS}-section-title`\n sourceTitle.textContent = flow.t('dialog.source')\n const sourceHeader = document.createElement('div')\n sourceHeader.className = `${DIALOG_CLASS}-source-header`\n const hostIndicator = document.createElement('span')\n hostIndicator.className = `${DIALOG_CLASS}-host-indicator`\n const hostIcon = document.createElement('span')\n hostIcon.className = `${DIALOG_CLASS}-host-icon`\n setComputerIcon(hostIcon)\n const hostLabelText = document.createElement('span')\n hostIndicator.append(hostIcon, hostLabelText)\n sourceHeader.append(sourceTitle, hostIndicator)\n const sourceBox = document.createElement('div')\n sourceBox.className = `${DIALOG_CLASS}-source-box`\n const sourceCenter = document.createElement('div')\n sourceCenter.className = `${DIALOG_CLASS}-source-center`\n const sourceTrigger = document.createElement('button')\n sourceTrigger.type = 'button'\n sourceTrigger.className = `${DIALOG_CLASS}-source-trigger`\n const sourceTriggerText = document.createElement('span')\n const sourceChevron = document.createElement('span')\n sourceChevron.className = `${DIALOG_CLASS}-chevron`\n sourceChevron.textContent = '\u2304'\n sourceTrigger.append(sourceTriggerText, sourceChevron)\n const addFolder = document.createElement('button')\n addFolder.type = 'button'\n addFolder.className = `${DIALOG_CLASS}-add-folder`\n const addFolderIcon = document.createElement('span')\n addFolderIcon.className = `${DIALOG_CLASS}-add-folder-icon`\n const addFolderText = document.createElement('span')\n addFolderText.textContent = flow.t('dialog.add')\n addFolder.append(addFolderIcon, addFolderText)\n sourceCenter.append(sourceTrigger, addFolder)\n const selectedFolder = document.createElement('div')\n selectedFolder.className = `${DIALOG_CLASS}-selected-folder`\n const selectedIcon = document.createElement('span')\n selectedIcon.textContent = '\uD83D\uDCC1'\n const selectedName = document.createElement('span')\n selectedName.className = `${DIALOG_CLASS}-selected-name`\n const removeFolder = document.createElement('button')\n removeFolder.type = 'button'\n removeFolder.className = `${DIALOG_CLASS}-remove-folder`\n removeFolder.setAttribute('aria-label', flow.t('dialog.removeFolder'))\n removeFolder.textContent = '\u00D7'\n selectedFolder.append(selectedIcon, selectedName, removeFolder)\n sourceBox.append(sourceCenter, selectedFolder)\n\n const error = document.createElement('p')\n error.className = `${DIALOG_CLASS}-error`\n error.setAttribute('role', 'alert')\n\n const actions = document.createElement('div')\n actions.className = `${DIALOG_CLASS}-actions`\n const cancel = document.createElement('button')\n cancel.type = 'button'\n cancel.className = `${DIALOG_CLASS}-button`\n cancel.textContent = flow.t('dialog.cancel')\n const create = document.createElement('button')\n create.type = 'button'\n create.className = `${DIALOG_CLASS}-button ${DIALOG_CLASS}-primary`\n create.textContent = flow.t('dialog.create')\n actions.append(cancel, create)\n\n dialog.append(header, nameLabel, sourceHeader, sourceBox, error, actions)\n overlay.append(dialog)\n document.body.append(overlay)\n\n let hosts: HostView[] = []\n let destination: Destination = { kind: 'local' }\n let selectedPath = ''\n const update = (): void => {\n const label = destination.kind === 'local' ? flow.t('dialog.local') : destination.host.label\n sourceTriggerText.textContent = `${flow.t('dialog.addToPrefix')}${label}${flow.t('dialog.addToSuffix')}`\n hostLabelText.textContent = label\n addFolderIcon.textContent = '+ '\n const hasFolder = selectedPath !== ''\n sourceCenter.style.display = hasFolder ? 'none' : 'flex'\n selectedFolder.style.display = hasFolder ? 'flex' : 'none'\n sourceBox.classList.toggle(`${DIALOG_CLASS}-source-box-selected`, hasFolder)\n selectedName.textContent = hasFolder ? folderName(selectedPath) : ''\n selectedFolder.title = hasFolder ? selectedPath : ''\n create.disabled = !hasFolder\n }\n const close = (): void => {\n closeBrowser?.()\n closeMenu?.()\n overlay.remove()\n window.removeEventListener('keydown', keyHandler, true)\n overlay.removeEventListener('pointerdown', outside, true)\n if (closeDialog === close) closeDialog = undefined\n }\n closeDialog = close\n const keyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n const outside = (event: Event): void => {\n if (event.target === overlay) close()\n }\n window.addEventListener('keydown', keyHandler, true)\n overlay.addEventListener('pointerdown', outside, true)\n cancel.addEventListener('click', close)\n closeButton.addEventListener('click', close)\n\n sourceTrigger.addEventListener('click', () => {\n openDestinationMenu(sourceTrigger, hosts, destination, flow.t, next => {\n destination = next\n selectedPath = ''\n update()\n })\n })\n addFolder.addEventListener('click', () => {\n if (destination.kind === 'local') {\n addFolder.disabled = true\n void pickLocalDirectory(flow.ctx)\n .then(path => {\n if (path !== null) {\n selectedPath = path\n update()\n }\n })\n .catch(failure => { error.textContent = messageOf(failure, flow.t('dialog.nativePickerFailed')) })\n .finally(() => { addFolder.disabled = false })\n return\n }\n const initial = destination.host.hostInfo?.home ?? '~'\n openDirectoryBrowser(flow, destination, initial, path => {\n selectedPath = path\n update()\n })\n })\n removeFolder.addEventListener('click', () => {\n selectedPath = ''\n update()\n })\n nameInput.addEventListener('input', update)\n\n try {\n const state = await flow.api.state()\n hosts = state.hosts.filter(host => host.phase === 'connected')\n } catch {\n hosts = []\n }\n update()\n create.addEventListener('click', () => {\n if (selectedPath === '') return\n const title = nameInput.value.trim() || folderName(selectedPath)\n create.disabled = true\n cancel.disabled = true\n create.textContent = flow.t('dialog.creating')\n void (async () => {\n try {\n if (destination.kind === 'local') {\n const local = await flow.api.createLocalWorkspace({ path: selectedPath, title })\n close()\n flow.ctx.uiWorkspace.startSession(local.workspaceId as never)\n return\n }\n const workspace = await flow.api.createWorkspace(destination.host.id, {\n path: selectedPath,\n title,\n })\n const session = await flow.api.createSession(destination.host.id, {\n workspaceId: workspace.workspaceId,\n })\n if (session.localSessionId !== undefined) {\n close()\n flow.api.openSession(session.localSessionId)\n return\n }\n const localId = await waitForLocalWorkspace(flow.api, destination.host.id, workspace.workspaceId)\n if (localId === undefined) throw new Error(flow.t('dialog.selectedFallback'))\n close()\n flow.ctx.uiWorkspace.startSession(localId as never)\n } catch (failure) {\n error.textContent = messageOf(failure, flow.t('dialog.createFailed'))\n create.disabled = false\n cancel.disabled = false\n create.textContent = flow.t('dialog.create')\n }\n })()\n })\n\n nameInput.focus()\n}\n\nfunction openDestinationMenu(\n anchor: HTMLElement,\n hosts: readonly HostView[],\n current: Destination,\n t: Translate,\n onPick: (destination: Destination) => void,\n): void {\n closeMenu?.()\n const menu = document.createElement('div')\n menu.className = `${DIALOG_CLASS}-destination-menu`\n const add = (label: string, destination: Destination): void => {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = `${DIALOG_CLASS}-destination-item`\n const text = document.createElement('span')\n text.textContent = label\n const check = document.createElement('span')\n check.className = `${DIALOG_CLASS}-destination-check`\n const selected = current.kind === destination.kind\n && (destination.kind === 'local' || (current.kind === 'remote' && current.host.id === destination.host.id))\n check.textContent = selected ? '\u2713' : ''\n item.append(text, check)\n item.addEventListener('click', () => {\n onPick(destination)\n close()\n })\n menu.append(item)\n }\n add(t('dialog.local'), { kind: 'local' })\n if (hosts.length > 0) {\n const section = document.createElement('div')\n section.className = `${DIALOG_CLASS}-destination-section`\n section.textContent = t('dialog.remoteDevices')\n menu.append(section)\n for (const host of hosts) add(host.label, { kind: 'remote', host })\n }\n const addRemote = document.createElement('button')\n addRemote.type = 'button'\n addRemote.className = `${DIALOG_CLASS}-destination-item ${DIALOG_CLASS}-destination-add`\n addRemote.textContent = t('dialog.addRemote')\n addRemote.addEventListener('click', () => {\n close()\n const error = document.querySelector(`.${DIALOG_CLASS}-error`)\n if (error !== null) error.textContent = t('dialog.addRemoteHint')\n })\n menu.append(addRemote)\n document.body.append(menu)\n const rect = anchor.getBoundingClientRect()\n menu.style.left = `${Math.min(rect.left, window.innerWidth - 260)}px`\n menu.style.top = `${rect.bottom + 6}px`\n const close = (): void => {\n menu.remove()\n window.removeEventListener('pointerdown', dismiss, true)\n if (closeMenu === close) closeMenu = undefined\n }\n closeMenu = close\n const dismiss = (event: Event): void => {\n if (event.target instanceof Node && menu.contains(event.target)) return\n close()\n }\n window.addEventListener('pointerdown', dismiss, true)\n}\n\nasync function openDirectoryBrowser(\n flow: WorkspaceFlowContext,\n destination: Destination,\n initialPath: string,\n onPick: (path: string) => void,\n): Promise<void> {\n closeBrowser?.()\n const overlay = document.createElement('div')\n overlay.className = `${DIALOG_CLASS}-browser-overlay`\n overlay.setAttribute('role', 'presentation')\n const dialog = document.createElement('div')\n dialog.className = `${DIALOG_CLASS} ${DIALOG_CLASS}-browser`\n dialog.setAttribute('role', 'dialog')\n dialog.setAttribute('aria-modal', 'true')\n dialog.setAttribute('aria-label', flow.t('dialog.folderTitle'))\n\n const title = document.createElement('h2')\n title.className = `${DIALOG_CLASS}-browser-title`\n title.textContent = flow.t('dialog.folderTitle')\n const browserHeader = document.createElement('div')\n browserHeader.className = `${DIALOG_CLASS}-browser-header`\n const browserClose = document.createElement('button')\n browserClose.type = 'button'\n browserClose.className = `${DIALOG_CLASS}-close`\n browserClose.setAttribute('aria-label', flow.t('dialog.close'))\n browserClose.textContent = '\u00D7'\n browserHeader.append(title, browserClose)\n const pathRow = document.createElement('div')\n pathRow.className = `${DIALOG_CLASS}-browser-path-row`\n const up = document.createElement('button')\n up.type = 'button'\n up.className = `${DIALOG_CLASS}-up`\n up.textContent = '\u2191'\n up.setAttribute('aria-label', flow.t('dialog.up'))\n const pathInput = document.createElement('input')\n pathInput.className = `${DIALOG_CLASS}-browser-path`\n pathInput.value = initialPath\n pathInput.autocomplete = 'off'\n pathRow.append(up, pathInput)\n const status = document.createElement('p')\n status.className = `${DIALOG_CLASS}-browser-status`\n status.textContent = flow.t('dialog.selectFolder')\n const list = document.createElement('div')\n list.className = `${DIALOG_CLASS}-browser-list`\n const actions = document.createElement('div')\n actions.className = `${DIALOG_CLASS}-actions`\n const cancel = document.createElement('button')\n cancel.type = 'button'\n cancel.className = `${DIALOG_CLASS}-button`\n cancel.textContent = flow.t('dialog.cancel')\n const use = document.createElement('button')\n use.type = 'button'\n use.className = `${DIALOG_CLASS}-button ${DIALOG_CLASS}-primary`\n use.textContent = flow.t('dialog.useFolder')\n actions.append(cancel, use)\n dialog.append(browserHeader, pathRow, status, list, actions)\n overlay.append(dialog)\n document.body.append(overlay)\n\n let current = initialPath\n let parent = initialPath\n let currentEntries: readonly { readonly name: string; readonly path: string }[] = []\n let requestSeq = 0\n let filterTimer: number | undefined\n\n const fetchListing = async (path: string): Promise<DirectoryListing | undefined> => {\n const sequence = ++requestSeq\n status.textContent = flow.t('dialog.reading')\n status.classList.remove(`${DIALOG_CLASS}-error-text`)\n try {\n const listing: DirectoryListing = destination.kind === 'local'\n ? await flow.api.listLocalDirectories(path)\n : await flow.api.listDirectories(destination.host.id, path)\n if (sequence !== requestSeq) return undefined\n return listing\n } catch (failure) {\n if (sequence === requestSeq) {\n status.textContent = messageOf(failure, flow.t('dialog.readFailed'))\n status.classList.add(`${DIALOG_CLASS}-error-text`)\n }\n return undefined\n }\n }\n\n const render = (entries: readonly { readonly name: string; readonly path: string }[]): void => {\n list.replaceChildren()\n if (entries.length === 0) {\n const empty = document.createElement('p')\n empty.className = `${DIALOG_CLASS}-browser-empty`\n empty.textContent = flow.t('dialog.emptyFolder')\n list.append(empty)\n return\n }\n for (const entry of entries) {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = `${DIALOG_CLASS}-browser-entry`\n const icon = document.createElement('span')\n icon.textContent = '\uD83D\uDCC1'\n const label = document.createElement('span')\n label.textContent = entry.name\n item.append(icon, label)\n item.addEventListener('click', () => { void load(entry.path) })\n list.append(item)\n }\n }\n\n const load = async (path: string): Promise<void> => {\n const listing = await fetchListing(path)\n if (listing === undefined) return\n current = listing.path\n parent = listing.parent\n currentEntries = listing.entries\n pathInput.value = listing.path\n status.textContent = flow.t('dialog.selectFolder')\n render(listing.entries)\n }\n\n const applyInputFilter = async (raw: string): Promise<void> => {\n if (raw === '') return\n const slash = raw.lastIndexOf('/')\n const dir = slash < 0 ? current : (raw.slice(0, slash + 1) || '/')\n const query = slash < 0 ? raw : raw.slice(slash + 1)\n if (query === '') {\n // A trailing slash is an explicit directory navigation.\n await load(raw)\n return\n }\n const normalizedCurrent = current.replace(/\\/+$/u, '') || '/'\n const normalizedDir = dir.replace(/\\/+$/u, '') || '/'\n if (normalizedDir === normalizedCurrent) {\n const needle = query.toLocaleLowerCase()\n render(currentEntries.filter(entry => entry.name.toLocaleLowerCase().startsWith(needle)))\n status.textContent = flow.t('dialog.selectFolder')\n return\n }\n const listing = await fetchListing(dir)\n if (listing === undefined) return\n current = listing.path\n parent = listing.parent\n currentEntries = listing.entries\n const needle = query.toLocaleLowerCase()\n render(listing.entries.filter(entry => entry.name.toLocaleLowerCase().startsWith(needle)))\n status.textContent = flow.t('dialog.selectFolder')\n }\n\n const close = (): void => {\n overlay.remove()\n window.removeEventListener('keydown', keyHandler, true)\n overlay.removeEventListener('pointerdown', outside, true)\n if (closeBrowser === close) closeBrowser = undefined\n }\n closeBrowser = close\n const keyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n const outside = (event: Event): void => {\n if (event.target === overlay) close()\n }\n window.addEventListener('keydown', keyHandler, true)\n overlay.addEventListener('pointerdown', outside, true)\n cancel.addEventListener('click', close)\n browserClose.addEventListener('click', close)\n use.addEventListener('click', () => {\n onPick(current)\n close()\n })\n up.addEventListener('click', () => {\n if (parent !== '' && parent !== current) void load(parent)\n })\n pathInput.addEventListener('input', () => {\n if (filterTimer !== undefined) window.clearTimeout(filterTimer)\n const value = pathInput.value\n filterTimer = window.setTimeout(() => { void applyInputFilter(value) }, 140)\n })\n pathInput.addEventListener('keydown', event => {\n if (event.key !== 'Enter') return\n event.preventDefault()\n const value = pathInput.value.trim()\n if (value !== '') void applyInputFilter(value)\n })\n void load(initialPath)\n}\n\nasync function waitForLocalWorkspace(api: HostsApi, hostId: string, workspaceId: string): Promise<string | undefined> {\n for (let attempt = 0; attempt < 40; attempt += 1) {\n const result = await api.workspaces(hostId)\n const workspace = result.workspaces.find(item => item.workspaceId === workspaceId)\n if (workspace?.localWorkspaceId !== undefined) return workspace.localWorkspaceId\n await new Promise(resolve => setTimeout(resolve, 500))\n }\n return undefined\n}\n\nfunction closeTransient(): void {\n closeDialog?.()\n closeBrowser?.()\n closeMenu?.()\n}\n\nfunction messageOf(failure: unknown, fallback: string): string {\n if (failure instanceof Error && failure.message !== '') return failure.message\n return fallback\n}\n\nfunction setFolderIcon(icon: HTMLElement): void {\n icon.innerHTML = '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M3.5 7.5A2.5 2.5 0 0 1 6 5h4l2 2h6a2.5 2.5 0 0 1 2.5 2.5v7A2.5 2.5 0 0 1 18 19H6a2.5 2.5 0 0 1-2.5-2.5v-9Z\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>'\n}\n\nfunction folderName(path: string): string {\n const clean = path.replace(/[\\\\/]+$/u, '')\n const parts = clean.split(/[\\\\/]+/u)\n return parts[parts.length - 1] ?? clean\n}\n\nfunction setComputerIcon(icon: HTMLElement): void {\n icon.innerHTML = '<svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5\" width=\"17\" height=\"12\" rx=\"1.7\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 20h8M12 17v3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>'\n}\n\nfunction ensureStyles(): void {\n const existing = document.getElementById('dshr-workspace-add-style')\n const style = existing instanceof HTMLStyleElement ? existing : document.createElement('style')\n style.id = 'dshr-workspace-add-style'\n style.textContent = `\n .${DIALOG_CLASS}-overlay {\n position: fixed; inset: 0; z-index: 100001; display: grid; place-items: center;\n padding: 24px; background: rgba(0,0,0,.55);\n }\n .${DIALOG_CLASS} {\n width: min(632px, calc(100vw - 48px));\n box-sizing: border-box; padding: 26px; display: flex; flex-direction: column;\n border-radius: 14px; border: 1px solid rgba(255,255,255,.1);\n background: #242424; color: #f2f2f2; box-shadow: 0 28px 80px rgba(0,0,0,.65);\n font: 14px/1.45 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n .${DIALOG_CLASS}-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }\n .${DIALOG_CLASS}-title { margin: 0; font-size: 28px; line-height: 1.1; font-weight: 700; letter-spacing: -.02em; }\n .${DIALOG_CLASS}-close { border: 0; background: transparent; color: rgba(255,255,255,.75); font-size: 30px; line-height: 1; cursor: pointer; padding: 0 2px; }\n .${DIALOG_CLASS}-close:hover { color: #fff; }\n .${DIALOG_CLASS}-field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 14px; }\n .${DIALOG_CLASS}-field > span { font-size: 13px; color: rgba(255,255,255,.72); }\n .${DIALOG_CLASS}-input-row { display: flex; align-items: center; gap: 10px; border: 1px solid rgba(255,255,255,.24); border-radius: 9px; padding: 0 12px; background: transparent; }\n .${DIALOG_CLASS}-input-icon { display: inline-flex; color: rgba(255,255,255,.7); }\n .${DIALOG_CLASS}-input { width: 100%; border: 0; outline: none; background: transparent; color: inherit; font: inherit; font-size: 14px; padding: 13px 0; }\n .${DIALOG_CLASS}-source-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 4px 0 10px; }\n .${DIALOG_CLASS}-section-title { margin: 0; font-size: 17px; font-weight: 600; color: rgba(255,255,255,.92); }\n .${DIALOG_CLASS}-host-indicator { display: inline-flex; align-items: center; gap: 8px; color: rgba(255,255,255,.62); font-size: 15px; }\n .${DIALOG_CLASS}-host-icon { display: inline-flex; }\n .${DIALOG_CLASS}-source-box { position: relative; border: 1px solid rgba(255,255,255,.16); border-radius: 9px; padding: 24px 16px; min-height: 150px; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; }\n .${DIALOG_CLASS}-source-center { display: flex; flex-direction: column; align-items: center; gap: 16px; }\n .${DIALOG_CLASS}-source-trigger { display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; color: rgba(255,255,255,.75); font: inherit; font-size: 15px; cursor: pointer; }\n .${DIALOG_CLASS}-source-trigger span:first-child { color: #fff; font-weight: 600; }\n .${DIALOG_CLASS}-chevron { font-size: 18px; line-height: 1; }\n .${DIALOG_CLASS}-add-folder { display: inline-flex; align-items: center; gap: 8px; border: 0; border-radius: 8px; padding: 8px 14px; background: rgba(255,255,255,.12); color: #fff; font: inherit; font-size: 14px; cursor: pointer; }\n .${DIALOG_CLASS}-add-folder:hover { background: rgba(255,255,255,.18); }\n .${DIALOG_CLASS}-add-folder-icon { font-size: 18px; line-height: 1; }\n .${DIALOG_CLASS}-source-box-selected { padding: 0; min-height: 0; flex: none; justify-content: flex-start; }\n .${DIALOG_CLASS}-selected-folder { display: none; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; padding: 12px 14px; color: rgba(255,255,255,.92); font-size: 16px; }\n .${DIALOG_CLASS}-selected-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .${DIALOG_CLASS}-remove-folder { flex: none; border: 0; background: transparent; color: rgba(255,255,255,.6); font-size: 24px; line-height: 1; cursor: pointer; padding: 0 2px; }\n .${DIALOG_CLASS}-remove-folder:hover { color: #fff; }\n .${DIALOG_CLASS}-error { margin: 6px 0 0; color: #ff8b8b; font-size: 12px; white-space: pre-wrap; }\n .${DIALOG_CLASS}-error:empty { display: none; }\n .${DIALOG_CLASS}-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }\n .${DIALOG_CLASS}-button { padding: 11px 18px; border-radius: 8px; border: 1px solid rgba(255,255,255,.18); background: transparent; color: #eee; cursor: pointer; font: inherit; font-size: 15px; }\n .${DIALOG_CLASS}-button:disabled { opacity: .45; cursor: default; }\n .${DIALOG_CLASS}-primary { border-color: #bdbdbd; background: #bdbdbd; color: #1a1a1a; font-weight: 600; }\n .${DIALOG_CLASS}-primary:hover:not(:disabled) { background: #d0d0d0; }\n .${DIALOG_CLASS}-destination-menu {\n position: fixed; z-index: 100010; min-width: 250px; padding: 6px;\n border-radius: 12px; border: 1px solid rgba(255,255,255,.14); background: #2a2a2a; color: #eee;\n box-shadow: 0 18px 48px rgba(0,0,0,.55); font: 14px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n .${DIALOG_CLASS}-destination-item { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 10px 12px; border: 0; border-radius: 8px; background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left; }\n .${DIALOG_CLASS}-destination-item:hover { background: rgba(255,255,255,.09); }\n .${DIALOG_CLASS}-destination-section { padding: 14px 12px 6px; color: rgba(255,255,255,.5); font-size: 13px; }\n .${DIALOG_CLASS}-destination-check { color: #bdbdbd; }\n .${DIALOG_CLASS}-destination-add { color: rgba(255,255,255,.75); }\n .${DIALOG_CLASS}-browser-overlay { position: fixed; inset: 0; z-index: 100002; display: grid; place-items: center; background: rgba(0,0,0,.6); padding: 24px; }\n .${DIALOG_CLASS}-browser { width: 640px; max-height: 86vh; display: flex; flex-direction: column; padding: 26px; }\n .${DIALOG_CLASS}-browser-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; }\n .${DIALOG_CLASS}-browser-title { margin: 0; font-size: 30px; line-height: 1.15; font-weight: 700; }\n .${DIALOG_CLASS}-browser-path-row { display: flex; align-items: center; gap: 8px; }\n .${DIALOG_CLASS}-up { flex: none; width: 34px; height: 34px; border: 0; background: transparent; color: rgba(255,255,255,.65); font-size: 22px; cursor: pointer; }\n .${DIALOG_CLASS}-browser-path { flex: 1; border: 1px solid rgba(255,255,255,.22); border-radius: 9px; background: transparent; color: #fff; font: inherit; font-size: 16px; padding: 12px 14px; outline: none; }\n .${DIALOG_CLASS}-browser-path:focus { border-color: #4f8cff; }\n .${DIALOG_CLASS}-browser-status { margin: 14px 0 8px; color: rgba(255,255,255,.55); font-size: 13px; }\n .${DIALOG_CLASS}-browser-list { flex: 1; min-height: 220px; max-height: 420px; overflow: auto; border: 1px solid rgba(255,255,255,.16); border-radius: 12px; padding: 6px; }\n .${DIALOG_CLASS}-browser-entry { display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; padding: 11px 12px; border: 0; border-radius: 8px; background: transparent; color: #fff; font: inherit; font-size: 16px; text-align: left; cursor: pointer; }\n .${DIALOG_CLASS}-browser-entry:hover { background: rgba(255,255,255,.08); }\n .${DIALOG_CLASS}-browser-empty { margin: 16px 12px; color: rgba(255,255,255,.5); font-size: 13px; }\n `\n if (existing === null) document.head.append(style)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,IAAM,gBAAgB;;;ACCtB,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAC9B;AAAA,EACA;AAAA,EAET,YAAY,MAAc,SAAiB,UAAmC,CAAC,GAAG;AAChF,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EACjB;AACF;AA+BO,SAAS,eAAe,KAAwB;AACrD,SAAO;AAAA,IACL,OAAO,MAAM,KAAK,SAAS,MAAS;AAAA,IACpC,gBAAgB,eAAa,KAAK,mBAAmB,EAAE,UAAU,CAAC;AAAA,IAClE,SAAS,WAAS,KAAK,YAAY,KAAK;AAAA,IACxC,YAAY,OAAM,WAAU;AAAE,YAAM,KAAK,eAAe,EAAE,OAAO,CAAC;AAAA,IAAE;AAAA,IACpE,SAAS,YAAU,KAAK,gBAAgB,EAAE,OAAO,CAAC;AAAA,IAClD,YAAY,YAAU,KAAK,mBAAmB,EAAE,OAAO,CAAC;AAAA,IACxD,SAAS,YAAU,KAAK,gBAAgB,EAAE,OAAO,CAAC;AAAA,IAClD,WAAW,YAAU,KAAK,kBAAkB,EAAE,OAAO,CAAC;AAAA,IACtD,YAAY,YAAU,KAAK,mBAAmB,EAAE,OAAO,CAAC;AAAA,IACxD,iBAAiB,CAAC,QAAQ,SAAS,KAAK,uBAAuB,EAAE,QAAQ,GAAG,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAAA,IAChH,sBAAsB,UAAQ,KAAK,wBAAwB,EAAE,GAAG,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAAA,IACpG,sBAAsB,WAAS,KAAK,0BAA0B,KAAK;AAAA,IACnE,iBAAiB,CAAC,QAAQ,UAAU,KAAK,yBAAyB,EAAE,QAAQ,GAAG,MAAM,CAAC;AAAA,IACtF,UAAU,CAAC,QAAQ,gBAAgB,KAAK,iBAAiB;AAAA,MACvD;AAAA,MACA,GAAG,gBAAgB,SAAY,CAAC,IAAI,EAAE,YAAY;AAAA,IACpD,CAAC;AAAA,IACD,QAAQ,YAAU,KAAK,gBAAgB,EAAE,OAAO,CAAC;AAAA,IACjD,UAAU,CAAC,QAAQ,WAAW,UAAU,KAAK,gBAAgB;AAAA,MAC3D;AAAA,MACA;AAAA,MACA,GAAG,UAAU,SAAY,CAAC,IAAI,EAAE,MAAM;AAAA,IACxC,CAAC;AAAA,IACD,eAAe,CAAC,QAAQ,UAAU,KAAK,uBAAuB,EAAE,QAAQ,GAAG,MAAM,CAAC;AAAA,IAClF,aAAa,eAAa;AAAE,UAAI,YAAY,YAAY,SAAkB;AAAA,IAAE;AAAA,IAC5E,UAAU,CAAC,WAAW,SAAS,KAAK,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAAA,IAC1E,UAAU,CAAC,WAAW,MAAM,QAAQ,UAAU,KAAK,mBAAmB;AAAA,MACpE;AAAA,MACA;AAAA,MACA,GAAG,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,MACxC,GAAG,UAAU,SAAY,CAAC,IAAI,EAAE,MAAM;AAAA,IACxC,CAAC;AAAA,IACD,UAAU,CAAC,WAAW,SAAS,KAAK,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAAA,IAC1E,WAAW,CAAC,WAAW,MAAM,QAAQ,WAAW,KAAK,oBAAoB;AAAA,MACvE;AAAA,MACA;AAAA,MACA,GAAG,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,MACxC,GAAG,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,IAC1C,CAAC;AAAA,IACD,QAAQ,CAAC,QAAQ,WAAW,MAAM,MAAM,cAAc,KAAK,uBAAuB;AAAA,MAChF;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK;AAAA,MACpC,GAAG,cAAc,SAAY,CAAC,IAAI;AAAA,IACpC,CAAC;AAAA,IACD,QAAQ,CAAC,QAAQ,cAAc,KAAK,uBAAuB,EAAE,QAAQ,UAAU,CAAC;AAAA,EAClF;AACF;AAEA,eAAe,KACb,UACA,SAC2B;AAC3B,QAAM,WAAW,MAAM,MAAM,eAAe;AAAA,IAC1C,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,YAAY,SAAY,EAAE,SAAS,IAAI,EAAE,UAAU,QAAQ,CAAC;AAAA,EACnF,CAAC;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,cAAc,mBAAmB,0BAA0B,SAAS,MAAM,EAAE;AAAA,EACxF;AACA,QAAM,SAAS,MAAM,SAAS,KAAK;AAGnC,MAAI,CAAC,OAAO,IAAI;AACd,UAAM,IAAI,cAAc,OAAO,MAAM,MAAM,OAAO,MAAM,SAAS,OAAO,MAAM,WAAW,CAAC,CAAC;AAAA,EAC7F;AACA,SAAO,OAAO;AAChB;;;AC9HA,mBAA6C;;;ACV7C;;;ADwOU;AAhNV,SAAS,WAAW,GAAc,OAAkC;AAClE,SAAO,EAAE,SAAS,KAAK,EAAa;AACtC;AAEA,IAAM,cAAuC;AAAA,EAC3C,qEAAwB;AAAA,EACxB,wHAA6C;AAAA,EAC7C,uFAA2B;AAAA,EAC3B,gEAAmB;AAAA,EACnB,oEAAkB;AAAA,EAClB,0GAAqB;AAAA,EACrB,2FAA0B;AAAA,EAC1B,oHAA0B;AAC5B;AAEA,SAAS,YAAY,GAAc,QAAwB;AACzD,QAAM,MAAM,YAAY,MAAM;AAC9B,SAAO,QAAQ,SAAY,SAAS,EAAE,GAAG;AAC3C;AAEA,IAAM,UAAU;AAGT,SAAS,uBAAuB,EAAE,KAAK,EAAE,GAAe;AAC7D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAkC,IAAI;AAChE,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAwB,IAAI;AACxD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAwB,IAAI;AACpD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAwB,IAAI;AACpE,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAwD,CAAC,CAAC;AAC9F,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAkC,CAAC,CAAC;AAC9E,QAAM,CAAC,UAAU,WAAW,QAAI,uBAA6D,CAAC,CAAC;AAC/F,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAS,EAAE;AACvD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS;AAAA,IAC/B,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,mBAAiB,aAAQ;AAEzB,QAAM,YAAQ,sBAAQ,MAAM,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;AAEvD,8BAAU,MAAM;AACd,QAAI,YAAY;AAChB,UAAM,OAAO,YAA2B;AACtC,UAAI;AACF,cAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,YAAI,CAAC,WAAW;AACd,mBAAS,IAAI;AACb,mBAAS,aAAW,YAAY,EAAE,sBAAsB,IAAI,OAAO,OAAO;AAAA,QAC5E;AAAA,MACF,SAAS,SAAS;AAChB,YAAI,CAAC,UAAW,UAAS,UAAU,SAAS,EAAE,sBAAsB,CAAC,CAAC;AAAA,MACxE;AAAA,IACF;AACA,SAAK,KAAK;AACV,UAAM,QAAQ,OAAO,YAAY,MAAM;AAAE,WAAK,KAAK;AAAA,IAAE,GAAG,OAAO;AAC/D,UAAM,UAAU,MAAY;AAAE,WAAK,KAAK;AAAA,IAAE;AAC1C,WAAO,iBAAiB,SAAS,OAAO;AACxC,WAAO,MAAM;AACX,kBAAY;AACZ,aAAO,cAAc,KAAK;AAC1B,aAAO,oBAAoB,SAAS,OAAO;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,KAAK,CAAC,CAAC;AAEX,8BAAU,MAAM;AACd,UAAM,OAAO,MAAM,KAAK,UAAQ,KAAK,OAAO,YAAY;AACxD,QAAI,MAAM,UAAU,eAAe,WAAW,KAAK,EAAE,MAAM,QAAW;AACpE,WAAK,eAAe,KAAK,EAAE;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,OAAO,cAAc,UAAU,CAAC;AAEpC,iBAAe,UAAyB;AACtC,aAAS,MAAM,IAAI,MAAM,CAAC;AAAA,EAC5B;AAEA,iBAAe,IAAI,KAAa,MAA0C;AACxE,YAAQ,GAAG;AACX,aAAS,IAAI;AACb,cAAU,IAAI;AACd,QAAI;AACF,YAAM,KAAK;AACX,YAAM,QAAQ;AAAA,IAChB,SAAS,SAAS;AAChB,eAAS,UAAU,SAAS,EAAE,0BAA0B,CAAC,CAAC;AAAA,IAC5D,UAAE;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAEA,iBAAe,QAAuB;AACpC,UAAM,YAAY,KAAK,UAAU,KAAK;AACtC,QAAI,cAAc,IAAI;AACpB,eAAS,EAAE,sBAAsB,CAAC;AAClC;AAAA,IACF;AACA,UAAM,IAAI,OAAO,YAAY;AAC3B,YAAM,IAAI,eAAe,SAAS;AAClC,YAAM,OAAO,MAAM,IAAI,QAAQ;AAAA,QAC7B;AAAA,QACA,GAAG,KAAK,MAAM,KAAK,MAAM,KAAK,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,KAAK,EAAE;AAAA,QAC9D,aAAa;AAAA,MACf,CAAC;AACD,cAAQ,EAAE,WAAW,IAAI,OAAO,GAAG,CAAC;AACpC,sBAAgB,KAAK,EAAE;AACvB,gBAAU,EAAE,oBAAoB,CAAC;AAAA,IACnC,CAAC;AAAA,EACH;AAEA,iBAAe,WAAW,MAA+B;AACvD,QAAI,iBAAiB,KAAK,IAAI;AAC5B,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,oBAAgB,KAAK,EAAE;AACvB,UAAM,eAAe,KAAK,EAAE;AAAA,EAC9B;AAEA,iBAAe,eAAe,QAA+B;AAC3D,kBAAc,cAAY,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,OAAU,EAAE;AAC9D,QAAI;AACF,YAAM,SAAS,MAAM,IAAI,WAAW,MAAM;AAC1C,oBAAc,cAAY,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,OAAO,UAAU,EAAE,EAAE;AAAA,IAC7E,SAAS,SAAS;AAChB,oBAAc,cAAY,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE;AACvD,eAAS,UAAU,SAAS,EAAE,+BAA+B,CAAC,CAAC;AAAA,IACjE;AAAA,EACF;AAEA,iBAAe,gBAAgB,QAAgB,aAAoC;AACjF,UAAM,MAAM,GAAG,MAAM,IAAI,WAAW;AACpC,UAAM,OAAO,EAAE,cAAc,GAAG,KAAK;AACrC,qBAAiB,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,KAAK,EAAE;AACzD,QAAI,CAAC,QAAQ,SAAS,GAAG,MAAM,OAAW;AAC1C,gBAAY,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,OAAU,EAAE;AACzD,QAAI;AACF,YAAM,SAAS,MAAM,IAAI,SAAS,QAAQ,WAAW;AACrD,kBAAY,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,OAAO,QAAQ,EAAE,EAAE;AAAA,IACtE,SAAS,SAAS;AAChB,kBAAY,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,EAAE,EAAE;AAClD,eAAS,UAAU,SAAS,EAAE,6BAA6B,CAAC,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,iBAAe,aAAa,MAA+B;AACzD,UAAM,OAAO,eAAe,KAAK;AACjC,QAAI,SAAS,IAAI;AACf,eAAS,EAAE,uBAAuB,CAAC;AACnC;AAAA,IACF;AACA,YAAQ,aAAa,KAAK,EAAE,EAAE;AAC9B,aAAS,IAAI;AACb,QAAI;AACF,YAAM,IAAI,gBAAgB,KAAK,IAAI,EAAE,KAAK,CAAC;AAC3C,wBAAkB,EAAE;AACpB,YAAM,eAAe,KAAK,EAAE;AAC5B,sBAAgB,KAAK,EAAE;AAAA,IACzB,SAAS,SAAS;AAChB,eAAS,UAAU,SAAS,EAAE,6BAA6B,CAAC,CAAC;AAAA,IAC/D,UAAE;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAIA,iBAAe,WAAW,MAAgB,aAAoC;AAC5E,YAAQ,UAAU,KAAK,EAAE,IAAI,WAAW,EAAE;AAC1C,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,MAAM,IAAI,cAAc,KAAK,IAAI,EAAE,YAAY,CAAC;AAChE,YAAM,MAAM,GAAG,KAAK,EAAE,IAAI,WAAW;AACrC,uBAAiB,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,KAAK,EAAE;AACzD,UAAI,QAAQ,mBAAmB,QAAW;AACxC,YAAI,YAAY,QAAQ,cAAc;AACtC,kBAAU,EAAE,yBAAyB,CAAC;AACtC,eAAO,WAAW,MAAM;AACtB,mBAAS,cAAc,IAAI,cAAc,WAAW,EAAE,KAAK,UAAU,SAAS,KAAK,CAAC,CAAC;AAAA,QACvF,GAAG,EAAE;AAAA,MACP,OAAO;AACL,kBAAU,EAAE,yBAAyB,CAAC;AAAA,MACxC;AACA,YAAM,SAAS,MAAM,IAAI,SAAS,KAAK,IAAI,WAAW;AACtD,kBAAY,cAAY,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,OAAO,QAAQ,EAAE,EAAE;AACpE,YAAM,YAAY,MAAM,IAAI,WAAW,KAAK,EAAE;AAC9C,oBAAc,cAAY,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,UAAU,UAAU,EAAE,EAAE;AAAA,IACjF,SAAS,SAAS;AAChB,eAAS,UAAU,SAAS,EAAE,8BAA8B,CAAC,CAAC;AAAA,IAChE,UAAE;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAIA,iBAAe,SAAS,MAA+B;AACrD,QAAI,CAAC,OAAO,QAAQ,GAAG,EAAE,8BAA8B,CAAC,GAAG,KAAK,KAAK,GAAG,EAAE,8BAA8B,CAAC,EAAE,EAAG;AAC9G,UAAM,IAAI,UAAU,KAAK,EAAE,IAAI,YAAY;AACzC,YAAM,IAAI,WAAW,KAAK,EAAE;AAC5B,UAAI,iBAAiB,KAAK,GAAI,iBAAgB,IAAI;AAAA,IACpD,CAAC;AAAA,EACH;AAEA,SACE,6CAAC,SAAI,WAAU,aACb;AAAA,iDAAC,YAAO,WAAU,eAChB;AAAA,mDAAC,SACC;AAAA,oDAAC,QAAI,YAAE,gBAAgB,GAAE;AAAA,QACzB,6CAAC,OAAE,WAAU,iBACV;AAAA,YAAE,wBAAwB;AAAA,UAC1B;AAAA,UACA,EAAE,wBAAwB;AAAA,WAC7B;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM;AAAE,iBAAK,QAAQ,EAAE,MAAM,aAAW,SAAS,UAAU,SAAS,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAAA,UAAE;AAAA,UAC5G,UAAU,SAAS;AAAA,UAElB,YAAE,kBAAkB;AAAA;AAAA,MACvB;AAAA,OACF;AAAA,IAEC,UAAU,QAAQ,4CAAC,OAAE,WAAU,iCAAgC,MAAK,SAAS,iBAAM;AAAA,IACnF,WAAW,QAAQ,4CAAC,OAAE,WAAU,gCAA+B,MAAK,UAAU,kBAAO;AAAA,IAEtF,6CAAC,aAAQ,WAAU,aACjB;AAAA,kDAAC,QAAI,YAAE,kBAAkB,GAAE;AAAA,MAC3B;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,UAAU,WAAS;AACjB,kBAAM,eAAe;AACrB,iBAAK,MAAM;AAAA,UACb;AAAA,UAEA;AAAA,yDAAC,WAAM,WAAU,8BACf;AAAA,0DAAC,UAAM,YAAE,oBAAoB,GAAE;AAAA,cAC/B;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,KAAK;AAAA,kBACZ,UAAU,WAAS,QAAQ,cAAY,EAAE,GAAG,SAAS,WAAW,MAAM,OAAO,MAAM,EAAE;AAAA,kBACrF,aAAa,EAAE,yBAAyB;AAAA,kBACxC,cAAa;AAAA,kBACb,YAAY;AAAA;AAAA,cACd;AAAA,eACF;AAAA,YACA,6CAAC,WAAM,WAAU,cACf;AAAA,0DAAC,UAAM,YAAE,gBAAgB,GAAE;AAAA,cAC3B;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,KAAK;AAAA,kBACZ,UAAU,WAAS,QAAQ,cAAY,EAAE,GAAG,SAAS,OAAO,MAAM,OAAO,MAAM,EAAE;AAAA,kBACjF,aAAa,EAAE,2BAA2B;AAAA;AAAA,cAC5C;AAAA,eACF;AAAA,YACA,6CAAC,SAAI,WAAU,qBACb;AAAA,0DAAC,YAAO,MAAK,UAAS,WAAU,mCAAkC,UAAU,SAAS,MAClF,mBAAS,QAAQ,EAAE,iBAAiB,IAAI,EAAE,sBAAsB,GACnE;AAAA,cACA,4CAAC,UAAK,WAAU,aAAa,YAAE,kBAAkB,GAAE;AAAA,eACrD;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IAEA,6CAAC,aAAQ,WAAU,aACjB;AAAA,mDAAC,SAAI,WAAU,sBACb;AAAA,oDAAC,QAAI,YAAE,gBAAgB,GAAE;AAAA,QACzB,6CAAC,UAAK,WAAU,aAAa;AAAA,gBAAM;AAAA,UAAO;AAAA,UAAE,EAAE,oBAAoB;AAAA,WAAE;AAAA,SACtE;AAAA,MACC,MAAM,WAAW,KAAK,4CAAC,OAAE,WAAU,cAAc,YAAE,kBAAkB,GAAE;AAAA,MACxE,4CAAC,SAAI,WAAU,cACZ,gBAAM,IAAI,UACT,6CAAC,aAAsB,WAAU,aAC/B;AAAA,qDAAC,YAAO,WAAU,kBAChB;AAAA,uDAAC,SAAI,WAAU,mBACb;AAAA,wDAAC,YAAQ,eAAK,OAAM;AAAA,YACpB,4CAAC,UAAM,eAAK,WAAU;AAAA,aACxB;AAAA,UACA,4CAAC,UAAK,WAAW,yBAAyB,KAAK,KAAK,IAAK,qBAAW,GAAG,KAAK,KAAK,GAAE;AAAA,WACrF;AAAA,QAEA,6CAAC,QAAG,WAAU,aACZ;AAAA,uDAAC,SAAI;AAAA,wDAAC,QAAI,YAAE,qBAAqB,GAAE;AAAA,YAAK,4CAAC,QAAI,eAAK,mBAAmB,UAAI;AAAA,aAAK;AAAA,UAC9E,6CAAC,SAAI;AAAA,wDAAC,QAAI,YAAE,sBAAsB,GAAE;AAAA,YAAK,4CAAC,QAAI,eAAK,aAAa,UAAI;AAAA,aAAK;AAAA,UACzE,6CAAC,SAAI;AAAA,wDAAC,QAAI,YAAE,qBAAqB,GAAE;AAAA,YAAK,4CAAC,QAAI,eAAK,kBAAkB,UAAI;AAAA,aAAK;AAAA,UAC7E,6CAAC,SAAI;AAAA,wDAAC,QAAG,mBAAK;AAAA,YAAK,4CAAC,QAAI,eAAK,UAAU,gBAAgB,UAAI;AAAA,aAAK;AAAA,UAChE,6CAAC,SAAI;AAAA,wDAAC,QAAI,YAAE,gBAAgB,GAAE;AAAA,YAAK,4CAAC,QAAI,eAAK,WAAW,EAAE,gBAAgB,IAAI,EAAE,kBAAkB,GAAE;AAAA,aAAK;AAAA,UACzG,6CAAC,SAAI;AAAA,wDAAC,QAAI,YAAE,qBAAqB,GAAE;AAAA,YAAK,4CAAC,QAAI,eAAK,aAAa,EAAE,qBAAqB,IAAI,KAAK,cAAc,EAAE,iBAAiB,IAAI,EAAE,wBAAwB,GAAE;AAAA,aAAK;AAAA,WACvK;AAAA,QAEC,KAAK,WAAW,QAAQ,4CAAC,OAAE,WAAU,eAAe,sBAAY,GAAG,KAAK,MAAM,GAAE;AAAA,QAChF,KAAK,UAAU,QAAQ,4CAAC,OAAE,WAAU,mBAAkB,MAAK,SAAS,eAAK,OAAM;AAAA,QAEhF,6CAAC,SAAI,WAAU,gBACZ;AAAA,eAAK,UAAU,cAEZ,4EACE;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS;AAAA,gBACnB,SAAS,MAAM;AAAE,uBAAK,WAAW,IAAI;AAAA,gBAAE;AAAA,gBAEtC,2BAAiB,KAAK,KAAK,EAAE,6BAA6B,IAAI,EAAE,2BAA2B;AAAA;AAAA,YAC9F;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS;AAAA,gBACnB,SAAS,MAAM;AAAE,uBAAK,IAAI,WAAW,KAAK,EAAE,IAAI,YAAY;AAAE,0BAAM,IAAI,QAAQ,KAAK,EAAE;AAAA,kBAAE,CAAC;AAAA,gBAAE;AAAA,gBAE3F,YAAE,qBAAqB;AAAA;AAAA,YAC1B;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS;AAAA,gBACnB,SAAS,MAAM;AAAE,uBAAK,IAAI,cAAc,KAAK,EAAE,IAAI,YAAY;AAAE,0BAAM,IAAI,WAAW,KAAK,EAAE;AAAA,kBAAE,CAAC;AAAA,gBAAE;AAAA,gBAEjG,YAAE,qBAAqB;AAAA;AAAA,YAC1B;AAAA,aACF,IAGA,4EACE;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS,QAAQ,KAAK,UAAU,gBAAgB,KAAK,UAAU,iBAAiB,KAAK,UAAU;AAAA,gBACzG,SAAS,MAAM;AAAE,uBAAK,IAAI,WAAW,KAAK,EAAE,IAAI,YAAY;AAAE,0BAAM,IAAI,QAAQ,KAAK,EAAE;AAAA,kBAAE,CAAC;AAAA,gBAAE;AAAA,gBAE3F,eAAK,UAAU,eAAe,EAAE,qBAAqB,IAAI,KAAK,UAAU,eAAe,EAAE,qBAAqB,IAAI,EAAE,kBAAkB;AAAA;AAAA,YACzI;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS;AAAA,gBACnB,SAAS,MAAM;AAAE,uBAAK,IAAI,WAAW,KAAK,EAAE,IAAI,YAAY;AAAE,0BAAM,IAAI,QAAQ,KAAK,EAAE;AAAA,kBAAE,CAAC;AAAA,gBAAE;AAAA,gBAE3F,YAAE,qBAAqB;AAAA;AAAA,YAC1B;AAAA,aACF;AAAA,UAEJ;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,UAAU,SAAS,QAAQ,KAAK,UAAU;AAAA,cAC1C,SAAS,MAAM;AAAE,qBAAK,IAAI,aAAa,KAAK,EAAE,IAAI,YAAY;AAAE,wBAAM,IAAI,UAAU,KAAK,EAAE;AAAA,gBAAE,CAAC;AAAA,cAAE;AAAA,cAE/F,YAAE,sBAAsB;AAAA;AAAA,UAC3B;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,UAAU,SAAS;AAAA,cACnB,SAAS,MAAM;AAAE,qBAAK,SAAS,IAAI;AAAA,cAAE;AAAA,cAEpC,YAAE,iBAAiB;AAAA;AAAA,UACtB;AAAA,WACF;AAAA,QAEC,iBAAiB,KAAK,MACrB,6CAAC,SAAI,WAAU,mBACb;AAAA,uDAAC,SAAI,WAAU,sBACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU,WAAS,kBAAkB,MAAM,OAAO,KAAK;AAAA,gBACvD,aAAa,EAAE,mCAAmC;AAAA,gBAClD,YAAY;AAAA,gBACZ,WAAW,WAAS;AAClB,sBAAI,MAAM,QAAQ,SAAS;AACzB,0BAAM,eAAe;AACrB,yBAAK,aAAa,IAAI;AAAA,kBACxB;AAAA,gBACF;AAAA;AAAA,YACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,UAAU,SAAS;AAAA,gBACnB,SAAS,MAAM;AAAE,uBAAK,aAAa,IAAI;AAAA,gBAAE;AAAA,gBAExC,mBAAS,aAAa,KAAK,EAAE,KAAK,EAAE,0BAA0B,IAAI,EAAE,uBAAuB;AAAA;AAAA,YAC9F;AAAA,aACF;AAAA,UACC,WAAW,KAAK,EAAE,MAAM,SACrB,4CAAC,OAAE,WAAU,cAAc,YAAE,4BAA4B,GAAE,IAC3D,WAAW,KAAK,EAAE,GAAG,WAAW,IAC9B,4CAAC,OAAE,WAAU,cAAc,YAAE,uBAAuB,GAAE,IACtD,WAAW,KAAK,EAAE,GAAG,IAAI,eAAa;AACtC,kBAAM,eAAe,GAAG,KAAK,EAAE,IAAI,UAAU,WAAW;AACxD,kBAAM,OAAO,cAAc,YAAY,KAAK;AAC5C,mBACE,6CAAC,SAAgC,WAAU,kBACzC;AAAA,2DAAC,SAAI,WAAU,sBACb;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,iBAAe;AAAA,oBACf,SAAS,MAAM;AAAE,2BAAK,gBAAgB,KAAK,IAAI,UAAU,WAAW;AAAA,oBAAE;AAAA,oBAEtE;AAAA,kEAAC,UAAK,WAAU,cAAc,iBAAO,WAAM,UAAI;AAAA,sBAC/C,4CAAC,UAAM,oBAAU,OAAM;AAAA,sBACvB,4CAAC,UAAM,oBAAU,MAAK;AAAA,sBACtB,6CAAC,UAAK,WAAU,cAAc;AAAA,kCAAU,WAAW;AAAA,wBAAO;AAAA,wBAAE,EAAE,uBAAuB;AAAA,yBAAE;AAAA;AAAA;AAAA,gBACzF;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,UAAU,SAAS;AAAA,oBACnB,SAAS,MAAM;AAAE,2BAAK,WAAW,MAAM,UAAU,WAAW;AAAA,oBAAE;AAAA,oBAE7D,mBAAS,UAAU,KAAK,EAAE,IAAI,UAAU,WAAW,KAAK,EAAE,mBAAmB,IAAI,EAAE,qBAAqB;AAAA;AAAA,gBAC3G;AAAA,iBACF;AAAA,cACC,QACC,4CAAC,SAAI,WAAU,iBACZ,mBAAS,YAAY,MAAM,SACxB,4CAAC,OAAE,WAAU,cAAc,YAAE,0BAA0B,GAAE,IACzD,SAAS,YAAY,GAAG,WAAW,IACjC,4CAAC,OAAE,WAAU,cAAc,YAAE,qBAAqB,GAAE,IACpD,SAAS,YAAY,GAAG,IAAI,aAC5B,6CAAC,SAA4B,WAAU,gBACrC;AAAA,4DAAC,UAAK,WAAW,QAAQ,OAAO,cAAc,aAAa;AAAA,gBAC3D,4CAAC,UAAM,kBAAQ,WAAU;AAAA,gBACzB,4CAAC,UAAM,kBAAQ,OAAO,UAAI;AAAA,gBAC1B,6CAAC,UAAK,WAAU,cAAc;AAAA,0BAAQ,cAAc;AAAA,kBAAI;AAAA,kBAAE,EAAE,qBAAqB;AAAA,mBAAE;AAAA,mBAJ3E,QAAQ,SAKlB,CACD,GACP;AAAA,iBApCM,UAAU,WAsCpB;AAAA,UAEJ,CAAC;AAAA,WACP;AAAA,WAlKU,KAAK,EAoKnB,CACD,GACH;AAAA,OACF;AAAA,KAEF;AAEJ;AAEA,SAAS,iBAAiB,KAAmB;AAC3C,8BAAU,MAAM;AACd,UAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,QAAI,QAAQ,SAAS;AACrB,QAAI,cAAc;AAClB,aAAS,KAAK,YAAY,GAAG;AAC7B,WAAO,MAAM;AAAE,UAAI,OAAO;AAAA,IAAE;AAAA,EAC9B,GAAG,CAAC,GAAG,CAAC;AACV;AAEA,SAAS,UAAU,OAAgB,UAA0B;AAC3D,SAAO,iBAAiB,SAAS,MAAM,YAAY,KAAK,MAAM,UAAU;AAC1E;;;AE3cO,SAAS,8BAA8B,KAAc,KAAqB;AAC/E,QAAM,YAAY,IAAI;AACtB,MAAI,cAAc,UAAa,OAAO,UAAU,iBAAiB,WAAY;AAC7E,QAAM,WAAW,UAAU,aAAa,KAAK,SAAS;AACtD,YAAU,eAAe,CAAC,aAAa,YAAY;AACjD,QAAI,gBAAgB,QAAW;AAC7B,eAAS,aAAa,OAAO;AAC7B;AAAA,IACF;AACA,SAAK,MAAM,KAAK,OAAO,WAAW,CAAC,EAAE,KAAK,YAAU;AAClD,UAAI,CAAC,OAAQ,UAAS,aAAa,OAAO;AAAA,IAC5C,CAAC,EAAE,MAAM,MAAM;AAAE,eAAS,aAAa,OAAO;AAAA,IAAE,CAAC;AAAA,EACnD;AACF;AAEA,eAAe,MAAM,KAAe,kBAA4C;AAC9E,QAAM,QAAQ,MAAM,IAAI,MAAM;AAC9B,aAAW,QAAQ,MAAM,OAAO;AAC9B,QAAI,KAAK,UAAU,YAAa;AAChC,UAAM,SAAS,MAAM,IAAI,WAAW,KAAK,EAAE;AAC3C,UAAM,YAAY,OAAO,WAAW,KAAK,UAAQ,KAAK,qBAAqB,gBAAgB;AAC3F,QAAI,cAAc,OAAW;AAC7B,UAAM,UAAU,MAAM,IAAI,cAAc,KAAK,IAAI,EAAE,aAAa,UAAU,YAAY,CAAC;AACvF,QAAI,QAAQ,mBAAmB,QAAW;AACxC,UAAI,YAAY,QAAQ,cAAc;AACtC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC5BO,SAAS,mBAAmB,KAAc,KAAqB;AACpE,QAAM,SAAS,IAAI;AACnB,QAAM,OAAO,QAAQ;AACrB,MAAI,SAAS,OAAW;AAExB,QAAM,WAAW;AAAA,IACf,MAAM,KAAK,KAAK,KAAK,IAAI;AAAA,IACzB,MAAM,KAAK,KAAK,KAAK,IAAI;AAAA,IACzB,MAAM,KAAK,KAAK,KAAK,IAAI;AAAA,IACzB,WAAW,KAAK,UAAU,KAAK,IAAI;AAAA,EACrC;AAEA,OAAK,OAAO,OAAO,WAAW,MAAM,WAAW;AAC7C,UAAM,SAAS,MAAM,IAAI,SAAS,WAAW,IAAI;AACjD,WAAO,OAAO,WAAW,EAAE,IAAI,MAAM,OAAO,OAAO,MAAM,IAAI,SAAS,KAAK,WAAW,MAAM,MAAM;AAAA,EACpG;AACA,OAAK,OAAO,OAAO,WAAW,MAAM,OAAO,WAAW;AACpD,UAAM,SAAS,MAAM,IAAI,SAAS,WAAW,MAAM,MAAM,QAAQ,MAAM,KAAK;AAC5E,WAAO,OAAO,WAAW,EAAE,IAAI,MAAM,OAAO,OAAO,MAAM,IAAI,SAAS,KAAK,WAAW,MAAM,OAAO,MAAM;AAAA,EAC3G;AACA,OAAK,OAAO,OAAO,WAAW,MAAM,WAAW;AAC7C,UAAM,SAAS,MAAM,IAAI,SAAS,WAAW,IAAI;AACjD,WAAO,OAAO,WAAW,EAAE,IAAI,MAAM,OAAO,OAAO,MAAM,IAAI,SAAS,KAAK,WAAW,MAAM,MAAM;AAAA,EACpG;AACA,OAAK,YAAY,OAAO,WAAW,MAAM,SAAS,WAAW;AAC3D,UAAM,SAAS,MAAM,IAAI,UAAU,WAAW,MAAM,QAAQ,OAAO,QAAQ,QAAQ,OAAO,MAAM;AAChG,WAAO,OAAO,WAAW,EAAE,IAAI,MAAM,OAAO,OAAO,MAAM,IAAI,SAAS,UAAU,WAAW,MAAM,SAAS,MAAM;AAAA,EAClH;AACF;;;AC3CO,IAAM,KAAK;AAEX,IAAM,KAAK;AAAA,EAChB,kBAAkB;AAAA,EAClB,0BAA0B;AAAA,EAC1B,0BAA0B;AAAA,EAC1B,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA,EAC3B,kBAAkB;AAAA,EAClB,6BAA6B;AAAA,EAC7B,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,0BAA0B;AAAA,EAC1B,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,iCAAiC;AAAA,EACjC,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,gCAAgC;AAAA,EAChC,gCAAgC;AAAA,EAChC,gCAAgC;AAAA,EAChC,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,qCAAqC;AAAA,EACrC,yBAAyB;AAAA,EACzB,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,4BAA4B;AAAA,EAC5B,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,0BAA0B;AAAA,EAC1B,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,0BAA0B;AAAA,EAC1B,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,sBAAsB;AAAA,EACtB,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,6BAA6B;AAAA,EAC7B,oBAAoB;AACtB;AAIO,IAAM,KAA8B;AAAA,EACzC,kBAAkB;AAAA,EAClB,0BAA0B;AAAA,EAC1B,0BAA0B;AAAA,EAC1B,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA,EAC3B,kBAAkB;AAAA,EAClB,6BAA6B;AAAA,EAC7B,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,0BAA0B;AAAA,EAC1B,wBAAwB;AAAA,EACxB,4BAA4B;AAAA,EAC5B,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,iCAAiC;AAAA,EACjC,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,gCAAgC;AAAA,EAChC,gCAAgC;AAAA,EAChC,gCAAgC;AAAA,EAChC,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,mBAAmB;AAAA,EACnB,qCAAqC;AAAA,EACrC,yBAAyB;AAAA,EACzB,4BAA4B;AAAA,EAC5B,8BAA8B;AAAA,EAC9B,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,4BAA4B;AAAA,EAC5B,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,0BAA0B;AAAA,EAC1B,0BAA0B;AAAA,EAC1B,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,0BAA0B;AAAA,EAC1B,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,uBAAuB;AAAA,EACvB,uBAAuB;AAAA,EACvB,2BAA2B;AAAA,EAC3B,sBAAsB;AAAA,EACtB,aAAa;AAAA,EACb,uBAAuB;AAAA,EACvB,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,6BAA6B;AAAA,EAC7B,oBAAoB;AACtB;;;ACzMA,IAAM,aAAa,oBAAI,IAAI,CAAC,iBAAiB,gCAAO,CAAC;AACrD,IAAM,oBAAoB;AAC1B,IAAM,eAAe;AACrB,IAAI;AACJ,IAAI;AACJ,IAAI;AAqBJ,eAAe,mBAAmB,KAAsC;AAGtE,QAAM,UAAW,WAAgF;AACjG,MAAI,YAAY,OAAW,QAAO,QAAQ,KAAK;AAC/C,QAAM,cAAe,IAAsF;AAC3G,MAAI,aAAa,kBAAkB,OAAW,QAAO,YAAY,cAAc;AAC/E,QAAM,SAAU,IAA0E;AAC1F,MAAI,QAAQ,oBAAoB,OAAW,OAAM,IAAI,MAAM,iCAAiC;AAC5F,QAAM,SAAS,MAAM,OAAO,gBAAgB,KAAK;AACjD,MAAI,CAAC,OAAO,GAAI,OAAM,IAAI,MAAM,OAAO,MAAM,OAAO;AACpD,SAAO,OAAO;AAChB;AAUO,SAAS,wBAAwB,KAAc,KAAe,GAAoB;AACvF,MAAI,OAAO,aAAa,YAAa;AACrC,eAAa;AAEb,MAAI,OAAO,MAAM;AACf,UAAM,UAAU,CAAC,UAA4B;AAC3C,YAAM,SAAS,MAAM;AACrB,UAAI,EAAE,kBAAkB,SAAU;AAClC,YAAM,SAAS,OAAO,QAA2B,oBAAoB;AACrE,UAAI,WAAW,KAAM;AACrB,YAAM,QAAQ,OAAO,aAAa,YAAY,KAAK;AACnD,UAAI,CAAC,WAAW,IAAI,KAAK,EAAG;AAC5B,UAAI,OAAO,QAAQ,0BAA0B,MAAM,KAAM;AACzD,UAAI,OAAO,QAAQ,iBAAiB,MAAM,KAAK;AAC7C,eAAO,OAAO,QAAQ,iBAAiB;AACvC;AAAA,MACF;AACA,YAAM,eAAe;AACrB,YAAM,yBAAyB;AAC/B,WAAK,iBAAiB,EAAE,KAAK,KAAK,EAAE,GAAG,MAAM;AAAA,IAC/C;AACA,aAAS,iBAAiB,SAAS,SAAS,IAAI;AAChD,WAAO,MAAM;AACX,eAAS,oBAAoB,SAAS,SAAS,IAAI;AACnD,qBAAe;AAAA,IACjB;AAAA,EACF,GAAG,kCAAkC;AACvC;AAEA,eAAe,iBAAiB,MAA4B,QAA0C;AACpG,iBAAe;AACf,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,UAAQ,aAAa,QAAQ,cAAc;AAC3C,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,aAAa,QAAQ,QAAQ;AACpC,SAAO,aAAa,cAAc,MAAM;AACxC,SAAO,aAAa,cAAc,KAAK,EAAE,cAAc,CAAC;AAExD,QAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,QAAM,YAAY,GAAG,YAAY;AACjC,QAAM,cAAc,KAAK,EAAE,cAAc;AACzC,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY,GAAG,YAAY;AAClC,QAAM,cAAc,SAAS,cAAc,QAAQ;AACnD,cAAY,OAAO;AACnB,cAAY,YAAY,GAAG,YAAY;AACvC,cAAY,aAAa,cAAc,KAAK,EAAE,cAAc,CAAC;AAC7D,cAAY,cAAc;AAC1B,SAAO,OAAO,OAAO,WAAW;AAEhC,QAAM,YAAY,SAAS,cAAc,OAAO;AAChD,YAAU,YAAY,GAAG,YAAY;AACrC,QAAM,WAAW,SAAS,cAAc,MAAM;AAC9C,WAAS,cAAc,KAAK,EAAE,aAAa;AAC3C,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,QAAM,WAAW,SAAS,cAAc,MAAM;AAC9C,WAAS,YAAY,GAAG,YAAY;AACpC,gBAAc,QAAQ;AACtB,QAAM,YAAY,SAAS,cAAc,OAAO;AAChD,YAAU,YAAY,GAAG,YAAY;AACrC,YAAU,cAAc,KAAK,EAAE,wBAAwB;AACvD,YAAU,eAAe;AACzB,UAAQ,OAAO,UAAU,SAAS;AAClC,YAAU,OAAO,UAAU,OAAO;AAElC,QAAM,cAAc,SAAS,cAAc,IAAI;AAC/C,cAAY,YAAY,GAAG,YAAY;AACvC,cAAY,cAAc,KAAK,EAAE,eAAe;AAChD,QAAM,eAAe,SAAS,cAAc,KAAK;AACjD,eAAa,YAAY,GAAG,YAAY;AACxC,QAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,gBAAc,YAAY,GAAG,YAAY;AACzC,QAAM,WAAW,SAAS,cAAc,MAAM;AAC9C,WAAS,YAAY,GAAG,YAAY;AACpC,kBAAgB,QAAQ;AACxB,QAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,gBAAc,OAAO,UAAU,aAAa;AAC5C,eAAa,OAAO,aAAa,aAAa;AAC9C,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY,GAAG,YAAY;AACrC,QAAM,eAAe,SAAS,cAAc,KAAK;AACjD,eAAa,YAAY,GAAG,YAAY;AACxC,QAAM,gBAAgB,SAAS,cAAc,QAAQ;AACrD,gBAAc,OAAO;AACrB,gBAAc,YAAY,GAAG,YAAY;AACzC,QAAM,oBAAoB,SAAS,cAAc,MAAM;AACvD,QAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,gBAAc,YAAY,GAAG,YAAY;AACzC,gBAAc,cAAc;AAC5B,gBAAc,OAAO,mBAAmB,aAAa;AACrD,QAAM,YAAY,SAAS,cAAc,QAAQ;AACjD,YAAU,OAAO;AACjB,YAAU,YAAY,GAAG,YAAY;AACrC,QAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,gBAAc,YAAY,GAAG,YAAY;AACzC,QAAM,gBAAgB,SAAS,cAAc,MAAM;AACnD,gBAAc,cAAc,KAAK,EAAE,YAAY;AAC/C,YAAU,OAAO,eAAe,aAAa;AAC7C,eAAa,OAAO,eAAe,SAAS;AAC5C,QAAM,iBAAiB,SAAS,cAAc,KAAK;AACnD,iBAAe,YAAY,GAAG,YAAY;AAC1C,QAAM,eAAe,SAAS,cAAc,MAAM;AAClD,eAAa,cAAc;AAC3B,QAAM,eAAe,SAAS,cAAc,MAAM;AAClD,eAAa,YAAY,GAAG,YAAY;AACxC,QAAM,eAAe,SAAS,cAAc,QAAQ;AACpD,eAAa,OAAO;AACpB,eAAa,YAAY,GAAG,YAAY;AACxC,eAAa,aAAa,cAAc,KAAK,EAAE,qBAAqB,CAAC;AACrE,eAAa,cAAc;AAC3B,iBAAe,OAAO,cAAc,cAAc,YAAY;AAC9D,YAAU,OAAO,cAAc,cAAc;AAE7C,QAAM,QAAQ,SAAS,cAAc,GAAG;AACxC,QAAM,YAAY,GAAG,YAAY;AACjC,QAAM,aAAa,QAAQ,OAAO;AAElC,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY,GAAG,YAAY;AAClC,SAAO,cAAc,KAAK,EAAE,eAAe;AAC3C,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY,GAAG,YAAY,WAAW,YAAY;AACzD,SAAO,cAAc,KAAK,EAAE,eAAe;AAC3C,UAAQ,OAAO,QAAQ,MAAM;AAE7B,SAAO,OAAO,QAAQ,WAAW,cAAc,WAAW,OAAO,OAAO;AACxE,UAAQ,OAAO,MAAM;AACrB,WAAS,KAAK,OAAO,OAAO;AAE5B,MAAI,QAAoB,CAAC;AACzB,MAAI,cAA2B,EAAE,MAAM,QAAQ;AAC/C,MAAI,eAAe;AACnB,QAAM,SAAS,MAAY;AACzB,UAAM,QAAQ,YAAY,SAAS,UAAU,KAAK,EAAE,cAAc,IAAI,YAAY,KAAK;AACvF,sBAAkB,cAAc,GAAG,KAAK,EAAE,oBAAoB,CAAC,GAAG,KAAK,GAAG,KAAK,EAAE,oBAAoB,CAAC;AACtG,kBAAc,cAAc;AAC5B,kBAAc,cAAc;AAC5B,UAAM,YAAY,iBAAiB;AACnC,iBAAa,MAAM,UAAU,YAAY,SAAS;AAClD,mBAAe,MAAM,UAAU,YAAY,SAAS;AACpD,cAAU,UAAU,OAAO,GAAG,YAAY,wBAAwB,SAAS;AAC3E,iBAAa,cAAc,YAAY,WAAW,YAAY,IAAI;AAClE,mBAAe,QAAQ,YAAY,eAAe;AAClD,WAAO,WAAW,CAAC;AAAA,EACrB;AACA,QAAM,QAAQ,MAAY;AACxB,mBAAe;AACf,gBAAY;AACZ,YAAQ,OAAO;AACf,WAAO,oBAAoB,WAAW,YAAY,IAAI;AACtD,YAAQ,oBAAoB,eAAe,SAAS,IAAI;AACxD,QAAI,gBAAgB,MAAO,eAAc;AAAA,EAC3C;AACA,gBAAc;AACd,QAAM,aAAa,CAAC,UAA+B;AACjD,QAAI,MAAM,QAAQ,SAAU,OAAM;AAAA,EACpC;AACA,QAAM,UAAU,CAAC,UAAuB;AACtC,QAAI,MAAM,WAAW,QAAS,OAAM;AAAA,EACtC;AACA,SAAO,iBAAiB,WAAW,YAAY,IAAI;AACnD,UAAQ,iBAAiB,eAAe,SAAS,IAAI;AACrD,SAAO,iBAAiB,SAAS,KAAK;AACtC,cAAY,iBAAiB,SAAS,KAAK;AAE3C,gBAAc,iBAAiB,SAAS,MAAM;AAC5C,wBAAoB,eAAe,OAAO,aAAa,KAAK,GAAG,UAAQ;AACrE,oBAAc;AACd,qBAAe;AACf,aAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AACD,YAAU,iBAAiB,SAAS,MAAM;AACxC,QAAI,YAAY,SAAS,SAAS;AAChC,gBAAU,WAAW;AACrB,WAAK,mBAAmB,KAAK,GAAG,EAC7B,KAAK,UAAQ;AACZ,YAAI,SAAS,MAAM;AACjB,yBAAe;AACf,iBAAO;AAAA,QACT;AAAA,MACF,CAAC,EACA,MAAM,aAAW;AAAE,cAAM,cAAcA,WAAU,SAAS,KAAK,EAAE,2BAA2B,CAAC;AAAA,MAAE,CAAC,EAChG,QAAQ,MAAM;AAAE,kBAAU,WAAW;AAAA,MAAM,CAAC;AAC/C;AAAA,IACF;AACA,UAAM,UAAU,YAAY,KAAK,UAAU,QAAQ;AACnD,yBAAqB,MAAM,aAAa,SAAS,UAAQ;AACvD,qBAAe;AACf,aAAO;AAAA,IACT,CAAC;AAAA,EACH,CAAC;AACD,eAAa,iBAAiB,SAAS,MAAM;AAC3C,mBAAe;AACf,WAAO;AAAA,EACT,CAAC;AACD,YAAU,iBAAiB,SAAS,MAAM;AAE1C,MAAI;AACF,UAAM,QAAQ,MAAM,KAAK,IAAI,MAAM;AACnC,YAAQ,MAAM,MAAM,OAAO,UAAQ,KAAK,UAAU,WAAW;AAAA,EAC/D,QAAQ;AACN,YAAQ,CAAC;AAAA,EACX;AACA,SAAO;AACP,SAAO,iBAAiB,SAAS,MAAM;AACrC,QAAI,iBAAiB,GAAI;AACzB,UAAMC,SAAQ,UAAU,MAAM,KAAK,KAAK,WAAW,YAAY;AAC/D,WAAO,WAAW;AAClB,WAAO,WAAW;AAClB,WAAO,cAAc,KAAK,EAAE,iBAAiB;AAC7C,UAAM,YAAY;AAChB,UAAI;AACF,YAAI,YAAY,SAAS,SAAS;AAChC,gBAAM,QAAQ,MAAM,KAAK,IAAI,qBAAqB,EAAE,MAAM,cAAc,OAAAA,OAAM,CAAC;AAC/E,gBAAM;AACN,eAAK,IAAI,YAAY,aAAa,MAAM,WAAoB;AAC5D;AAAA,QACF;AACA,cAAM,YAAY,MAAM,KAAK,IAAI,gBAAgB,YAAY,KAAK,IAAI;AAAA,UACpE,MAAM;AAAA,UACN,OAAAA;AAAA,QACF,CAAC;AACD,cAAM,UAAU,MAAM,KAAK,IAAI,cAAc,YAAY,KAAK,IAAI;AAAA,UAChE,aAAa,UAAU;AAAA,QACzB,CAAC;AACD,YAAI,QAAQ,mBAAmB,QAAW;AACxC,gBAAM;AACN,eAAK,IAAI,YAAY,QAAQ,cAAc;AAC3C;AAAA,QACF;AACA,cAAM,UAAU,MAAM,sBAAsB,KAAK,KAAK,YAAY,KAAK,IAAI,UAAU,WAAW;AAChG,YAAI,YAAY,OAAW,OAAM,IAAI,MAAM,KAAK,EAAE,yBAAyB,CAAC;AAC5E,cAAM;AACN,aAAK,IAAI,YAAY,aAAa,OAAgB;AAAA,MACpD,SAAS,SAAS;AAChB,cAAM,cAAcD,WAAU,SAAS,KAAK,EAAE,qBAAqB,CAAC;AACpE,eAAO,WAAW;AAClB,eAAO,WAAW;AAClB,eAAO,cAAc,KAAK,EAAE,eAAe;AAAA,MAC7C;AAAA,IACF,GAAG;AAAA,EACL,CAAC;AAED,YAAU,MAAM;AAClB;AAEA,SAAS,oBACP,QACA,OACA,SACA,GACA,QACM;AACN,cAAY;AACZ,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY,GAAG,YAAY;AAChC,QAAM,MAAM,CAAC,OAAe,gBAAmC;AAC7D,UAAM,OAAO,SAAS,cAAc,QAAQ;AAC5C,SAAK,OAAO;AACZ,SAAK,YAAY,GAAG,YAAY;AAChC,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,cAAc;AACnB,UAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,UAAM,YAAY,GAAG,YAAY;AACjC,UAAM,WAAW,QAAQ,SAAS,YAAY,SACxC,YAAY,SAAS,WAAY,QAAQ,SAAS,YAAY,QAAQ,KAAK,OAAO,YAAY,KAAK;AACzG,UAAM,cAAc,WAAW,WAAM;AACrC,SAAK,OAAO,MAAM,KAAK;AACvB,SAAK,iBAAiB,SAAS,MAAM;AACnC,aAAO,WAAW;AAClB,YAAM;AAAA,IACR,CAAC;AACD,SAAK,OAAO,IAAI;AAAA,EAClB;AACA,MAAI,EAAE,cAAc,GAAG,EAAE,MAAM,QAAQ,CAAC;AACxC,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,YAAY,GAAG,YAAY;AACnC,YAAQ,cAAc,EAAE,sBAAsB;AAC9C,SAAK,OAAO,OAAO;AACnB,eAAW,QAAQ,MAAO,KAAI,KAAK,OAAO,EAAE,MAAM,UAAU,KAAK,CAAC;AAAA,EACpE;AACA,QAAM,YAAY,SAAS,cAAc,QAAQ;AACjD,YAAU,OAAO;AACjB,YAAU,YAAY,GAAG,YAAY,qBAAqB,YAAY;AACtE,YAAU,cAAc,EAAE,kBAAkB;AAC5C,YAAU,iBAAiB,SAAS,MAAM;AACxC,UAAM;AACN,UAAM,QAAQ,SAAS,cAAc,IAAI,YAAY,QAAQ;AAC7D,QAAI,UAAU,KAAM,OAAM,cAAc,EAAE,sBAAsB;AAAA,EAClE,CAAC;AACD,OAAK,OAAO,SAAS;AACrB,WAAS,KAAK,OAAO,IAAI;AACzB,QAAM,OAAO,OAAO,sBAAsB;AAC1C,OAAK,MAAM,OAAO,GAAG,KAAK,IAAI,KAAK,MAAM,OAAO,aAAa,GAAG,CAAC;AACjE,OAAK,MAAM,MAAM,GAAG,KAAK,SAAS,CAAC;AACnC,QAAM,QAAQ,MAAY;AACxB,SAAK,OAAO;AACZ,WAAO,oBAAoB,eAAe,SAAS,IAAI;AACvD,QAAI,cAAc,MAAO,aAAY;AAAA,EACvC;AACA,cAAY;AACZ,QAAM,UAAU,CAAC,UAAuB;AACtC,QAAI,MAAM,kBAAkB,QAAQ,KAAK,SAAS,MAAM,MAAM,EAAG;AACjE,UAAM;AAAA,EACR;AACA,SAAO,iBAAiB,eAAe,SAAS,IAAI;AACtD;AAEA,eAAe,qBACb,MACA,aACA,aACA,QACe;AACf,iBAAe;AACf,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,UAAQ,aAAa,QAAQ,cAAc;AAC3C,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY,GAAG,YAAY,IAAI,YAAY;AAClD,SAAO,aAAa,QAAQ,QAAQ;AACpC,SAAO,aAAa,cAAc,MAAM;AACxC,SAAO,aAAa,cAAc,KAAK,EAAE,oBAAoB,CAAC;AAE9D,QAAM,QAAQ,SAAS,cAAc,IAAI;AACzC,QAAM,YAAY,GAAG,YAAY;AACjC,QAAM,cAAc,KAAK,EAAE,oBAAoB;AAC/C,QAAM,gBAAgB,SAAS,cAAc,KAAK;AAClD,gBAAc,YAAY,GAAG,YAAY;AACzC,QAAM,eAAe,SAAS,cAAc,QAAQ;AACpD,eAAa,OAAO;AACpB,eAAa,YAAY,GAAG,YAAY;AACxC,eAAa,aAAa,cAAc,KAAK,EAAE,cAAc,CAAC;AAC9D,eAAa,cAAc;AAC3B,gBAAc,OAAO,OAAO,YAAY;AACxC,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,QAAM,KAAK,SAAS,cAAc,QAAQ;AAC1C,KAAG,OAAO;AACV,KAAG,YAAY,GAAG,YAAY;AAC9B,KAAG,cAAc;AACjB,KAAG,aAAa,cAAc,KAAK,EAAE,WAAW,CAAC;AACjD,QAAM,YAAY,SAAS,cAAc,OAAO;AAChD,YAAU,YAAY,GAAG,YAAY;AACrC,YAAU,QAAQ;AAClB,YAAU,eAAe;AACzB,UAAQ,OAAO,IAAI,SAAS;AAC5B,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,YAAY,GAAG,YAAY;AAClC,SAAO,cAAc,KAAK,EAAE,qBAAqB;AACjD,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY,GAAG,YAAY;AAChC,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY,GAAG,YAAY;AACnC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY,GAAG,YAAY;AAClC,SAAO,cAAc,KAAK,EAAE,eAAe;AAC3C,QAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,MAAI,OAAO;AACX,MAAI,YAAY,GAAG,YAAY,WAAW,YAAY;AACtD,MAAI,cAAc,KAAK,EAAE,kBAAkB;AAC3C,UAAQ,OAAO,QAAQ,GAAG;AAC1B,SAAO,OAAO,eAAe,SAAS,QAAQ,MAAM,OAAO;AAC3D,UAAQ,OAAO,MAAM;AACrB,WAAS,KAAK,OAAO,OAAO;AAE5B,MAAI,UAAU;AACd,MAAI,SAAS;AACb,MAAI,iBAA8E,CAAC;AACnF,MAAI,aAAa;AACjB,MAAI;AAEJ,QAAM,eAAe,OAAO,SAAwD;AAClF,UAAM,WAAW,EAAE;AACnB,WAAO,cAAc,KAAK,EAAE,gBAAgB;AAC5C,WAAO,UAAU,OAAO,GAAG,YAAY,aAAa;AACpD,QAAI;AACF,YAAM,UAA4B,YAAY,SAAS,UACnD,MAAM,KAAK,IAAI,qBAAqB,IAAI,IACxC,MAAM,KAAK,IAAI,gBAAgB,YAAY,KAAK,IAAI,IAAI;AAC5D,UAAI,aAAa,WAAY,QAAO;AACpC,aAAO;AAAA,IACT,SAAS,SAAS;AAChB,UAAI,aAAa,YAAY;AAC3B,eAAO,cAAcA,WAAU,SAAS,KAAK,EAAE,mBAAmB,CAAC;AACnE,eAAO,UAAU,IAAI,GAAG,YAAY,aAAa;AAAA,MACnD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,YAA+E;AAC7F,SAAK,gBAAgB;AACrB,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,QAAQ,SAAS,cAAc,GAAG;AACxC,YAAM,YAAY,GAAG,YAAY;AACjC,YAAM,cAAc,KAAK,EAAE,oBAAoB;AAC/C,WAAK,OAAO,KAAK;AACjB;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,OAAO,SAAS,cAAc,QAAQ;AAC5C,WAAK,OAAO;AACZ,WAAK,YAAY,GAAG,YAAY;AAChC,YAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,WAAK,cAAc;AACnB,YAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,YAAM,cAAc,MAAM;AAC1B,WAAK,OAAO,MAAM,KAAK;AACvB,WAAK,iBAAiB,SAAS,MAAM;AAAE,aAAK,KAAK,MAAM,IAAI;AAAA,MAAE,CAAC;AAC9D,WAAK,OAAO,IAAI;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,OAAO,OAAO,SAAgC;AAClD,UAAM,UAAU,MAAM,aAAa,IAAI;AACvC,QAAI,YAAY,OAAW;AAC3B,cAAU,QAAQ;AAClB,aAAS,QAAQ;AACjB,qBAAiB,QAAQ;AACzB,cAAU,QAAQ,QAAQ;AAC1B,WAAO,cAAc,KAAK,EAAE,qBAAqB;AACjD,WAAO,QAAQ,OAAO;AAAA,EACxB;AAEA,QAAM,mBAAmB,OAAO,QAA+B;AAC7D,QAAI,QAAQ,GAAI;AAChB,UAAM,QAAQ,IAAI,YAAY,GAAG;AACjC,UAAM,MAAM,QAAQ,IAAI,UAAW,IAAI,MAAM,GAAG,QAAQ,CAAC,KAAK;AAC9D,UAAM,QAAQ,QAAQ,IAAI,MAAM,IAAI,MAAM,QAAQ,CAAC;AACnD,QAAI,UAAU,IAAI;AAEhB,YAAM,KAAK,GAAG;AACd;AAAA,IACF;AACA,UAAM,oBAAoB,QAAQ,QAAQ,SAAS,EAAE,KAAK;AAC1D,UAAM,gBAAgB,IAAI,QAAQ,SAAS,EAAE,KAAK;AAClD,QAAI,kBAAkB,mBAAmB;AACvC,YAAME,UAAS,MAAM,kBAAkB;AACvC,aAAO,eAAe,OAAO,WAAS,MAAM,KAAK,kBAAkB,EAAE,WAAWA,OAAM,CAAC,CAAC;AACxF,aAAO,cAAc,KAAK,EAAE,qBAAqB;AACjD;AAAA,IACF;AACA,UAAM,UAAU,MAAM,aAAa,GAAG;AACtC,QAAI,YAAY,OAAW;AAC3B,cAAU,QAAQ;AAClB,aAAS,QAAQ;AACjB,qBAAiB,QAAQ;AACzB,UAAM,SAAS,MAAM,kBAAkB;AACvC,WAAO,QAAQ,QAAQ,OAAO,WAAS,MAAM,KAAK,kBAAkB,EAAE,WAAW,MAAM,CAAC,CAAC;AACzF,WAAO,cAAc,KAAK,EAAE,qBAAqB;AAAA,EACnD;AAEA,QAAM,QAAQ,MAAY;AACxB,YAAQ,OAAO;AACf,WAAO,oBAAoB,WAAW,YAAY,IAAI;AACtD,YAAQ,oBAAoB,eAAe,SAAS,IAAI;AACxD,QAAI,iBAAiB,MAAO,gBAAe;AAAA,EAC7C;AACA,iBAAe;AACf,QAAM,aAAa,CAAC,UAA+B;AACjD,QAAI,MAAM,QAAQ,SAAU,OAAM;AAAA,EACpC;AACA,QAAM,UAAU,CAAC,UAAuB;AACtC,QAAI,MAAM,WAAW,QAAS,OAAM;AAAA,EACtC;AACA,SAAO,iBAAiB,WAAW,YAAY,IAAI;AACnD,UAAQ,iBAAiB,eAAe,SAAS,IAAI;AACrD,SAAO,iBAAiB,SAAS,KAAK;AACtC,eAAa,iBAAiB,SAAS,KAAK;AAC5C,MAAI,iBAAiB,SAAS,MAAM;AAClC,WAAO,OAAO;AACd,UAAM;AAAA,EACR,CAAC;AACD,KAAG,iBAAiB,SAAS,MAAM;AACjC,QAAI,WAAW,MAAM,WAAW,QAAS,MAAK,KAAK,MAAM;AAAA,EAC3D,CAAC;AACD,YAAU,iBAAiB,SAAS,MAAM;AACxC,QAAI,gBAAgB,OAAW,QAAO,aAAa,WAAW;AAC9D,UAAM,QAAQ,UAAU;AACxB,kBAAc,OAAO,WAAW,MAAM;AAAE,WAAK,iBAAiB,KAAK;AAAA,IAAE,GAAG,GAAG;AAAA,EAC7E,CAAC;AACD,YAAU,iBAAiB,WAAW,WAAS;AAC7C,QAAI,MAAM,QAAQ,QAAS;AAC3B,UAAM,eAAe;AACrB,UAAM,QAAQ,UAAU,MAAM,KAAK;AACnC,QAAI,UAAU,GAAI,MAAK,iBAAiB,KAAK;AAAA,EAC/C,CAAC;AACD,OAAK,KAAK,WAAW;AACvB;AAEA,eAAe,sBAAsB,KAAe,QAAgB,aAAkD;AACpH,WAAS,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;AAChD,UAAM,SAAS,MAAM,IAAI,WAAW,MAAM;AAC1C,UAAM,YAAY,OAAO,WAAW,KAAK,UAAQ,KAAK,gBAAgB,WAAW;AACjF,QAAI,WAAW,qBAAqB,OAAW,QAAO,UAAU;AAChE,UAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,GAAG,CAAC;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,iBAAuB;AAC9B,gBAAc;AACd,iBAAe;AACf,cAAY;AACd;AAEA,SAASF,WAAU,SAAkB,UAA0B;AAC7D,MAAI,mBAAmB,SAAS,QAAQ,YAAY,GAAI,QAAO,QAAQ;AACvE,SAAO;AACT;AAEA,SAAS,cAAc,MAAyB;AAC9C,OAAK,YAAY;AACnB;AAEA,SAAS,WAAW,MAAsB;AACxC,QAAM,QAAQ,KAAK,QAAQ,YAAY,EAAE;AACzC,QAAM,QAAQ,MAAM,MAAM,SAAS;AACnC,SAAO,MAAM,MAAM,SAAS,CAAC,KAAK;AACpC;AAEA,SAAS,gBAAgB,MAAyB;AAChD,OAAK,YAAY;AACnB;AAEA,SAAS,eAAqB;AAC5B,QAAM,WAAW,SAAS,eAAe,0BAA0B;AACnE,QAAM,QAAQ,oBAAoB,mBAAmB,WAAW,SAAS,cAAc,OAAO;AAC9F,QAAM,KAAK;AACX,QAAM,cAAc;AAAA,OACf,YAAY;AAAA;AAAA;AAAA;AAAA,OAIZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAOZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,OAKZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA,OACZ,YAAY;AAAA;AAEjB,MAAI,aAAa,KAAM,UAAS,KAAK,OAAO,KAAK;AACnD;;;AR5oBO,IAAM,SAAS,CAAC,SAAS,eAAe,QAAQ;AAGhD,SAAS,MAAM,KAAoB;AACxC,QAAM,MAAM,eAAe,GAAG;AAC9B,QAAM,SAAU,IAA0C;AAC1D,MAAI,OAAO,MAAM,OAAO,SAAS,IAAI,EAAE,IAAI,GAAG,CAAC,GAAG,iCAAiC;AACnF,QAAM,IAAI,OAAO,KAAK,EAAE;AACxB,gCAA8B,KAAK,GAAG;AACtC,0BAAwB,KAAK,KAAK,CAAC;AACnC,MAAI,OAAO,CAAC,uBAAuB,GAAG,WAAS;AAC7C,uBAAmB,OAAO,GAAG;AAAA,EAC/B,CAAC;AACD,MAAI,MAAM,OAAO,oBAAoB,MAAM,IAAI,MAAM,SAAS;AAAA,IAC5D,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO,MAAM,EAAE,gBAAgB;AAAA,IAC/B,QAAQ;AAAA,IACR,QAAQ,OAAO,EAAE,IAAI;AAAA,EACvB,GAAG,sBAAsB,CAAC;AAC5B;",
4
+ "sourcesContent": ["/**\n * dsh-remote-local-plugin browser entry.\n *\n * Registers one Settings section. The component talks only to the injected\n * API closure; the Cordis context stays in `apply`.\n *\n * @module dsh-remote-local/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-workspace/client'\nimport { createHostsApi } from './api.ts'\nimport { RemoteDevelopmentPanel } from './RemoteDevelopmentPanel.tsx'\nimport { installNativeWorkspaceRouting } from './routing.ts'\nimport { installRemoteFiles } from './remote-files.ts'\nimport { NS, en, zh, type LocaleFace } from './locale.ts'\nimport { installWorkspaceAddMenu } from './workspace-add.ts'\n\n/** The Settings shell's slot registry and the authenticated connection. */\nexport const inject = ['slots', 'uiWorkspace', 'locale']\n\n/** Register the \u8FDC\u7A0B\u5F00\u53D1 page. */\nexport function apply(ctx: Context): void {\n const api = createHostsApi(ctx)\n const locale = (ctx as unknown as { locale: LocaleFace }).locale\n ctx.effect(() => locale.register(NS, { zh, en }), 'dsh-remote: locale dictionaries')\n const t = locale.bind(NS)\n installNativeWorkspaceRouting(ctx, api)\n installWorkspaceAddMenu(ctx, api, t)\n ctx.inject(['remote.workspaceFiles'], child => {\n installRemoteFiles(child, api)\n })\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'remote-development',\n order: 76,\n label: () => t('settings.title'),\n locale: NS,\n inject: () => ({ api }),\n }, RemoteDevelopmentPanel))\n}\n", "/**\n * Host API contract shared by the host handler and the browser Settings page.\n *\n * The route is registered inside DSH Connection's authenticated `/api` fence,\n * so it works through the web HTTP carrier and the desktop IPC carrier without\n * a second authentication scheme.\n *\n * @module dsh-remote-local/host-api/contract\n */\n\nimport type { HostView } from '../hosts/types.ts'\nimport type {\n AcceptedResult,\n CreateSessionResult,\n HostInfo,\n PromptDeliveryMode,\n RemoteSessionSummary,\n RemoteWorkspace,\n SessionSnapshot,\n} from '@dsh-remote/protocol'\n\n/** Exact authenticated Fetch route owned by the local plugin. */\nexport const HOST_API_PATH = '/api/dsh-remote'\n\n/** Endpoint names. */\nexport type HostApiEndpoint =\n | 'state'\n | 'target/validate'\n | 'host/add'\n | 'host/remove'\n | 'host/connect'\n | 'host/disconnect'\n | 'host/refresh'\n | 'host/provision'\n | 'host/workspaces'\n | 'host/workspace/create'\n | 'host/directory/list'\n | 'local/directory/list'\n | 'local/workspace/create'\n | 'host/sessions'\n | 'host/session'\n | 'host/session/create'\n | 'host/session/prompt'\n | 'host/session/cancel'\n | 'host/web-url'\n | 'host/files/list'\n | 'host/files/read'\n | 'host/files/stat'\n | 'host/files/bytes'\n\n/** Browser-safe state snapshot. */\nexport interface RemoteHostsState {\n readonly protocolVersion: number\n readonly hosts: readonly HostView[]\n}\n\n/** Result of parsing a destination. */\nexport interface ValidatedTarget {\n readonly canonical: string\n readonly host: string\n readonly user: string | null\n readonly port: number | null\n}\n\n/** Result of listing workspaces. */\nexport interface HostWorkspace extends RemoteWorkspace {\n /** Local mirror workspace id, when it has been synced. */\n readonly localWorkspaceId?: string\n}\n\nexport interface WorkspacesResult {\n readonly workspaces: readonly HostWorkspace[]\n}\n\n/** Result of listing sessions. */\nexport interface SessionsResult {\n readonly sessions: readonly RemoteSessionSummary[]\n}\n\nexport interface HostCreateSessionResult extends CreateSessionResult {\n /** Local mirror session that opens in the ordinary Conversation UI. */\n readonly localSessionId?: string\n}\n\nexport interface MirrorFileResult {\n readonly mirrored: boolean\n readonly value?: unknown\n}\n\n/** Result of resolving the remote Web UI. */\nexport interface WebUrlResult {\n readonly url: string\n}\n\n/** One request body. */\nexport interface HostApiRequest {\n readonly endpoint: HostApiEndpoint\n readonly payload?: unknown\n}\n\n/** Success/failure result returned by the route. */\nexport type HostApiResult<T> =\n | { readonly ok: true; readonly value: T }\n | {\n readonly ok: false\n readonly error: {\n readonly code: string\n readonly message: string\n readonly details: Record<string, unknown>\n }\n }\n\n/** Payloads accepted by each endpoint. */\nexport interface HostApiPayloads {\n state: undefined\n 'target/validate': { sshTarget: string }\n 'host/add': {\n sshTarget: string\n label?: string\n token?: string\n remoteAgentPort?: number\n autoConnect?: boolean\n }\n 'host/remove': { hostId: string }\n 'host/connect': { hostId: string }\n 'host/disconnect': { hostId: string }\n 'host/refresh': { hostId: string }\n 'host/provision': { hostId: string }\n 'host/workspaces': { hostId: string }\n 'host/workspace/create': { hostId: string; path: string; title?: string }\n 'host/directory/list': { hostId: string; path?: string }\n 'local/directory/list': { path?: string }\n 'local/workspace/create': { path: string; title?: string }\n 'host/sessions': { hostId: string; workspaceId?: string }\n 'host/session': { hostId: string; sessionId: string; limit?: number }\n 'host/session/create': { hostId: string; workspaceId?: string; cwd?: string; agentPreset?: string }\n 'host/session/prompt': {\n hostId: string\n sessionId: string\n text: string\n mode?: PromptDeliveryMode\n provider?: string\n model?: string\n reasoningEffort?: string\n }\n 'host/session/cancel': { hostId: string; sessionId: string }\n 'host/web-url': { hostId: string }\n 'host/files/list': { sessionId: string; path: string }\n 'host/files/read': { sessionId: string; path: string; offset?: number; limit?: number }\n 'host/files/stat': { sessionId: string; path: string }\n 'host/files/bytes': { sessionId: string; path: string; offset?: number; length?: number }\n}\n\n/** Values returned by each endpoint. */\nexport interface HostApiValues {\n state: RemoteHostsState\n 'target/validate': ValidatedTarget\n 'host/add': HostView\n 'host/remove': { removed: true }\n 'host/connect': HostView\n 'host/disconnect': HostView\n 'host/refresh': HostView\n 'host/provision': HostView\n 'host/workspaces': WorkspacesResult\n 'host/workspace/create': RemoteWorkspace\n 'host/directory/list': { path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }\n 'local/directory/list': { path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }\n 'local/workspace/create': { workspaceId: string }\n 'host/sessions': SessionsResult\n 'host/session': SessionSnapshot\n 'host/session/create': HostCreateSessionResult\n 'host/session/prompt': AcceptedResult\n 'host/session/cancel': AcceptedResult\n 'host/web-url': WebUrlResult\n 'host/files/list': MirrorFileResult\n 'host/files/read': MirrorFileResult\n 'host/files/stat': MirrorFileResult\n 'host/files/bytes': MirrorFileResult\n}\n\n/** Informational type for the Settings page. */\nexport interface HostSummary extends Pick<HostView, 'id' | 'label' | 'sshTarget' | 'phase'> {\n readonly hostInfo: HostInfo | null\n}\n", "/**\n * Browser-side API wrapper around the host's authenticated Fetch route.\n * Components never see `ctx`.\n *\n * @module dsh-remote-local/client/api\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { AcceptedResult, PromptDeliveryMode, RemoteWorkspace, SessionSnapshot } from '@dsh-remote/protocol'\nimport type { HostCreateSessionResult, MirrorFileResult } from '../host-api/contract.ts'\nimport {\n HOST_API_PATH,\n type HostApiPayloads,\n type HostApiValues,\n type RemoteHostsState,\n type SessionsResult,\n type ValidatedTarget,\n type WebUrlResult,\n type WorkspacesResult,\n} from '../host-api/contract.ts'\nimport type { HostView } from '../hosts/types.ts'\n\n/** Error surfaced by one host call. */\nexport class RemoteUiError extends Error {\n readonly code: string\n readonly details: Record<string, unknown>\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message)\n this.name = 'RemoteUiError'\n this.code = code\n this.details = details\n }\n}\n\n/** API surface injected into the Settings component. */\nexport interface HostsApi {\n readonly state: () => Promise<RemoteHostsState>\n readonly validateTarget: (sshTarget: string) => Promise<ValidatedTarget>\n readonly addHost: (input: HostApiPayloads['host/add']) => Promise<HostView>\n readonly removeHost: (hostId: string) => Promise<void>\n readonly connect: (hostId: string) => Promise<HostView>\n readonly disconnect: (hostId: string) => Promise<HostView>\n readonly refresh: (hostId: string) => Promise<HostView>\n readonly provision: (hostId: string) => Promise<HostView>\n readonly workspaces: (hostId: string) => Promise<WorkspacesResult>\n readonly listDirectories: (hostId: string, path?: string) => Promise<{ path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }>\n readonly listLocalDirectories: (path?: string) => Promise<{ path: string; parent: string; entries: readonly { name: string; path: string }[]; truncated: boolean }>\n readonly createLocalWorkspace: (input: { path: string; title?: string }) => Promise<{ workspaceId: string }>\n readonly createWorkspace: (hostId: string, input: { path: string; title?: string }) => Promise<RemoteWorkspace>\n readonly sessions: (hostId: string, workspaceId?: string) => Promise<SessionsResult>\n readonly webUrl: (hostId: string) => Promise<WebUrlResult>\n readonly snapshot: (hostId: string, sessionId: string, limit?: number) => Promise<SessionSnapshot>\n readonly createSession: (hostId: string, input: { workspaceId?: string; cwd?: string; agentPreset?: string }) => Promise<HostCreateSessionResult>\n readonly openSession: (sessionId: string) => void\n readonly fileList: (sessionId: string, path: string) => Promise<MirrorFileResult>\n readonly fileRead: (sessionId: string, path: string, offset?: number, limit?: number) => Promise<MirrorFileResult>\n readonly fileStat: (sessionId: string, path: string) => Promise<MirrorFileResult>\n readonly fileBytes: (sessionId: string, path: string, offset?: number, length?: number) => Promise<MirrorFileResult>\n readonly prompt: (hostId: string, sessionId: string, text: string, mode?: PromptDeliveryMode, selection?: { provider: string; model: string; reasoningEffort?: string }) => Promise<AcceptedResult>\n readonly cancel: (hostId: string, sessionId: string) => Promise<AcceptedResult>\n}\n\n/** Build the API closure once per plugin mount. */\nexport function createHostsApi(ctx: Context): HostsApi {\n return {\n state: () => call('state', undefined),\n validateTarget: sshTarget => call('target/validate', { sshTarget }),\n addHost: input => call('host/add', input),\n removeHost: async hostId => { await call('host/remove', { hostId }) },\n connect: hostId => call('host/connect', { hostId }),\n disconnect: hostId => call('host/disconnect', { hostId }),\n refresh: hostId => call('host/refresh', { hostId }),\n provision: hostId => call('host/provision', { hostId }),\n workspaces: hostId => call('host/workspaces', { hostId }),\n listDirectories: (hostId, path) => call('host/directory/list', { hostId, ...path === undefined ? {} : { path } }),\n listLocalDirectories: path => call('local/directory/list', { ...path === undefined ? {} : { path } }),\n createLocalWorkspace: input => call('local/workspace/create', input),\n createWorkspace: (hostId, input) => call('host/workspace/create', { hostId, ...input }),\n sessions: (hostId, workspaceId) => call('host/sessions', {\n hostId,\n ...workspaceId === undefined ? {} : { workspaceId },\n }),\n webUrl: hostId => call('host/web-url', { hostId }),\n snapshot: (hostId, sessionId, limit) => call('host/session', {\n hostId,\n sessionId,\n ...limit === undefined ? {} : { limit },\n }),\n createSession: (hostId, input) => call('host/session/create', { hostId, ...input }),\n openSession: sessionId => { ctx.uiWorkspace.openSession(sessionId as never) },\n fileList: (sessionId, path) => call('host/files/list', { sessionId, path }),\n fileRead: (sessionId, path, offset, limit) => call('host/files/read', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...limit === undefined ? {} : { limit },\n }),\n fileStat: (sessionId, path) => call('host/files/stat', { sessionId, path }),\n fileBytes: (sessionId, path, offset, length) => call('host/files/bytes', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...length === undefined ? {} : { length },\n }),\n prompt: (hostId, sessionId, text, mode, selection) => call('host/session/prompt', {\n hostId,\n sessionId,\n text,\n ...mode === undefined ? {} : { mode },\n ...selection === undefined ? {} : selection,\n }),\n cancel: (hostId, sessionId) => call('host/session/cancel', { hostId, sessionId }),\n }\n}\n\nasync function call<K extends keyof HostApiPayloads & string>(\n endpoint: K,\n payload: HostApiPayloads[K],\n): Promise<HostApiValues[K]> {\n const response = await fetch(HOST_API_PATH, {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload === undefined ? { endpoint } : { endpoint, payload }),\n })\n if (!response.ok) {\n throw new RemoteUiError('request/invalid', `host API returned HTTP ${response.status}`)\n }\n const result = await response.json() as\n | { ok: true; value: HostApiValues[K] }\n | { ok: false; error: { code: string; message: string; details?: Record<string, unknown> } }\n if (!result.ok) {\n throw new RemoteUiError(result.error.code, result.error.message, result.error.details ?? {})\n }\n return result.value\n}\n", "/**\n * Settings \u2192 \u8FDC\u7A0B\u5F00\u53D1 page.\n *\n * The page is a projection over the host plugin's authenticated RPC channel.\n * It owns only view state (form fields, which host is expanded); every\n * durable fact and every tunnel lives in the host process.\n *\n * @module dsh-remote-local/client/panel\n */\n\nimport { useEffect, useMemo, useState } from 'react'\nimport type { RemoteSessionSummary, RemoteWorkspace } from '@dsh-remote/protocol'\nimport type { HostView } from '../hosts/types.ts'\nimport type { RemoteHostsState } from '../host-api/contract.ts'\nimport type { HostsApi } from './api.ts'\nimport type { CopyKey, Translate } from './locale.ts'\nimport panelCss from './panel.css'\n\n/** Props injected by the Settings page. */\ninterface PanelProps {\n readonly api: HostsApi\n readonly t: Translate\n}\n\nfunction phaseLabel(t: Translate, phase: HostView['phase']): string {\n return t(`phase.${phase}` as CopyKey)\n}\n\nconst DETAIL_KEYS: Record<string, CopyKey> = {\n '\u6B63\u5728\u901A\u8FC7 SSH \u53D1\u73B0\u8FDC\u7AEF agent\u2026': 'detail.discovering',\n '\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026': 'detail.installing',\n '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026': 'detail.connecting',\n '\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026': 'detail.reprovisioning',\n 'SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026': 'detail.reconnecting',\n '\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026': 'detail.preparing',\n '\u6B63\u5728\u4E0A\u4F20\u8FDC\u7AEF agent \u5B89\u88C5\u5305\u2026': 'detail.uploadingAgent',\n '\u5B89\u88C5\u5305\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5F00\u59CB\u8FDC\u7AEF\u5B89\u88C5\u2026': 'detail.uploaded',\n '\u6B63\u5728\u4E0B\u8F7D Node.js\u2026': 'detail.nodeDownloading',\n 'Node.js \u5B89\u88C5\u5B8C\u6210\uFF0C\u7EE7\u7EED\u5B89\u88C5 pnpm/DSH\u2026': 'detail.nodeInstalled',\n 'Node.js \u5DF2\u5C31\u7EEA\uFF0C\u51C6\u5907\u5B89\u88C5 pnpm/DSH\u2026': 'detail.nodeReady',\n '\u6B63\u5728\u5B89\u88C5 pnpm\u2026': 'detail.pnpmInstalling',\n '\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF DSH\uFF08\u4E0B\u8F7D\u4F9D\u8D56\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF09\u2026': 'detail.dshInstalling',\n '\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF agent\u2026': 'detail.agentInstalling',\n '\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026': 'detail.webStarting',\n '\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u6587\u4EF6\u2026': 'detail.waitingState',\n '\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u2026': 'detail.waitingAgent',\n '\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026': 'detail.agentReady',\n '\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\u2026': 'detail.installed',\n '\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u7B49\u5F85 DSH web \u542F\u52A8\u2026': 'detail.waitingWeb',\n '\u4ECD\u5728\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026': 'detail.stillWaiting',\n '\u6B63\u5728\u8BFB\u53D6\u8FDC\u7AEF agent \u72B6\u6001\u2026': 'detail.readingState',\n '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u2026': 'detail.tunnel',\n '\u6B63\u5728\u6821\u9A8C\u8FDC\u7AEF agent\u2026': 'detail.checkingAgent',\n '\u6B63\u5728\u540C\u6B65\u8FDC\u7AEF\u6A21\u578B\u73AF\u5883\u2026': 'detail.reconcilingModel',\n '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1': 'detail.connectedProxy',\n '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek': 'detail.connectedDirect',\n '\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key': 'detail.connectedRemote',\n}\n\nfunction detailLabel(t: Translate, detail: string): string {\n const key = DETAIL_KEYS[detail]\n return key === undefined ? detail : t(key)\n}\n\nconst POLL_MS = 2_500\n\n/** Render the remote-development settings page. */\nexport function RemoteDevelopmentPanel({ api, t }: PanelProps) {\n const [state, setState] = useState<RemoteHostsState | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [notice, setNotice] = useState<string | null>(null)\n const [busy, setBusy] = useState<string | null>(null)\n const [expandedHost, setExpandedHost] = useState<string | null>(null)\n const [workspaces, setWorkspaces] = useState<Record<string, RemoteWorkspace[] | undefined>>({})\n const [openWorkspace, setOpenWorkspace] = useState<Record<string, boolean>>({})\n const [sessions, setSessions] = useState<Record<string, RemoteSessionSummary[] | undefined>>({})\n const [workspaceDraft, setWorkspaceDraft] = useState('')\n const [form, setForm] = useState({\n sshTarget: '',\n label: '',\n })\n\n useInjectedStyle(panelCss)\n\n const hosts = useMemo(() => state?.hosts ?? [], [state])\n\n useEffect(() => {\n let cancelled = false\n const load = async (): Promise<void> => {\n try {\n const next = await api.state()\n if (!cancelled) {\n setState(next)\n setError(current => current === t('settings.unavailable') ? null : current)\n }\n } catch (failure) {\n if (!cancelled) setError(messageOf(failure, t('settings.unavailable')))\n }\n }\n void load()\n const timer = window.setInterval(() => { void load() }, POLL_MS)\n const onFocus = (): void => { void load() }\n window.addEventListener('focus', onFocus)\n return () => {\n cancelled = true\n window.clearInterval(timer)\n window.removeEventListener('focus', onFocus)\n }\n }, [api, t])\n\n useEffect(() => {\n const host = hosts.find(item => item.id === expandedHost)\n if (host?.phase === 'connected' && workspaces[host.id] === undefined) {\n void loadWorkspaces(host.id)\n }\n }, [hosts, expandedHost, workspaces])\n\n async function refresh(): Promise<void> {\n setState(await api.state())\n }\n\n async function run(key: string, task: () => Promise<void>): Promise<void> {\n setBusy(key)\n setError(null)\n setNotice(null)\n try {\n await task()\n await refresh()\n } catch (failure) {\n setError(messageOf(failure, t('settings.operationFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n async function onAdd(): Promise<void> {\n const sshTarget = form.sshTarget.trim()\n if (sshTarget === '') {\n setError(t('settings.sshRequired'))\n return\n }\n await run('add', async () => {\n await api.validateTarget(sshTarget)\n const host = await api.addHost({\n sshTarget,\n ...form.label.trim() === '' ? {} : { label: form.label.trim() },\n autoConnect: true,\n })\n setForm({ sshTarget: '', label: '' })\n setExpandedHost(host.id)\n setNotice(t('settings.hostSaved'))\n })\n }\n\n async function toggleHost(host: HostView): Promise<void> {\n if (expandedHost === host.id) {\n setExpandedHost(null)\n return\n }\n setExpandedHost(host.id)\n await loadWorkspaces(host.id)\n }\n\n async function loadWorkspaces(hostId: string): Promise<void> {\n setWorkspaces(current => ({ ...current, [hostId]: undefined }))\n try {\n const result = await api.workspaces(hostId)\n setWorkspaces(current => ({ ...current, [hostId]: [...result.workspaces] }))\n } catch (failure) {\n setWorkspaces(current => ({ ...current, [hostId]: [] }))\n setError(messageOf(failure, t('settings.readWorkspacesFailed')))\n }\n }\n\n async function toggleWorkspace(hostId: string, workspaceId: string): Promise<void> {\n const key = `${hostId}:${workspaceId}`\n const next = !(openWorkspace[key] ?? false)\n setOpenWorkspace(current => ({ ...current, [key]: next }))\n if (!next || sessions[key] !== undefined) return\n setSessions(current => ({ ...current, [key]: undefined }))\n try {\n const result = await api.sessions(hostId, workspaceId)\n setSessions(current => ({ ...current, [key]: [...result.sessions] }))\n } catch (failure) {\n setSessions(current => ({ ...current, [key]: [] }))\n setError(messageOf(failure, t('settings.readSessionsFailed')))\n }\n }\n\n async function addWorkspace(host: HostView): Promise<void> {\n const path = workspaceDraft.trim()\n if (path === '') {\n setError(t('settings.pathRequired'))\n return\n }\n setBusy(`workspace:${host.id}`)\n setError(null)\n try {\n await api.createWorkspace(host.id, { path })\n setWorkspaceDraft('')\n await loadWorkspaces(host.id)\n setExpandedHost(host.id)\n } catch (failure) {\n setError(messageOf(failure, t('settings.addWorkspaceFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n\n\n async function newSession(host: HostView, workspaceId: string): Promise<void> {\n setBusy(`create:${host.id}:${workspaceId}`)\n setError(null)\n try {\n const created = await api.createSession(host.id, { workspaceId })\n const key = `${host.id}:${workspaceId}`\n setOpenWorkspace(current => ({ ...current, [key]: true }))\n if (created.localSessionId !== undefined) {\n api.openSession(created.localSessionId)\n setNotice(t('settings.sessionOpening'))\n window.setTimeout(() => {\n document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))\n }, 80)\n } else {\n setNotice(t('settings.sessionSyncing'))\n }\n const result = await api.sessions(host.id, workspaceId)\n setSessions(current => ({ ...current, [key]: [...result.sessions] }))\n const refreshed = await api.workspaces(host.id)\n setWorkspaces(current => ({ ...current, [host.id]: [...refreshed.workspaces] }))\n } catch (failure) {\n setError(messageOf(failure, t('settings.createSessionFailed')))\n } finally {\n setBusy(null)\n }\n }\n\n\n\n async function onRemove(host: HostView): Promise<void> {\n if (!window.confirm(`${t('settings.confirmRemovePrefix')}${host.label}${t('settings.confirmRemoveSuffix')}`)) return\n await run(`remove:${host.id}`, async () => {\n await api.removeHost(host.id)\n if (expandedHost === host.id) setExpandedHost(null)\n })\n }\n\n return (\n <div className=\"dshr-root\">\n <header className=\"dshr-header\">\n <div>\n <h2>{t('settings.title')}</h2>\n <p className=\"dshr-subtitle\">\n {t('settings.subtitle.lead')}\n {' '}\n {t('settings.subtitle.body')}\n </p>\n </div>\n <button\n type=\"button\"\n className=\"dshr-button\"\n onClick={() => { void refresh().catch(failure => setError(messageOf(failure, t('settings.refreshFailed')))) }}\n disabled={busy !== null}\n >\n {t('settings.refresh')}\n </button>\n </header>\n\n {error !== null && <p className=\"dshr-banner dshr-banner-error\" role=\"alert\">{error}</p>}\n {notice !== null && <p className=\"dshr-banner dshr-banner-info\" role=\"status\">{notice}</p>}\n\n <section className=\"dshr-card\">\n <h3>{t('settings.addHost')}</h3>\n <form\n className=\"dshr-form\"\n onSubmit={event => {\n event.preventDefault()\n void onAdd()\n }}\n >\n <label className=\"dshr-field dshr-field-wide\">\n <span>{t('settings.sshTarget')}</span>\n <input\n value={form.sshTarget}\n onChange={event => setForm(current => ({ ...current, sshTarget: event.target.value }))}\n placeholder={t('settings.sshPlaceholder')}\n autoComplete=\"off\"\n spellCheck={false}\n />\n </label>\n <label className=\"dshr-field\">\n <span>{t('settings.label')}</span>\n <input\n value={form.label}\n onChange={event => setForm(current => ({ ...current, label: event.target.value }))}\n placeholder={t('settings.labelPlaceholder')}\n />\n </label>\n <div className=\"dshr-form-actions\">\n <button type=\"submit\" className=\"dshr-button dshr-button-primary\" disabled={busy !== null}>\n {busy === 'add' ? t('settings.adding') : t('settings.saveConnect')}\n </button>\n <span className=\"dshr-hint\">{t('settings.sshHint')}</span>\n </div>\n </form>\n </section>\n\n <section className=\"dshr-card\">\n <div className=\"dshr-section-title\">\n <h3>{t('settings.hosts')}</h3>\n <span className=\"dshr-hint\">{hosts.length} {t('settings.hostsUnit')}</span>\n </div>\n {hosts.length === 0 && <p className=\"dshr-empty\">{t('settings.noHosts')}</p>}\n <div className=\"dshr-hosts\">\n {hosts.map(host => (\n <article key={host.id} className=\"dshr-host\">\n <header className=\"dshr-host-head\">\n <div className=\"dshr-host-title\">\n <strong>{host.label}</strong>\n <code>{host.sshTarget}</code>\n </div>\n <span className={`dshr-phase dshr-phase-${host.phase}`}>{phaseLabel(t, host.phase)}</span>\n </header>\n\n <dl className=\"dshr-meta\">\n <div><dt>{t('settings.remotePort')}</dt><dd>{host.remoteAgentPort ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.localTunnel')}</dt><dd>{host.localPort ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.workspaces')}</dt><dd>{host.workspaceCount ?? '\u2014'}</dd></div>\n <div><dt>Agent</dt><dd>{host.hostInfo?.agentVersion ?? '\u2014'}</dd></div>\n <div><dt>{t('settings.token')}</dt><dd>{host.hasToken ? t('settings.saved') : t('settings.unsaved')}</dd></div>\n <div><dt>{t('settings.modelProxy')}</dt><dd>{host.modelProxy ? t('settings.localProxy') : host.modelDirect ? t('settings.direct') : t('settings.remoteDefault')}</dd></div>\n </dl>\n\n {host.detail !== null && <p className=\"dshr-detail\">{detailLabel(t, host.detail)}</p>}\n {host.error !== null && <p className=\"dshr-host-error\" role=\"alert\">{host.error}</p>}\n\n <div className=\"dshr-actions\">\n {host.phase === 'connected'\n ? (\n <>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void toggleHost(host) }}\n >\n {expandedHost === host.id ? t('settings.collapseWorkspaces') : t('settings.expandWorkspaces')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`refresh:${host.id}`, async () => { await api.refresh(host.id) }) }}\n >\n {t('settings.rediscover')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`disconnect:${host.id}`, async () => { await api.disconnect(host.id) }) }}\n >\n {t('settings.disconnect')}\n </button>\n </>\n )\n : (\n <>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-primary\"\n disabled={busy !== null || host.phase === 'installing' || host.phase === 'discovering' || host.phase === 'connecting'}\n onClick={() => { void run(`connect:${host.id}`, async () => { await api.connect(host.id) }) }}\n >\n {host.phase === 'installing' ? t('settings.installing') : host.phase === 'connecting' ? t('settings.connecting') : t('settings.connect')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null}\n onClick={() => { void run(`refresh:${host.id}`, async () => { await api.refresh(host.id) }) }}\n >\n {t('settings.rediscover')}\n </button>\n </>\n )}\n <button\n type=\"button\"\n className=\"dshr-button\"\n disabled={busy !== null || host.phase === 'installing'}\n onClick={() => { void run(`provision:${host.id}`, async () => { await api.provision(host.id) }) }}\n >\n {t('settings.reprovision')}\n </button>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-danger\"\n disabled={busy !== null}\n onClick={() => { void onRemove(host) }}\n >\n {t('settings.remove')}\n </button>\n </div>\n\n {expandedHost === host.id && (\n <div className=\"dshr-workspaces\">\n <div className=\"dshr-workspace-add\">\n <input\n value={workspaceDraft}\n onChange={event => setWorkspaceDraft(event.target.value)}\n placeholder={t('settings.workspacePathPlaceholder')}\n spellCheck={false}\n onKeyDown={event => {\n if (event.key === 'Enter') {\n event.preventDefault()\n void addWorkspace(host)\n }\n }}\n />\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-small\"\n disabled={busy !== null}\n onClick={() => { void addWorkspace(host) }}\n >\n {busy === `workspace:${host.id}` ? t('settings.addingWorkspace') : t('settings.addWorkspace')}\n </button>\n </div>\n {workspaces[host.id] === undefined\n ? <p className=\"dshr-empty\">{t('settings.readingWorkspaces')}</p>\n : workspaces[host.id]?.length === 0\n ? <p className=\"dshr-empty\">{t('settings.noWorkspaces')}</p>\n : workspaces[host.id]?.map(workspace => {\n const workspaceKey = `${host.id}:${workspace.workspaceId}`\n const open = openWorkspace[workspaceKey] ?? false\n return (\n <div key={workspace.workspaceId} className=\"dshr-workspace\">\n <div className=\"dshr-workspace-row\">\n <button\n type=\"button\"\n className=\"dshr-workspace-head\"\n aria-expanded={open}\n onClick={() => { void toggleWorkspace(host.id, workspace.workspaceId) }}\n >\n <span className=\"dshr-caret\">{open ? '\u25BE' : '\u25B8'}</span>\n <span>{workspace.title}</span>\n <code>{workspace.path}</code>\n <span className=\"dshr-count\">{workspace.sessionIds.length} {t('settings.sessionsUnit')}</span>\n </button>\n <button\n type=\"button\"\n className=\"dshr-button dshr-button-small\"\n disabled={busy !== null}\n onClick={() => { void newSession(host, workspace.workspaceId) }}\n >\n {busy === `create:${host.id}:${workspace.workspaceId}` ? t('settings.creating') : t('settings.newSession')}\n </button>\n </div>\n {open && (\n <div className=\"dshr-sessions\">\n {sessions[workspaceKey] === undefined\n ? <p className=\"dshr-empty\">{t('settings.readingSessions')}</p>\n : sessions[workspaceKey]?.length === 0\n ? <p className=\"dshr-empty\">{t('settings.noSessions')}</p>\n : sessions[workspaceKey]?.map(session => (\n <div key={session.sessionId} className=\"dshr-session\">\n <span className={session.live ? 'dshr-live' : 'dshr-idle'} />\n <code>{session.sessionId}</code>\n <span>{session.cwd ?? '\u2014'}</span>\n <span className=\"dshr-count\">{session.eventCount ?? '\u2014'} {t('settings.eventsUnit')}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </article>\n ))}\n </div>\n </section>\n\n </div>\n )\n}\n\nfunction useInjectedStyle(css: string): void {\n useEffect(() => {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-remote-local'\n tag.textContent = css\n document.head.appendChild(tag)\n return () => { tag.remove() }\n }, [css])\n}\n\nfunction messageOf(value: unknown, fallback: string): string {\n return value instanceof Error && value.message !== '' ? value.message : fallback\n}\n", ".dshr-root {\n --dshr-accent: #4f7cff;\n --dshr-danger: #d24b4b;\n --dshr-success: #2f9e63;\n --dshr-warning: #c98417;\n --dshr-border: color-mix(in srgb, currentColor 14%, transparent);\n --dshr-soft: color-mix(in srgb, currentColor 4%, transparent);\n --dshr-softer: color-mix(in srgb, currentColor 2.5%, transparent);\n display: flex;\n flex-direction: column;\n gap: 16px;\n padding: 4px 2px 32px;\n font-size: 13px;\n line-height: 1.5;\n}\n\n.dshr-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.dshr-header h2 {\n margin: 0 0 4px;\n font-size: 17px;\n font-weight: 650;\n}\n\n.dshr-subtitle {\n max-width: 760px;\n margin: 0;\n opacity: 0.72;\n}\n\n.dshr-subtitle code,\n.dshr-host-title code,\n.dshr-workspace-head code,\n.dshr-session code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 0.92em;\n}\n\n.dshr-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border: 1px solid var(--dshr-border);\n border-radius: 12px;\n background: var(--dshr-softer);\n}\n\n.dshr-card h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 650;\n}\n\n.dshr-section-title {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n}\n\n.dshr-form {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n}\n\n.dshr-field {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n\n.dshr-field-wide {\n grid-column: 1 / -1;\n}\n\n.dshr-field > span {\n font-size: 12px;\n opacity: 0.72;\n}\n\n.dshr-field input {\n width: 100%;\n box-sizing: border-box;\n padding: 7px 9px;\n border: 1px solid var(--dshr-border);\n border-radius: 8px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n outline: none;\n}\n\n.dshr-field input:focus {\n border-color: var(--dshr-accent);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dshr-accent) 22%, transparent);\n}\n\n.dshr-check {\n display: flex;\n align-items: center;\n gap: 7px;\n font-size: 12px;\n}\n\n.dshr-form-actions {\n display: flex;\n grid-column: 1 / -1;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.dshr-button {\n padding: 6px 12px;\n border: 1px solid var(--dshr-border);\n border-radius: 8px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.dshr-button:hover:not(:disabled) {\n background: color-mix(in srgb, currentColor 8%, transparent);\n}\n\n.dshr-button:disabled {\n cursor: not-allowed;\n opacity: 0.45;\n}\n\n.dshr-button-primary {\n border-color: color-mix(in srgb, var(--dshr-accent) 65%, transparent);\n background: var(--dshr-accent);\n color: #fff;\n}\n\n.dshr-button-primary:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dshr-accent) 84%, #000);\n}\n\n.dshr-button-danger {\n border-color: color-mix(in srgb, var(--dshr-danger) 45%, transparent);\n color: var(--dshr-danger);\n}\n\n.dshr-hint {\n font-size: 12px;\n opacity: 0.62;\n}\n\n.dshr-empty {\n margin: 0;\n padding: 10px 0;\n opacity: 0.62;\n}\n\n.dshr-banner {\n margin: 0;\n padding: 9px 12px;\n border-radius: 9px;\n border: 1px solid var(--dshr-border);\n}\n\n.dshr-banner-error {\n border-color: color-mix(in srgb, var(--dshr-danger) 45%, transparent);\n background: color-mix(in srgb, var(--dshr-danger) 10%, transparent);\n color: var(--dshr-danger);\n}\n\n.dshr-banner-info {\n border-color: color-mix(in srgb, var(--dshr-accent) 35%, transparent);\n background: color-mix(in srgb, var(--dshr-accent) 9%, transparent);\n}\n\n.dshr-hosts {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.dshr-host {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n border: 1px solid var(--dshr-border);\n border-radius: 10px;\n background: var(--dshr-soft);\n}\n\n.dshr-host-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n\n.dshr-host-title {\n display: flex;\n align-items: baseline;\n gap: 10px;\n flex-wrap: wrap;\n min-width: 0;\n}\n\n.dshr-host-title strong {\n font-size: 14px;\n}\n\n.dshr-phase {\n flex: none;\n padding: 1px 8px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n border: 1px solid currentColor;\n opacity: 0.9;\n}\n\n.dshr-phase-connected {\n color: var(--dshr-success);\n}\n\n.dshr-phase-discovering,\n.dshr-phase-connecting {\n color: var(--dshr-warning);\n}\n\n.dshr-phase-error {\n color: var(--dshr-danger);\n}\n\n.dshr-phase-idle,\n.dshr-phase-disconnected {\n opacity: 0.62;\n}\n\n.dshr-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 6px 20px;\n margin: 0;\n font-size: 12px;\n}\n\n.dshr-meta > div {\n display: flex;\n gap: 5px;\n}\n\n.dshr-meta dt {\n opacity: 0.58;\n}\n\n.dshr-meta dd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n\n.dshr-host-error {\n margin: 0;\n color: var(--dshr-danger);\n font-size: 12px;\n overflow-wrap: anywhere;\n}\n\n.dshr-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.dshr-workspaces {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding-top: 4px;\n border-top: 1px dashed var(--dshr-border);\n}\n\n.dshr-workspace {\n border-radius: 8px;\n background: var(--dshr-softer);\n}\n\n.dshr-workspace-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n width: 100%;\n box-sizing: border-box;\n padding: 8px 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n flex-wrap: wrap;\n}\n\n.dshr-workspace-head:hover {\n background: var(--dshr-soft);\n}\n\n.dshr-workspace-head code {\n opacity: 0.6;\n}\n\n.dshr-caret {\n width: 12px;\n opacity: 0.6;\n}\n\n.dshr-count {\n margin-left: auto;\n font-size: 11px;\n opacity: 0.55;\n white-space: nowrap;\n}\n\n.dshr-sessions {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 0 10px 8px 30px;\n}\n\n.dshr-session {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 3px 0;\n font-size: 12px;\n flex-wrap: wrap;\n}\n\n.dshr-session > span:not(.dshr-live):not(.dshr-idle) {\n opacity: 0.62;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.dshr-live,\n.dshr-idle {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n transform: translateY(-1px);\n}\n\n.dshr-live {\n background: var(--dshr-success);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--dshr-success) 20%, transparent);\n}\n\n.dshr-idle {\n background: color-mix(in srgb, currentColor 28%, transparent);\n}\n\n@media (max-width: 720px) {\n .dshr-form {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .dshr-header {\n flex-direction: column;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dshr-button {\n transition: none;\n }\n}\n\n.dshr-detail {\n margin: 0;\n font-size: 12px;\n color: var(--dshr-warning);\n}\n\n.dshr-workspace-row {\n display: flex;\n align-items: center;\n gap: 6px;\n padding-right: 6px;\n}\n\n.dshr-workspace-row .dshr-workspace-head {\n flex: 1;\n}\n\n.dshr-button-small {\n flex: none;\n padding: 3px 8px;\n font-size: 11px;\n}\n\n.dshr-workspace-add {\n display: flex;\n gap: 6px;\n padding: 4px 0 6px;\n}\n\n.dshr-workspace-add input {\n flex: 1;\n min-width: 0;\n padding: 5px 8px;\n border: 1px solid var(--dshr-border);\n border-radius: 7px;\n background: var(--dshr-soft);\n color: inherit;\n font: inherit;\n font-size: 12px;\n outline: none;\n}\n\n.dshr-workspace-add input:focus {\n border-color: var(--dshr-accent);\n}\n", "/**\n * Route ordinary sidebar \"new session\" actions for mirrored workspaces to the\n * remote agent instead of creating a local Agent in the mirror anchor.\n *\n * The Settings page has its own `+ \u65B0\u4F1A\u8BDD` flow. This adapter covers the\n * standard workspace-row action so the native sidebar behaves the same way.\n *\n * @module dsh-remote-local/client/routing\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostsApi } from './api.ts'\n\ninterface StartSessionOptions {\n readonly agentPreset?: string\n}\n\ninterface WorkspaceService {\n startSession(workspaceId?: unknown, options?: StartSessionOptions): void\n}\n\n/** Install the sidebar routing adapter once per client mount. */\nexport function installNativeWorkspaceRouting(ctx: Context, api: HostsApi): void {\n const workspace = ctx.uiWorkspace as unknown as WorkspaceService | undefined\n if (workspace === undefined || typeof workspace.startSession !== 'function') return\n const original = workspace.startSession.bind(workspace)\n workspace.startSession = (workspaceId, options) => {\n if (workspaceId === undefined) {\n original(workspaceId, options)\n return\n }\n void route(api, String(workspaceId)).then(routed => {\n if (!routed) original(workspaceId, options)\n }).catch(() => { original(workspaceId, options) })\n }\n}\n\nasync function route(api: HostsApi, localWorkspaceId: string): Promise<boolean> {\n const state = await api.state()\n for (const host of state.hosts) {\n if (host.phase !== 'connected') continue\n const result = await api.workspaces(host.id)\n const workspace = result.workspaces.find(item => item.localWorkspaceId === localWorkspaceId)\n if (workspace === undefined) continue\n const created = await api.createSession(host.id, { workspaceId: workspace.workspaceId })\n if (created.localSessionId !== undefined) {\n api.openSession(created.localSessionId)\n return true\n }\n }\n return false\n}\n", "/**\n * Client proxy for workspace-files calls on mirrored Sessions.\n *\n * The Host's generated Remote methods bind the stock workspace-files service\n * before an instance adapter can replace them. The sidebar tree and document\n * preview therefore call this proxy, which asks the local plugin's host API to\n * resolve the mirror and read the remote workspace.\n *\n * @module dsh-remote-local/client/remote-files\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostsApi } from './api.ts'\n\ninterface RemoteFace {\n list(sessionId: string, path: string, signal: AbortSignal): Promise<unknown>\n read(sessionId: string, path: string, range: { offset?: number; limit?: number }, signal: AbortSignal): Promise<unknown>\n stat(sessionId: string, path: string, signal: AbortSignal): Promise<unknown>\n readBytes(sessionId: string, path: string, options: { range?: { offset?: number; length?: number }; baseFile?: string }, signal: AbortSignal): Promise<unknown>\n changes(sessionId: string, path: string, signal: AbortSignal): AsyncIterable<unknown>\n}\n\n/** Install the proxy on the client Remote face. */\nexport function installRemoteFiles(ctx: Context, api: HostsApi): void {\n const remote = ctx.remote as unknown as { workspaceFiles?: RemoteFace } | undefined\n const face = remote?.workspaceFiles\n if (face === undefined) return\n\n const original = {\n list: face.list.bind(face),\n read: face.read.bind(face),\n stat: face.stat.bind(face),\n readBytes: face.readBytes.bind(face),\n }\n\n face.list = async (sessionId, path, signal) => {\n const result = await api.fileList(sessionId, path)\n return result.mirrored ? { ok: true, value: result.value } : original.list(sessionId, path, signal)\n }\n face.read = async (sessionId, path, range, signal) => {\n const result = await api.fileRead(sessionId, path, range.offset, range.limit)\n return result.mirrored ? { ok: true, value: result.value } : original.read(sessionId, path, range, signal)\n }\n face.stat = async (sessionId, path, signal) => {\n const result = await api.fileStat(sessionId, path)\n return result.mirrored ? { ok: true, value: result.value } : original.stat(sessionId, path, signal)\n }\n face.readBytes = async (sessionId, path, options, signal) => {\n const result = await api.fileBytes(sessionId, path, options.range?.offset, options.range?.length)\n return result.mirrored ? { ok: true, value: result.value } : original.readBytes(sessionId, path, options, signal)\n }\n}\n", "/**\n * Client copy for the remote-development surfaces.\n *\n * The dictionaries are registered into DSH's locale service; `ctx.locale.bind`\n * follows the language selected in DSH. Settings components also receive the\n * framework `t` seat, so language switches re-render them.\n */\n\nexport const NS = 'dshRemote'\n\nexport const zh = {\n 'settings.title': '\u8FDC\u7A0B\u5F00\u53D1',\n 'settings.subtitle.lead': '\u53EA\u9700\u586B\u5199 SSH \u76EE\u6807\u3002\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u8FDC\u7AEF agent\uFF0C\u5EFA\u7ACB\u96A7\u9053\u3002',\n 'settings.subtitle.body': '\u8FDE\u63A5\u540E\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u548C\u4F1A\u8BDD\u4F1A\u540C\u6B65\u5230\u5DE6\u4FA7\uFF0C\u50CF\u672C\u5730\u4F1A\u8BDD\u4E00\u6837\u6253\u5F00\uFF1B\u6A21\u578B\u51ED\u636E\u9ED8\u8BA4\u5199\u5165\u8FDC\u7AEF\u670D\u52A1\u73AF\u5883\uFF1B\u4E5F\u53EF\u542F\u7528\u672C\u5730\u4EE3\u7406\u6A21\u5F0F\u3002',\n 'settings.refresh': '\u5237\u65B0',\n 'settings.addHost': '\u6DFB\u52A0\u4E3B\u673A',\n 'settings.sshTarget': 'SSH \u76EE\u6807',\n 'settings.sshPlaceholder': 'user@host \u6216 ssh://user@host:22',\n 'settings.label': '\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09',\n 'settings.labelPlaceholder': '\u53EF\u9009\uFF0C\u4F8B\u5982 \u5F00\u53D1\u673A',\n 'settings.saveConnect': '\u4FDD\u5B58\u5E76\u8FDE\u63A5',\n 'settings.adding': '\u6DFB\u52A0\u4E2D\u2026',\n 'settings.sshHint': '\u9700\u8981\u5BC6\u94A5\u6216 ssh-agent \u514D\u4EA4\u4E92\u767B\u5F55\uFF1B\u9996\u6B21\u81EA\u52A8\u5B89\u88C5\u7EA6 1\u20133 \u5206\u949F\u3002',\n 'settings.hosts': '\u4E3B\u673A',\n 'settings.hostsUnit': '\u53F0',\n 'settings.noHosts': '\u8FD8\u6CA1\u6709\u8FDC\u7A0B\u4E3B\u673A\u3002\u5148\u5728\u4E0A\u65B9\u6DFB\u52A0\u4E00\u53F0\u3002',\n 'settings.refreshFailed': '\u5237\u65B0\u5931\u8D25',\n 'settings.unavailable': '\u65E0\u6CD5\u8FDE\u63A5\u672C\u5730\u8FDC\u7A0B\u5BBF\u4E3B\u670D\u52A1',\n 'settings.operationFailed': '\u64CD\u4F5C\u5931\u8D25',\n 'settings.sshRequired': '\u8BF7\u586B\u5199 SSH \u76EE\u6807\uFF0C\u4F8B\u5982 user@host \u6216 ssh://user@host:22',\n 'settings.hostSaved': '\u4E3B\u673A\u5DF2\u4FDD\u5B58\u3002\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8\u8FDC\u7AEF agent\uFF1B\u5B89\u88C5\u8FDB\u5EA6\u4F1A\u663E\u793A\u5728\u4E3B\u673A\u5361\u7247\u4E0A\u3002',\n 'settings.readWorkspacesFailed': '\u8BFB\u53D6\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'settings.readSessionsFailed': '\u8BFB\u53D6\u8FDC\u7AEF\u4F1A\u8BDD\u5931\u8D25',\n 'settings.pathRequired': '\u8BF7\u586B\u5199\u8FDC\u7AEF\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/user/project',\n 'settings.addWorkspaceFailed': '\u6DFB\u52A0\u8FDC\u7AEF\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'settings.sessionOpening': '\u8FDC\u7AEF\u4F1A\u8BDD\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u666E\u901A\u4F1A\u8BDD\u754C\u9762\u6253\u5F00\u2026',\n 'settings.sessionSyncing': '\u8FDC\u7AEF\u4F1A\u8BDD\u5DF2\u521B\u5EFA\uFF0C\u6B63\u5728\u540C\u6B65\u5230\u4FA7\u8FB9\u680F\u2026',\n 'settings.createSessionFailed': '\u521B\u5EFA\u8FDC\u7AEF\u4F1A\u8BDD\u5931\u8D25',\n 'settings.confirmRemovePrefix': '\u5220\u9664\u4E3B\u673A\u300C',\n 'settings.confirmRemoveSuffix': '\u300D\uFF1F\u8FDC\u7AEF\u6587\u4EF6\u4E0E\u4F1A\u8BDD\u4E0D\u4F1A\u88AB\u5220\u9664\u3002',\n 'settings.remotePort': '\u8FDC\u7AEF\u7AEF\u53E3',\n 'settings.localTunnel': '\u672C\u5730\u96A7\u9053',\n 'settings.workspaces': '\u5DE5\u4F5C\u533A',\n 'settings.token': 'Token',\n 'settings.saved': '\u5DF2\u4FDD\u5B58',\n 'settings.unsaved': '\u672A\u4FDD\u5B58',\n 'settings.modelProxy': '\u6A21\u578B\u4EE3\u7406',\n 'settings.localProxy': '\u672C\u5730\u4EE3\u7406',\n 'settings.direct': '\u8FDC\u7AEF\u76F4\u8FDE',\n 'settings.remoteDefault': '\u8FDC\u7AEF\u9ED8\u8BA4',\n 'settings.collapseWorkspaces': '\u6536\u8D77\u5DE5\u4F5C\u533A',\n 'settings.expandWorkspaces': '\u5DE5\u4F5C\u533A',\n 'settings.rediscover': '\u91CD\u65B0\u53D1\u73B0',\n 'settings.disconnect': '\u65AD\u5F00',\n 'settings.connect': '\u8FDE\u63A5',\n 'settings.installing': '\u6B63\u5728\u5B89\u88C5\u2026',\n 'settings.connecting': '\u6B63\u5728\u8FDE\u63A5\u2026',\n 'settings.reprovision': '\u91CD\u65B0\u5B89\u88C5',\n 'settings.remove': '\u5220\u9664',\n 'settings.workspacePathPlaceholder': '\u8FDC\u7AEF\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/user/project',\n 'settings.addWorkspace': '\u6DFB\u52A0\u5DE5\u4F5C\u533A',\n 'settings.addingWorkspace': '\u6DFB\u52A0\u4E2D\u2026',\n 'settings.readingWorkspaces': '\u6B63\u5728\u8BFB\u53D6\u5DE5\u4F5C\u533A\u2026',\n 'settings.noWorkspaces': '\u8FDC\u7AEF\u8FD8\u6CA1\u6709\u6CE8\u518C\u5DE5\u4F5C\u533A\u3002\u53EF\u5728\u8FDC\u7AEF DSH \u91CC\u6DFB\u52A0\u76EE\u5F55\u540E\u5237\u65B0\u3002',\n 'settings.sessionsUnit': '\u4E2A\u4F1A\u8BDD',\n 'settings.newSession': '+ \u65B0\u4F1A\u8BDD',\n 'settings.creating': '\u521B\u5EFA\u4E2D\u2026',\n 'settings.readingSessions': '\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u2026',\n 'settings.noSessions': '\u8BE5\u5DE5\u4F5C\u533A\u6682\u65E0\u4F1A\u8BDD\u3002\u70B9\u201C+ \u65B0\u4F1A\u8BDD\u201D\u5F00\u59CB\u3002',\n 'settings.eventsUnit': '\u4E8B\u4EF6',\n 'phase.idle': '\u672A\u8FDE\u63A5',\n 'phase.discovering': '\u53D1\u73B0\u4E2D',\n 'phase.installing': '\u5B89\u88C5\u4E2D',\n 'phase.connecting': '\u8FDE\u63A5\u4E2D',\n 'phase.connected': '\u5DF2\u8FDE\u63A5',\n 'phase.disconnected': '\u5DF2\u65AD\u5F00',\n 'phase.error': '\u5F02\u5E38',\n 'detail.discovering': '\u6B63\u5728\u901A\u8FC7 SSH \u53D1\u73B0\u8FDC\u7AEF agent\u2026',\n 'detail.installing': '\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026',\n 'detail.connecting': '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026',\n 'detail.reprovisioning': '\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026',\n 'detail.reconnecting': 'SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026',\n 'detail.connectedProxy': '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1',\n 'detail.connectedDirect': '\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek',\n 'detail.connectedRemote': '\u5DF2\u8FDE\u63A5\uFF1B\u8FDC\u7AEF\u4F7F\u7528\u81EA\u8EAB\u51ED\u636E\u6216\u672A\u914D\u7F6E\u6A21\u578B key',\n 'detail.preparing': '\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026',\n 'detail.uploadingAgent': '\u6B63\u5728\u4E0A\u4F20\u8FDC\u7AEF agent \u5B89\u88C5\u5305\u2026',\n 'detail.uploaded': '\u5B89\u88C5\u5305\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5F00\u59CB\u8FDC\u7AEF\u5B89\u88C5\u2026',\n 'detail.nodeDownloading': '\u6B63\u5728\u4E0B\u8F7D Node.js\u2026',\n 'detail.nodeInstalled': 'Node.js \u5B89\u88C5\u5B8C\u6210\uFF0C\u7EE7\u7EED\u5B89\u88C5 pnpm/DSH\u2026',\n 'detail.nodeReady': 'Node.js \u5DF2\u5C31\u7EEA\uFF0C\u51C6\u5907\u5B89\u88C5 pnpm/DSH\u2026',\n 'detail.pnpmInstalling': '\u6B63\u5728\u5B89\u88C5 pnpm\u2026',\n 'detail.dshInstalling': '\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF DSH\uFF08\u4E0B\u8F7D\u4F9D\u8D56\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF09\u2026',\n 'detail.agentInstalling': '\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF agent\u2026',\n 'detail.webStarting': '\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026',\n 'detail.waitingState': '\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u6587\u4EF6\u2026',\n 'detail.waitingAgent': '\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u2026',\n 'detail.agentReady': '\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026',\n 'detail.installed': '\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\u2026',\n 'detail.waitingWeb': '\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u7B49\u5F85 DSH web \u542F\u52A8\u2026',\n 'detail.stillWaiting': '\u4ECD\u5728\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026',\n 'detail.readingState': '\u6B63\u5728\u8BFB\u53D6\u8FDC\u7AEF agent \u72B6\u6001\u2026',\n 'detail.tunnel': '\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u2026',\n 'detail.checkingAgent': '\u6B63\u5728\u6821\u9A8C\u8FDC\u7AEF agent\u2026',\n 'detail.reconcilingModel': '\u6B63\u5728\u540C\u6B65\u8FDC\u7AEF\u6A21\u578B\u73AF\u5883\u2026',\n 'dialog.title': '\u65B0\u5EFA\u5DE5\u4F5C\u533A',\n 'dialog.close': '\u5173\u95ED',\n 'dialog.name': '\u5DE5\u4F5C\u533A\u540D\u79F0',\n 'dialog.namePlaceholder': '\u5DE5\u4F5C\u533A\u540D\u79F0\uFF08\u53EF\u9009\uFF09',\n 'dialog.source': '\u6E90\u76EE\u5F55',\n 'dialog.addToPrefix': '\u5C06\u76EE\u5F55\u6DFB\u52A0\u5230 ',\n 'dialog.addToSuffix': '',\n 'dialog.add': '\u6DFB\u52A0',\n 'dialog.local': '\u672C\u673A',\n 'dialog.remoteDevices': '\u8FDC\u7AEF\u8BBE\u5907',\n 'dialog.addRemote': '+ \u6DFB\u52A0\u8FDC\u7AEF\u2026',\n 'dialog.addRemoteHint': '\u8BF7\u5148\u5728 Settings \u2192 \u8FDC\u7A0B\u5F00\u53D1 \u6DFB\u52A0\u5E76\u8FDE\u63A5\u8FDC\u7AEF\u4E3B\u673A\u3002',\n 'dialog.cancel': '\u53D6\u6D88',\n 'dialog.create': '\u521B\u5EFA\u5DE5\u4F5C\u533A',\n 'dialog.creating': '\u521B\u5EFA\u4E2D\u2026',\n 'dialog.createFailed': '\u65B0\u5EFA\u5DE5\u4F5C\u533A\u5931\u8D25',\n 'dialog.removeFolder': '\u79FB\u9664\u76EE\u5F55',\n 'dialog.selectedFallback': '\u8FDC\u7AEF workspace \u5DF2\u521B\u5EFA\uFF0C\u4F46\u672C\u5730\u955C\u50CF\u8FD8\u6CA1\u6709\u540C\u6B65\u5B8C\u6210',\n 'dialog.folderTitle': '\u9009\u62E9\u6E90\u76EE\u5F55',\n 'dialog.up': '\u4E0A\u4E00\u7EA7',\n 'dialog.selectFolder': '\u9009\u62E9\u5F53\u524D\u76EE\u5F55',\n 'dialog.reading': '\u6B63\u5728\u8BFB\u53D6\u76EE\u5F55\u2026',\n 'dialog.emptyFolder': '\u6B64\u76EE\u5F55\u6CA1\u6709\u5B50\u76EE\u5F55',\n 'dialog.readFailed': '\u8BFB\u53D6\u76EE\u5F55\u5931\u8D25',\n 'dialog.nativePickerFailed': '\u65E0\u6CD5\u6253\u5F00\u7CFB\u7EDF\u76EE\u5F55\u9009\u62E9\u5668',\n 'dialog.useFolder': '\u4F7F\u7528\u6B64\u76EE\u5F55',\n} as const\n\nexport type CopyKey = keyof typeof zh\n\nexport const en: Record<CopyKey, string> = {\n 'settings.title': 'Remote development',\n 'settings.subtitle.lead': 'Enter an SSH target. The plugin installs and starts the remote agent and opens a tunnel.',\n 'settings.subtitle.body': 'Remote workspaces and sessions sync into the normal sidebar and open like local sessions. The model key is written into the remote service environment by default; local proxying remains optional.',\n 'settings.refresh': 'Refresh',\n 'settings.addHost': 'Add host',\n 'settings.sshTarget': 'SSH target',\n 'settings.sshPlaceholder': 'user@host or ssh://user@host:22',\n 'settings.label': 'Display name (optional)',\n 'settings.labelPlaceholder': 'Optional, e.g. dev box',\n 'settings.saveConnect': 'Save and connect',\n 'settings.adding': 'Adding\u2026',\n 'settings.sshHint': 'Requires key or ssh-agent authentication. First install takes about 1\u20133 minutes.',\n 'settings.hosts': 'Hosts',\n 'settings.hostsUnit': '',\n 'settings.noHosts': 'No remote hosts yet. Add one above.',\n 'settings.refreshFailed': 'Refresh failed',\n 'settings.unavailable': 'Cannot reach the local remote-host service',\n 'settings.operationFailed': 'Operation failed',\n 'settings.sshRequired': 'Enter an SSH target, e.g. user@host or ssh://user@host:22',\n 'settings.hostSaved': 'Host saved. The plugin will install and start the remote agent; progress appears on the host card.',\n 'settings.readWorkspacesFailed': 'Could not read remote workspaces',\n 'settings.readSessionsFailed': 'Could not read remote sessions',\n 'settings.pathRequired': 'Enter an absolute remote directory, e.g. /home/user/project',\n 'settings.addWorkspaceFailed': 'Could not add the remote workspace',\n 'settings.sessionOpening': 'Remote session created; opening it in the conversation view\u2026',\n 'settings.sessionSyncing': 'Remote session created; syncing it into the sidebar\u2026',\n 'settings.createSessionFailed': 'Could not create the remote session',\n 'settings.confirmRemovePrefix': 'Remove host \"',\n 'settings.confirmRemoveSuffix': '\"? Remote files and sessions are not deleted.',\n 'settings.remotePort': 'Remote port',\n 'settings.localTunnel': 'Local tunnel',\n 'settings.workspaces': 'Workspaces',\n 'settings.token': 'Token',\n 'settings.saved': 'Saved',\n 'settings.unsaved': 'Not saved',\n 'settings.modelProxy': 'Model credentials',\n 'settings.localProxy': 'Local proxy',\n 'settings.direct': 'Remote direct',\n 'settings.remoteDefault': 'Remote default',\n 'settings.collapseWorkspaces': 'Hide workspaces',\n 'settings.expandWorkspaces': 'Workspaces',\n 'settings.rediscover': 'Rediscover',\n 'settings.disconnect': 'Disconnect',\n 'settings.connect': 'Connect',\n 'settings.installing': 'Installing\u2026',\n 'settings.connecting': 'Connecting\u2026',\n 'settings.reprovision': 'Reinstall',\n 'settings.remove': 'Remove',\n 'settings.workspacePathPlaceholder': 'Absolute remote path, e.g. /home/user/project',\n 'settings.addWorkspace': 'Add workspace',\n 'settings.addingWorkspace': 'Adding\u2026',\n 'settings.readingWorkspaces': 'Reading workspaces\u2026',\n 'settings.noWorkspaces': 'No remote workspaces registered yet. Add a directory in the remote DSH, then refresh.',\n 'settings.sessionsUnit': 'sessions',\n 'settings.newSession': '+ New session',\n 'settings.creating': 'Creating\u2026',\n 'settings.readingSessions': 'Reading sessions\u2026',\n 'settings.noSessions': 'No sessions in this workspace. Start one with \u201C+ New session\u201D.',\n 'settings.eventsUnit': 'events',\n 'phase.idle': 'Idle',\n 'phase.discovering': 'Discovering',\n 'phase.installing': 'Installing',\n 'phase.connecting': 'Connecting',\n 'phase.connected': 'Connected',\n 'phase.disconnected': 'Disconnected',\n 'phase.error': 'Error',\n 'detail.discovering': 'Discovering the remote agent over SSH\u2026',\n 'detail.installing': 'Installing DSH and dsh-remote-agent remotely; this takes about 1\u20133 minutes\u2026',\n 'detail.connecting': 'Opening the SSH tunnel and checking the remote agent\u2026',\n 'detail.reprovisioning': 'Reinstalling the remote agent\u2026',\n 'detail.reconnecting': 'SSH tunnel closed; waiting to reconnect\u2026',\n 'detail.connectedProxy': 'Connected; model requests go through the local credential proxy',\n 'detail.connectedDirect': 'Connected; the remote host calls DeepSeek directly',\n 'detail.connectedRemote': 'Connected; the remote host uses its own credentials or has no model key',\n 'detail.preparing': 'Preparing the remote installation environment\u2026',\n 'detail.uploadingAgent': 'Uploading the remote agent package\u2026',\n 'detail.uploaded': 'Package uploaded; starting the remote installation\u2026',\n 'detail.nodeDownloading': 'Downloading Node.js\u2026',\n 'detail.nodeInstalled': 'Node.js installed; continuing with pnpm/DSH\u2026',\n 'detail.nodeReady': 'Node.js is ready; preparing pnpm/DSH\u2026',\n 'detail.pnpmInstalling': 'Installing pnpm\u2026',\n 'detail.dshInstalling': 'Installing remote DSH (dependency download may take several minutes)\u2026',\n 'detail.agentInstalling': 'Installing the remote agent\u2026',\n 'detail.webStarting': 'Starting the remote DSH Web service\u2026',\n 'detail.waitingState': 'Waiting for the remote agent state file\u2026',\n 'detail.waitingAgent': 'Waiting for the remote agent\u2026',\n 'detail.agentReady': 'Remote agent is ready; establishing the connection\u2026',\n 'detail.installed': 'Remote installation complete\u2026',\n 'detail.waitingWeb': 'Remote installation complete; waiting for DSH Web\u2026',\n 'detail.stillWaiting': 'Still waiting for remote DSH Web\u2026',\n 'detail.readingState': 'Reading the remote agent state\u2026',\n 'detail.tunnel': 'Establishing the SSH tunnel\u2026',\n 'detail.checkingAgent': 'Checking the remote agent\u2026',\n 'detail.reconcilingModel': 'Synchronizing the remote model environment\u2026',\n 'dialog.title': 'New workspace',\n 'dialog.close': 'Close',\n 'dialog.name': 'Workspace name',\n 'dialog.namePlaceholder': 'Workspace name (optional)',\n 'dialog.source': 'Source folder',\n 'dialog.addToPrefix': 'Add a folder on ',\n 'dialog.addToSuffix': '',\n 'dialog.add': 'Add',\n 'dialog.local': 'This computer',\n 'dialog.remoteDevices': 'Remote devices',\n 'dialog.addRemote': '+ Add remote\u2026',\n 'dialog.addRemoteHint': 'Add and connect a remote host in Settings \u2192 Remote development first.',\n 'dialog.cancel': 'Cancel',\n 'dialog.create': 'Create workspace',\n 'dialog.creating': 'Creating\u2026',\n 'dialog.createFailed': 'Could not create the workspace',\n 'dialog.removeFolder': 'Remove folder',\n 'dialog.selectedFallback': 'The remote workspace was created, but the local mirror has not synced yet',\n 'dialog.folderTitle': 'Choose a source folder',\n 'dialog.up': 'Parent folder',\n 'dialog.selectFolder': 'Select current folder',\n 'dialog.reading': 'Reading folder\u2026',\n 'dialog.emptyFolder': 'No subfolders here',\n 'dialog.readFailed': 'Could not read the folder',\n 'dialog.nativePickerFailed': 'Could not open the system folder picker',\n 'dialog.useFolder': 'Use folder',\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n dshRemote: CopyKey\n }\n}\n\nexport type Translate = (key: CopyKey) => string\n\nexport interface LocaleFace {\n register(namespace: string, dictionaries: { zh: Record<string, string>; en: Record<string, string> }): () => void\n bind(namespace: string): Translate\n}\n", "/**\n * Codex-style unified workspace creation dialog for the sidebar + button.\n *\n * One modal owns the destination (local or a connected remote host), the\n * optional workspace name, and the source folder browser. It is intentionally\n * not a separate local/remote menu.\n *\n * @module dsh-remote-local/client/workspace-add\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { HostView } from '../hosts/types.ts'\nimport type { HostsApi } from './api.ts'\nimport type { Translate } from './locale.ts'\n\nconst ADD_LABELS = new Set(['Add workspace', '\u6DFB\u52A0\u5DE5\u4F5C\u533A'])\nconst NATIVE_CLICK_FLAG = 'dshrNativeClick'\nconst DIALOG_CLASS = 'dshr-workspace-add-dialog'\nlet closeDialog: (() => void) | undefined\nlet closeBrowser: (() => void) | undefined\nlet closeMenu: (() => void) | undefined\n\ninterface WorkspaceFlowContext {\n readonly ctx: Context\n readonly api: HostsApi\n readonly t: Translate\n}\n\ntype Destination = { readonly kind: 'local' } | { readonly kind: 'remote'; readonly host: HostView }\n\ninterface HostDirectoryPicker {\n pick(): Promise<\n | { readonly ok: true; readonly value: string | null }\n | { readonly ok: false; readonly error: { readonly message: string } }\n >\n}\n\ninterface DesktopDirectoryPicker {\n pick(): Promise<string | null>\n}\n\nasync function pickLocalDirectory(ctx: Context): Promise<string | null> {\n // DSH Desktop installs this bridge before the client mounts; the stock\n // native directory-flow plugin uses exactly the same entry point.\n const desktop = (globalThis as unknown as { __DSH_DIRECTORY_PICKER__?: DesktopDirectoryPicker }).__DSH_DIRECTORY_PICKER__\n if (desktop !== undefined) return desktop.pick()\n const uiWorkspace = (ctx as unknown as { uiWorkspace?: { pickDirectory?: () => Promise<string | null> } }).uiWorkspace\n if (uiWorkspace?.pickDirectory !== undefined) return uiWorkspace.pickDirectory()\n const remote = (ctx as unknown as { remote?: { directoryPicker?: HostDirectoryPicker } }).remote\n if (remote?.directoryPicker === undefined) throw new Error('directory picker is unavailable')\n const result = await remote.directoryPicker.pick()\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n}\n\ninterface DirectoryListing {\n readonly path: string\n readonly parent: string\n readonly entries: readonly { readonly name: string; readonly path: string }[]\n readonly truncated: boolean\n}\n\n/** Install the sidebar add-workspace dialog once per client mount. */\nexport function installWorkspaceAddMenu(ctx: Context, api: HostsApi, t: Translate): void {\n if (typeof document === 'undefined') return\n ensureStyles()\n\n ctx.effect(() => {\n const onClick = (event: MouseEvent): void => {\n const target = event.target\n if (!(target instanceof Element)) return\n const button = target.closest<HTMLButtonElement>('button[aria-label]')\n if (button === null) return\n const label = button.getAttribute('aria-label') ?? ''\n if (!ADD_LABELS.has(label)) return\n if (button.closest('[class*=\"headerActions\"]') === null) return\n if (button.dataset[NATIVE_CLICK_FLAG] === '1') {\n delete button.dataset[NATIVE_CLICK_FLAG]\n return\n }\n event.preventDefault()\n event.stopImmediatePropagation()\n void openCreateDialog({ ctx, api, t }, button)\n }\n document.addEventListener('click', onClick, true)\n return () => {\n document.removeEventListener('click', onClick, true)\n closeTransient()\n }\n }, 'dsh-remote: workspace add dialog')\n}\n\nasync function openCreateDialog(flow: WorkspaceFlowContext, anchor: HTMLButtonElement): Promise<void> {\n closeTransient()\n const overlay = document.createElement('div')\n overlay.className = `${DIALOG_CLASS}-overlay`\n overlay.setAttribute('role', 'presentation')\n const dialog = document.createElement('div')\n dialog.className = DIALOG_CLASS\n dialog.setAttribute('role', 'dialog')\n dialog.setAttribute('aria-modal', 'true')\n dialog.setAttribute('aria-label', flow.t('dialog.title'))\n\n const title = document.createElement('h2')\n title.className = `${DIALOG_CLASS}-title`\n title.textContent = flow.t('dialog.title')\n const header = document.createElement('div')\n header.className = `${DIALOG_CLASS}-header`\n const closeButton = document.createElement('button')\n closeButton.type = 'button'\n closeButton.className = `${DIALOG_CLASS}-close`\n closeButton.setAttribute('aria-label', flow.t('dialog.close'))\n closeButton.textContent = '\u00D7'\n header.append(title, closeButton)\n\n const nameLabel = document.createElement('label')\n nameLabel.className = `${DIALOG_CLASS}-field`\n const nameText = document.createElement('span')\n nameText.textContent = flow.t('dialog.name')\n const nameRow = document.createElement('div')\n nameRow.className = `${DIALOG_CLASS}-input-row`\n const nameIcon = document.createElement('span')\n nameIcon.className = `${DIALOG_CLASS}-input-icon`\n setFolderIcon(nameIcon)\n const nameInput = document.createElement('input')\n nameInput.className = `${DIALOG_CLASS}-input`\n nameInput.placeholder = flow.t('dialog.namePlaceholder')\n nameInput.autocomplete = 'off'\n nameRow.append(nameIcon, nameInput)\n nameLabel.append(nameText, nameRow)\n\n const sourceTitle = document.createElement('h3')\n sourceTitle.className = `${DIALOG_CLASS}-section-title`\n sourceTitle.textContent = flow.t('dialog.source')\n const sourceHeader = document.createElement('div')\n sourceHeader.className = `${DIALOG_CLASS}-source-header`\n const hostIndicator = document.createElement('span')\n hostIndicator.className = `${DIALOG_CLASS}-host-indicator`\n const hostIcon = document.createElement('span')\n hostIcon.className = `${DIALOG_CLASS}-host-icon`\n setComputerIcon(hostIcon)\n const hostLabelText = document.createElement('span')\n hostIndicator.append(hostIcon, hostLabelText)\n sourceHeader.append(sourceTitle, hostIndicator)\n const sourceBox = document.createElement('div')\n sourceBox.className = `${DIALOG_CLASS}-source-box`\n const sourceCenter = document.createElement('div')\n sourceCenter.className = `${DIALOG_CLASS}-source-center`\n const sourceTrigger = document.createElement('button')\n sourceTrigger.type = 'button'\n sourceTrigger.className = `${DIALOG_CLASS}-source-trigger`\n const sourceTriggerText = document.createElement('span')\n const sourceChevron = document.createElement('span')\n sourceChevron.className = `${DIALOG_CLASS}-chevron`\n sourceChevron.textContent = '\u2304'\n sourceTrigger.append(sourceTriggerText, sourceChevron)\n const addFolder = document.createElement('button')\n addFolder.type = 'button'\n addFolder.className = `${DIALOG_CLASS}-add-folder`\n const addFolderIcon = document.createElement('span')\n addFolderIcon.className = `${DIALOG_CLASS}-add-folder-icon`\n const addFolderText = document.createElement('span')\n addFolderText.textContent = flow.t('dialog.add')\n addFolder.append(addFolderIcon, addFolderText)\n sourceCenter.append(sourceTrigger, addFolder)\n const selectedFolder = document.createElement('div')\n selectedFolder.className = `${DIALOG_CLASS}-selected-folder`\n const selectedIcon = document.createElement('span')\n selectedIcon.textContent = '\uD83D\uDCC1'\n const selectedName = document.createElement('span')\n selectedName.className = `${DIALOG_CLASS}-selected-name`\n const removeFolder = document.createElement('button')\n removeFolder.type = 'button'\n removeFolder.className = `${DIALOG_CLASS}-remove-folder`\n removeFolder.setAttribute('aria-label', flow.t('dialog.removeFolder'))\n removeFolder.textContent = '\u00D7'\n selectedFolder.append(selectedIcon, selectedName, removeFolder)\n sourceBox.append(sourceCenter, selectedFolder)\n\n const error = document.createElement('p')\n error.className = `${DIALOG_CLASS}-error`\n error.setAttribute('role', 'alert')\n\n const actions = document.createElement('div')\n actions.className = `${DIALOG_CLASS}-actions`\n const cancel = document.createElement('button')\n cancel.type = 'button'\n cancel.className = `${DIALOG_CLASS}-button`\n cancel.textContent = flow.t('dialog.cancel')\n const create = document.createElement('button')\n create.type = 'button'\n create.className = `${DIALOG_CLASS}-button ${DIALOG_CLASS}-primary`\n create.textContent = flow.t('dialog.create')\n actions.append(cancel, create)\n\n dialog.append(header, nameLabel, sourceHeader, sourceBox, error, actions)\n overlay.append(dialog)\n document.body.append(overlay)\n\n let hosts: HostView[] = []\n let destination: Destination = { kind: 'local' }\n let selectedPath = ''\n const update = (): void => {\n const label = destination.kind === 'local' ? flow.t('dialog.local') : destination.host.label\n sourceTriggerText.textContent = `${flow.t('dialog.addToPrefix')}${label}${flow.t('dialog.addToSuffix')}`\n hostLabelText.textContent = label\n addFolderIcon.textContent = '+ '\n const hasFolder = selectedPath !== ''\n sourceCenter.style.display = hasFolder ? 'none' : 'flex'\n selectedFolder.style.display = hasFolder ? 'flex' : 'none'\n sourceBox.classList.toggle(`${DIALOG_CLASS}-source-box-selected`, hasFolder)\n selectedName.textContent = hasFolder ? folderName(selectedPath) : ''\n selectedFolder.title = hasFolder ? selectedPath : ''\n create.disabled = !hasFolder\n }\n const close = (): void => {\n closeBrowser?.()\n closeMenu?.()\n overlay.remove()\n window.removeEventListener('keydown', keyHandler, true)\n overlay.removeEventListener('pointerdown', outside, true)\n if (closeDialog === close) closeDialog = undefined\n }\n closeDialog = close\n const keyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n const outside = (event: Event): void => {\n if (event.target === overlay) close()\n }\n window.addEventListener('keydown', keyHandler, true)\n overlay.addEventListener('pointerdown', outside, true)\n cancel.addEventListener('click', close)\n closeButton.addEventListener('click', close)\n\n sourceTrigger.addEventListener('click', () => {\n openDestinationMenu(sourceTrigger, hosts, destination, flow.t, next => {\n destination = next\n selectedPath = ''\n update()\n })\n })\n addFolder.addEventListener('click', () => {\n if (destination.kind === 'local') {\n addFolder.disabled = true\n void pickLocalDirectory(flow.ctx)\n .then(path => {\n if (path !== null) {\n selectedPath = path\n update()\n }\n })\n .catch(failure => { error.textContent = messageOf(failure, flow.t('dialog.nativePickerFailed')) })\n .finally(() => { addFolder.disabled = false })\n return\n }\n const initial = destination.host.hostInfo?.home ?? '~'\n openDirectoryBrowser(flow, destination, initial, path => {\n selectedPath = path\n update()\n })\n })\n removeFolder.addEventListener('click', () => {\n selectedPath = ''\n update()\n })\n nameInput.addEventListener('input', update)\n\n try {\n const state = await flow.api.state()\n hosts = state.hosts.filter(host => host.phase === 'connected')\n } catch {\n hosts = []\n }\n update()\n create.addEventListener('click', () => {\n if (selectedPath === '') return\n const title = nameInput.value.trim() || folderName(selectedPath)\n create.disabled = true\n cancel.disabled = true\n create.textContent = flow.t('dialog.creating')\n void (async () => {\n try {\n if (destination.kind === 'local') {\n const local = await flow.api.createLocalWorkspace({ path: selectedPath, title })\n close()\n flow.ctx.uiWorkspace.startSession(local.workspaceId as never)\n return\n }\n const workspace = await flow.api.createWorkspace(destination.host.id, {\n path: selectedPath,\n title,\n })\n const session = await flow.api.createSession(destination.host.id, {\n workspaceId: workspace.workspaceId,\n })\n if (session.localSessionId !== undefined) {\n close()\n flow.api.openSession(session.localSessionId)\n return\n }\n const localId = await waitForLocalWorkspace(flow.api, destination.host.id, workspace.workspaceId)\n if (localId === undefined) throw new Error(flow.t('dialog.selectedFallback'))\n close()\n flow.ctx.uiWorkspace.startSession(localId as never)\n } catch (failure) {\n error.textContent = messageOf(failure, flow.t('dialog.createFailed'))\n create.disabled = false\n cancel.disabled = false\n create.textContent = flow.t('dialog.create')\n }\n })()\n })\n\n nameInput.focus()\n}\n\nfunction openDestinationMenu(\n anchor: HTMLElement,\n hosts: readonly HostView[],\n current: Destination,\n t: Translate,\n onPick: (destination: Destination) => void,\n): void {\n closeMenu?.()\n const menu = document.createElement('div')\n menu.className = `${DIALOG_CLASS}-destination-menu`\n const add = (label: string, destination: Destination): void => {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = `${DIALOG_CLASS}-destination-item`\n const text = document.createElement('span')\n text.textContent = label\n const check = document.createElement('span')\n check.className = `${DIALOG_CLASS}-destination-check`\n const selected = current.kind === destination.kind\n && (destination.kind === 'local' || (current.kind === 'remote' && current.host.id === destination.host.id))\n check.textContent = selected ? '\u2713' : ''\n item.append(text, check)\n item.addEventListener('click', () => {\n onPick(destination)\n close()\n })\n menu.append(item)\n }\n add(t('dialog.local'), { kind: 'local' })\n if (hosts.length > 0) {\n const section = document.createElement('div')\n section.className = `${DIALOG_CLASS}-destination-section`\n section.textContent = t('dialog.remoteDevices')\n menu.append(section)\n for (const host of hosts) add(host.label, { kind: 'remote', host })\n }\n const addRemote = document.createElement('button')\n addRemote.type = 'button'\n addRemote.className = `${DIALOG_CLASS}-destination-item ${DIALOG_CLASS}-destination-add`\n addRemote.textContent = t('dialog.addRemote')\n addRemote.addEventListener('click', () => {\n close()\n const error = document.querySelector(`.${DIALOG_CLASS}-error`)\n if (error !== null) error.textContent = t('dialog.addRemoteHint')\n })\n menu.append(addRemote)\n document.body.append(menu)\n const rect = anchor.getBoundingClientRect()\n menu.style.left = `${Math.min(rect.left, window.innerWidth - 260)}px`\n menu.style.top = `${rect.bottom + 6}px`\n const close = (): void => {\n menu.remove()\n window.removeEventListener('pointerdown', dismiss, true)\n if (closeMenu === close) closeMenu = undefined\n }\n closeMenu = close\n const dismiss = (event: Event): void => {\n if (event.target instanceof Node && menu.contains(event.target)) return\n close()\n }\n window.addEventListener('pointerdown', dismiss, true)\n}\n\nasync function openDirectoryBrowser(\n flow: WorkspaceFlowContext,\n destination: Destination,\n initialPath: string,\n onPick: (path: string) => void,\n): Promise<void> {\n closeBrowser?.()\n const overlay = document.createElement('div')\n overlay.className = `${DIALOG_CLASS}-browser-overlay`\n overlay.setAttribute('role', 'presentation')\n const dialog = document.createElement('div')\n dialog.className = `${DIALOG_CLASS} ${DIALOG_CLASS}-browser`\n dialog.setAttribute('role', 'dialog')\n dialog.setAttribute('aria-modal', 'true')\n dialog.setAttribute('aria-label', flow.t('dialog.folderTitle'))\n\n const title = document.createElement('h2')\n title.className = `${DIALOG_CLASS}-browser-title`\n title.textContent = flow.t('dialog.folderTitle')\n const browserHeader = document.createElement('div')\n browserHeader.className = `${DIALOG_CLASS}-browser-header`\n const browserClose = document.createElement('button')\n browserClose.type = 'button'\n browserClose.className = `${DIALOG_CLASS}-close`\n browserClose.setAttribute('aria-label', flow.t('dialog.close'))\n browserClose.textContent = '\u00D7'\n browserHeader.append(title, browserClose)\n const pathRow = document.createElement('div')\n pathRow.className = `${DIALOG_CLASS}-browser-path-row`\n const up = document.createElement('button')\n up.type = 'button'\n up.className = `${DIALOG_CLASS}-up`\n up.textContent = '\u2191'\n up.setAttribute('aria-label', flow.t('dialog.up'))\n const pathInput = document.createElement('input')\n pathInput.className = `${DIALOG_CLASS}-browser-path`\n pathInput.value = initialPath\n pathInput.autocomplete = 'off'\n pathRow.append(up, pathInput)\n const status = document.createElement('p')\n status.className = `${DIALOG_CLASS}-browser-status`\n status.textContent = flow.t('dialog.selectFolder')\n const list = document.createElement('div')\n list.className = `${DIALOG_CLASS}-browser-list`\n const actions = document.createElement('div')\n actions.className = `${DIALOG_CLASS}-actions`\n const cancel = document.createElement('button')\n cancel.type = 'button'\n cancel.className = `${DIALOG_CLASS}-button`\n cancel.textContent = flow.t('dialog.cancel')\n const use = document.createElement('button')\n use.type = 'button'\n use.className = `${DIALOG_CLASS}-button ${DIALOG_CLASS}-primary`\n use.textContent = flow.t('dialog.useFolder')\n actions.append(cancel, use)\n dialog.append(browserHeader, pathRow, status, list, actions)\n overlay.append(dialog)\n document.body.append(overlay)\n\n let current = initialPath\n let parent = initialPath\n let currentEntries: readonly { readonly name: string; readonly path: string }[] = []\n let requestSeq = 0\n let filterTimer: number | undefined\n\n const fetchListing = async (path: string): Promise<DirectoryListing | undefined> => {\n const sequence = ++requestSeq\n status.textContent = flow.t('dialog.reading')\n status.classList.remove(`${DIALOG_CLASS}-error-text`)\n try {\n const listing: DirectoryListing = destination.kind === 'local'\n ? await flow.api.listLocalDirectories(path)\n : await flow.api.listDirectories(destination.host.id, path)\n if (sequence !== requestSeq) return undefined\n return listing\n } catch (failure) {\n if (sequence === requestSeq) {\n status.textContent = messageOf(failure, flow.t('dialog.readFailed'))\n status.classList.add(`${DIALOG_CLASS}-error-text`)\n }\n return undefined\n }\n }\n\n const render = (entries: readonly { readonly name: string; readonly path: string }[]): void => {\n list.replaceChildren()\n if (entries.length === 0) {\n const empty = document.createElement('p')\n empty.className = `${DIALOG_CLASS}-browser-empty`\n empty.textContent = flow.t('dialog.emptyFolder')\n list.append(empty)\n return\n }\n for (const entry of entries) {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = `${DIALOG_CLASS}-browser-entry`\n const icon = document.createElement('span')\n icon.textContent = '\uD83D\uDCC1'\n const label = document.createElement('span')\n label.textContent = entry.name\n item.append(icon, label)\n item.addEventListener('click', () => { void load(entry.path) })\n list.append(item)\n }\n }\n\n const load = async (path: string): Promise<void> => {\n const listing = await fetchListing(path)\n if (listing === undefined) return\n current = listing.path\n parent = listing.parent\n currentEntries = listing.entries\n pathInput.value = listing.path\n status.textContent = flow.t('dialog.selectFolder')\n render(listing.entries)\n }\n\n const applyInputFilter = async (raw: string): Promise<void> => {\n if (raw === '') return\n const slash = raw.lastIndexOf('/')\n const dir = slash < 0 ? current : (raw.slice(0, slash + 1) || '/')\n const query = slash < 0 ? raw : raw.slice(slash + 1)\n if (query === '') {\n // A trailing slash is an explicit directory navigation.\n await load(raw)\n return\n }\n const normalizedCurrent = current.replace(/\\/+$/u, '') || '/'\n const normalizedDir = dir.replace(/\\/+$/u, '') || '/'\n if (normalizedDir === normalizedCurrent) {\n const needle = query.toLocaleLowerCase()\n render(currentEntries.filter(entry => entry.name.toLocaleLowerCase().startsWith(needle)))\n status.textContent = flow.t('dialog.selectFolder')\n return\n }\n const listing = await fetchListing(dir)\n if (listing === undefined) return\n current = listing.path\n parent = listing.parent\n currentEntries = listing.entries\n const needle = query.toLocaleLowerCase()\n render(listing.entries.filter(entry => entry.name.toLocaleLowerCase().startsWith(needle)))\n status.textContent = flow.t('dialog.selectFolder')\n }\n\n const close = (): void => {\n overlay.remove()\n window.removeEventListener('keydown', keyHandler, true)\n overlay.removeEventListener('pointerdown', outside, true)\n if (closeBrowser === close) closeBrowser = undefined\n }\n closeBrowser = close\n const keyHandler = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') close()\n }\n const outside = (event: Event): void => {\n if (event.target === overlay) close()\n }\n window.addEventListener('keydown', keyHandler, true)\n overlay.addEventListener('pointerdown', outside, true)\n cancel.addEventListener('click', close)\n browserClose.addEventListener('click', close)\n use.addEventListener('click', () => {\n onPick(current)\n close()\n })\n up.addEventListener('click', () => {\n if (parent !== '' && parent !== current) void load(parent)\n })\n pathInput.addEventListener('input', () => {\n if (filterTimer !== undefined) window.clearTimeout(filterTimer)\n const value = pathInput.value\n filterTimer = window.setTimeout(() => { void applyInputFilter(value) }, 140)\n })\n pathInput.addEventListener('keydown', event => {\n if (event.key !== 'Enter') return\n event.preventDefault()\n const value = pathInput.value.trim()\n if (value !== '') void applyInputFilter(value)\n })\n void load(initialPath)\n}\n\nasync function waitForLocalWorkspace(api: HostsApi, hostId: string, workspaceId: string): Promise<string | undefined> {\n for (let attempt = 0; attempt < 40; attempt += 1) {\n const result = await api.workspaces(hostId)\n const workspace = result.workspaces.find(item => item.workspaceId === workspaceId)\n if (workspace?.localWorkspaceId !== undefined) return workspace.localWorkspaceId\n await new Promise(resolve => setTimeout(resolve, 500))\n }\n return undefined\n}\n\nfunction closeTransient(): void {\n closeDialog?.()\n closeBrowser?.()\n closeMenu?.()\n}\n\nfunction messageOf(failure: unknown, fallback: string): string {\n if (failure instanceof Error && failure.message !== '') return failure.message\n return fallback\n}\n\nfunction setFolderIcon(icon: HTMLElement): void {\n icon.innerHTML = '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M3.5 7.5A2.5 2.5 0 0 1 6 5h4l2 2h6a2.5 2.5 0 0 1 2.5 2.5v7A2.5 2.5 0 0 1 18 19H6a2.5 2.5 0 0 1-2.5-2.5v-9Z\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>'\n}\n\nfunction folderName(path: string): string {\n const clean = path.replace(/[\\\\/]+$/u, '')\n const parts = clean.split(/[\\\\/]+/u)\n return parts[parts.length - 1] ?? clean\n}\n\nfunction setComputerIcon(icon: HTMLElement): void {\n icon.innerHTML = '<svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><rect x=\"3.5\" y=\"5\" width=\"17\" height=\"12\" rx=\"1.7\" stroke=\"currentColor\" stroke-width=\"1.5\"/><path d=\"M8 20h8M12 17v3\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>'\n}\n\nfunction ensureStyles(): void {\n const existing = document.getElementById('dshr-workspace-add-style')\n const style = existing instanceof HTMLStyleElement ? existing : document.createElement('style')\n style.id = 'dshr-workspace-add-style'\n style.textContent = `\n .${DIALOG_CLASS}-overlay {\n position: fixed; inset: 0; z-index: 100001; display: grid; place-items: center;\n padding: 24px; background: rgba(0,0,0,.55);\n }\n .${DIALOG_CLASS} {\n width: min(632px, calc(100vw - 48px));\n box-sizing: border-box; padding: 26px; display: flex; flex-direction: column;\n border-radius: 14px; border: 1px solid rgba(255,255,255,.1);\n background: #242424; color: #f2f2f2; box-shadow: 0 28px 80px rgba(0,0,0,.65);\n font: 14px/1.45 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n .${DIALOG_CLASS}-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; }\n .${DIALOG_CLASS}-title { margin: 0; font-size: 28px; line-height: 1.1; font-weight: 700; letter-spacing: -.02em; }\n .${DIALOG_CLASS}-close { border: 0; background: transparent; color: rgba(255,255,255,.75); font-size: 30px; line-height: 1; cursor: pointer; padding: 0 2px; }\n .${DIALOG_CLASS}-close:hover { color: #fff; }\n .${DIALOG_CLASS}-field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 14px; }\n .${DIALOG_CLASS}-field > span { font-size: 13px; color: rgba(255,255,255,.72); }\n .${DIALOG_CLASS}-input-row { display: flex; align-items: center; gap: 10px; border: 1px solid rgba(255,255,255,.24); border-radius: 9px; padding: 0 12px; background: transparent; }\n .${DIALOG_CLASS}-input-icon { display: inline-flex; color: rgba(255,255,255,.7); }\n .${DIALOG_CLASS}-input { width: 100%; border: 0; outline: none; background: transparent; color: inherit; font: inherit; font-size: 14px; padding: 13px 0; }\n .${DIALOG_CLASS}-source-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 4px 0 10px; }\n .${DIALOG_CLASS}-section-title { margin: 0; font-size: 17px; font-weight: 600; color: rgba(255,255,255,.92); }\n .${DIALOG_CLASS}-host-indicator { display: inline-flex; align-items: center; gap: 8px; color: rgba(255,255,255,.62); font-size: 15px; }\n .${DIALOG_CLASS}-host-icon { display: inline-flex; }\n .${DIALOG_CLASS}-source-box { position: relative; border: 1px solid rgba(255,255,255,.16); border-radius: 9px; padding: 24px 16px; min-height: 150px; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; }\n .${DIALOG_CLASS}-source-center { display: flex; flex-direction: column; align-items: center; gap: 16px; }\n .${DIALOG_CLASS}-source-trigger { display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; color: rgba(255,255,255,.75); font: inherit; font-size: 15px; cursor: pointer; }\n .${DIALOG_CLASS}-source-trigger span:first-child { color: #fff; font-weight: 600; }\n .${DIALOG_CLASS}-chevron { font-size: 18px; line-height: 1; }\n .${DIALOG_CLASS}-add-folder { display: inline-flex; align-items: center; gap: 8px; border: 0; border-radius: 8px; padding: 8px 14px; background: rgba(255,255,255,.12); color: #fff; font: inherit; font-size: 14px; cursor: pointer; }\n .${DIALOG_CLASS}-add-folder:hover { background: rgba(255,255,255,.18); }\n .${DIALOG_CLASS}-add-folder-icon { font-size: 18px; line-height: 1; }\n .${DIALOG_CLASS}-source-box-selected { padding: 0; min-height: 0; flex: none; justify-content: flex-start; }\n .${DIALOG_CLASS}-selected-folder { display: none; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; padding: 12px 14px; color: rgba(255,255,255,.92); font-size: 16px; }\n .${DIALOG_CLASS}-selected-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .${DIALOG_CLASS}-remove-folder { flex: none; border: 0; background: transparent; color: rgba(255,255,255,.6); font-size: 24px; line-height: 1; cursor: pointer; padding: 0 2px; }\n .${DIALOG_CLASS}-remove-folder:hover { color: #fff; }\n .${DIALOG_CLASS}-error { margin: 6px 0 0; color: #ff8b8b; font-size: 12px; white-space: pre-wrap; }\n .${DIALOG_CLASS}-error:empty { display: none; }\n .${DIALOG_CLASS}-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }\n .${DIALOG_CLASS}-button { padding: 11px 18px; border-radius: 8px; border: 1px solid rgba(255,255,255,.18); background: transparent; color: #eee; cursor: pointer; font: inherit; font-size: 15px; }\n .${DIALOG_CLASS}-button:disabled { opacity: .45; cursor: default; }\n .${DIALOG_CLASS}-primary { border-color: #bdbdbd; background: #bdbdbd; color: #1a1a1a; font-weight: 600; }\n .${DIALOG_CLASS}-primary:hover:not(:disabled) { background: #d0d0d0; }\n .${DIALOG_CLASS}-destination-menu {\n position: fixed; z-index: 100010; min-width: 250px; padding: 6px;\n border-radius: 12px; border: 1px solid rgba(255,255,255,.14); background: #2a2a2a; color: #eee;\n box-shadow: 0 18px 48px rgba(0,0,0,.55); font: 14px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n .${DIALOG_CLASS}-destination-item { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 10px 12px; border: 0; border-radius: 8px; background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left; }\n .${DIALOG_CLASS}-destination-item:hover { background: rgba(255,255,255,.09); }\n .${DIALOG_CLASS}-destination-section { padding: 14px 12px 6px; color: rgba(255,255,255,.5); font-size: 13px; }\n .${DIALOG_CLASS}-destination-check { color: #bdbdbd; }\n .${DIALOG_CLASS}-destination-add { color: rgba(255,255,255,.75); }\n .${DIALOG_CLASS}-browser-overlay { position: fixed; inset: 0; z-index: 100002; display: grid; place-items: center; background: rgba(0,0,0,.6); padding: 24px; }\n .${DIALOG_CLASS}-browser { width: 640px; max-height: 86vh; display: flex; flex-direction: column; padding: 26px; }\n .${DIALOG_CLASS}-browser-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 20px; }\n .${DIALOG_CLASS}-browser-title { margin: 0; font-size: 30px; line-height: 1.15; font-weight: 700; }\n .${DIALOG_CLASS}-browser-path-row { display: flex; align-items: center; gap: 8px; }\n .${DIALOG_CLASS}-up { flex: none; width: 34px; height: 34px; border: 0; background: transparent; color: rgba(255,255,255,.65); font-size: 22px; cursor: pointer; }\n .${DIALOG_CLASS}-browser-path { flex: 1; border: 1px solid rgba(255,255,255,.22); border-radius: 9px; background: transparent; color: #fff; font: inherit; font-size: 16px; padding: 12px 14px; outline: none; }\n .${DIALOG_CLASS}-browser-path:focus { border-color: #4f8cff; }\n .${DIALOG_CLASS}-browser-status { margin: 14px 0 8px; color: rgba(255,255,255,.55); font-size: 13px; }\n .${DIALOG_CLASS}-browser-list { flex: 1; min-height: 220px; max-height: 420px; overflow: auto; border: 1px solid rgba(255,255,255,.16); border-radius: 12px; padding: 6px; }\n .${DIALOG_CLASS}-browser-entry { display: flex; align-items: center; gap: 12px; width: 100%; box-sizing: border-box; padding: 11px 12px; border: 0; border-radius: 8px; background: transparent; color: #fff; font: inherit; font-size: 16px; text-align: left; cursor: pointer; }\n .${DIALOG_CLASS}-browser-entry:hover { background: rgba(255,255,255,.08); }\n .${DIALOG_CLASS}-browser-empty { margin: 16px 12px; color: rgba(255,255,255,.5); font-size: 13px; }\n `\n if (existing === null) document.head.append(style)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["messageOf", "title", "needle"]
7
7
  }
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /* dsh-remote-local-plugin@0.8.0 — bundled host entry */
1
+ /* dsh-remote-local-plugin@0.8.1 — bundled host entry */
2
2
 
3
3
  // src/index.ts
4
4
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
@@ -2798,6 +2798,7 @@ WAIT_FOR_STATE=${options.start === "none" ? 0 : 1}
2798
2798
 
2799
2799
  export DSH_HOME
2800
2800
  mkdir -p "$INSTALL_DIR" "$DSH_HOME/remote-agent"
2801
+ echo "dsh-remote: preparing remote install"
2801
2802
 
2802
2803
  ${nodePathPrelude()}
2803
2804
 
@@ -2819,6 +2820,7 @@ if ! command -v npm >/dev/null 2>&1; then
2819
2820
  echo "dsh-remote: npm is required; install a Node.js distribution that includes npm" >&2
2820
2821
  exit 23
2821
2822
  fi
2823
+ echo "dsh-remote: node ready"
2822
2824
 
2823
2825
  export PATH="$INSTALL_DIR/node_modules/.bin:$PATH"
2824
2826
  if ! command -v pnpm >/dev/null 2>&1; then
@@ -2839,9 +2841,11 @@ fi
2839
2841
  echo "dsh-remote: adding plugin to profile $PROFILE"
2840
2842
  "$DSH_BIN" plugin --profile "$PROFILE" add "$AGENT_TARBALL"
2841
2843
 
2844
+ echo "dsh-remote: starting dsh web"
2842
2845
  ${startBlock}
2843
2846
 
2844
2847
  if [ "$WAIT_FOR_STATE" -eq 1 ]; then
2848
+ echo "dsh-remote: waiting for agent state"
2845
2849
  i=0
2846
2850
  while [ "$i" -lt 60 ]; do
2847
2851
  if [ -s "$STATE_FILE" ]; then break; fi
@@ -2853,6 +2857,7 @@ fi
2853
2857
  echo "dsh-remote: installed"
2854
2858
  echo "dsh-remote: state file $STATE_FILE"
2855
2859
  if [ "$WAIT_FOR_STATE" -eq 1 ] && [ -s "$STATE_FILE" ]; then
2860
+ echo "dsh-remote: agent ready"
2856
2861
  PORT_VALUE=$(sed -n 's/.*"port": *\\([0-9][0-9]*\\).*/\\1/p' "$STATE_FILE" | head -1)
2857
2862
  echo "dsh-remote: remote agent port \${PORT_VALUE:-unknown}"
2858
2863
  else
@@ -2875,6 +2880,7 @@ cat > "$UNIT_DIR/dsh-remote-agent.service" <<DSH_REMOTE_UNIT
2875
2880
  ${unit}DSH_REMOTE_UNIT
2876
2881
  chmod 600 "$UNIT_DIR/dsh-remote-agent.service"
2877
2882
 
2883
+ echo "dsh-remote: restarting systemd user unit"
2878
2884
  if command -v systemctl >/dev/null 2>&1 && systemctl --user daemon-reload >/dev/null 2>&1; then
2879
2885
  if systemctl --user enable dsh-remote-agent.service >/dev/null 2>&1 && systemctl --user restart dsh-remote-agent.service; then
2880
2886
  echo "dsh-remote: restarted systemd user unit dsh-remote-agent.service"
@@ -2892,6 +2898,7 @@ function renderNohup(options) {
2892
2898
  return `if [ -f "$DSH_HOME/remote-agent/web.pid" ]; then
2893
2899
  kill "$(cat "$DSH_HOME/remote-agent/web.pid")" 2>/dev/null || true
2894
2900
  fi
2901
+ echo "dsh-remote: starting dsh web under nohup"
2895
2902
  ${modelEnv}nohup "$DSH_BIN" web --host 127.0.0.1 --port "$PORT" --no-open > "$DSH_HOME/remote-agent/web.log" 2>&1 &
2896
2903
  echo $! > "$DSH_HOME/remote-agent/web.pid"
2897
2904
  echo "dsh-remote: started dsh web under nohup (pid $(cat "$DSH_HOME/remote-agent/web.pid"))"`;
@@ -2987,7 +2994,7 @@ function runRemoteScript(target, script, options) {
2987
2994
  reject(new Error(`remote bootstrap timed out after ${options.timeoutMs}ms`));
2988
2995
  }, options.timeoutMs);
2989
2996
  timer.unref?.();
2990
- const append = (target2, chunk) => {
2997
+ const append = (target2, stream, chunk) => {
2991
2998
  bytes += chunk.length;
2992
2999
  if (bytes > 4 * 1024 * 1024) {
2993
3000
  child.kill("SIGKILL");
@@ -2995,9 +3002,13 @@ function runRemoteScript(target, script, options) {
2995
3002
  }
2996
3003
  target2.push(chunk);
2997
3004
  if (options.verbose) process.stderr.write(chunk);
3005
+ try {
3006
+ options.onOutput?.(stream, chunk.toString("utf8"));
3007
+ } catch {
3008
+ }
2998
3009
  };
2999
- child.stdout.on("data", (chunk) => append(stdout, chunk));
3000
- child.stderr.on("data", (chunk) => append(stderr, chunk));
3010
+ child.stdout.on("data", (chunk) => append(stdout, "stdout", chunk));
3011
+ child.stderr.on("data", (chunk) => append(stderr, "stderr", chunk));
3001
3012
  child.stdin.on("error", (error) => {
3002
3013
  if (settled) return;
3003
3014
  settled = true;
@@ -3031,7 +3042,9 @@ async function provisionRemoteAgent(options) {
3031
3042
  await access(options.agentTarballPath);
3032
3043
  const remoteTarball = `/tmp/dsh-remote-agent-${Date.now()}-${process.pid}.tgz`;
3033
3044
  const timeoutMs = Math.max(options.timeoutMs ?? 6e5, 6e5);
3045
+ options.onProgress?.("dsh-remote: uploading agent package");
3034
3046
  await uploadFile2(options.agentTarballPath, remoteTarball, target, options);
3047
+ options.onProgress?.("dsh-remote: agent package uploaded");
3035
3048
  const script = renderBootstrapScript({
3036
3049
  profile: options.profile,
3037
3050
  port: 0,
@@ -3046,11 +3059,34 @@ async function provisionRemoteAgent(options) {
3046
3059
  ...options.modelBaseUrl === void 0 ? {} : { modelBaseUrl: options.modelBaseUrl },
3047
3060
  ...options.modelApiKey === void 0 ? {} : { modelApiKey: options.modelApiKey }
3048
3061
  });
3062
+ let stdoutBuffer = "";
3063
+ let stderrBuffer = "";
3064
+ const emitLines = (stream, chunk) => {
3065
+ const key2 = stream === "stdout" ? "stdout" : "stderr";
3066
+ let buffer = key2 === "stdout" ? stdoutBuffer : stderrBuffer;
3067
+ buffer += chunk;
3068
+ let newline = buffer.indexOf("\n");
3069
+ while (newline >= 0) {
3070
+ const line = buffer.slice(0, newline).trim();
3071
+ buffer = buffer.slice(newline + 1);
3072
+ if (line !== "") {
3073
+ try {
3074
+ options.onProgress?.(line);
3075
+ } catch {
3076
+ }
3077
+ }
3078
+ newline = buffer.indexOf("\n");
3079
+ }
3080
+ if (buffer.length > 8192) buffer = buffer.slice(-8192);
3081
+ if (key2 === "stdout") stdoutBuffer = buffer;
3082
+ else stderrBuffer = buffer;
3083
+ };
3049
3084
  const result = await runRemoteScript(target, script, {
3050
3085
  binary: options.sshBinary,
3051
3086
  strictHostKeyChecking: options.strictHostKeyChecking,
3052
3087
  timeoutMs,
3053
- verbose: options.verbose ?? false
3088
+ verbose: options.verbose ?? false,
3089
+ onOutput: emitLines
3054
3090
  });
3055
3091
  return { stdout: result.stdout, stderr: result.stderr, remoteTarball };
3056
3092
  }
@@ -3181,7 +3217,7 @@ var RemoteProvisioner = class {
3181
3217
  */
3182
3218
  async ensureInstalled(target, options = {}) {
3183
3219
  const tarball = findAgentTarball();
3184
- options.onProgress?.("\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF08\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\uFF09\u2026");
3220
+ options.onProgress?.("\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026");
3185
3221
  try {
3186
3222
  await provisionRemoteAgent({
3187
3223
  target: target.canonical,
@@ -3199,7 +3235,11 @@ var RemoteProvisioner = class {
3199
3235
  timeoutMs: this.#config.provisionTimeoutMs,
3200
3236
  ...options.modelBaseUrl === void 0 ? {} : { modelBaseUrl: options.modelBaseUrl },
3201
3237
  ...options.modelApiKey === void 0 ? {} : { modelApiKey: options.modelApiKey },
3202
- verbose: false
3238
+ verbose: false,
3239
+ onProgress: (line) => {
3240
+ const message = progressForLine(line);
3241
+ if (message !== void 0) options.onProgress?.(message);
3242
+ }
3203
3243
  });
3204
3244
  } catch (error) {
3205
3245
  const detail = error instanceof Error ? error.message : String(error);
@@ -3209,12 +3249,13 @@ var RemoteProvisioner = class {
3209
3249
  cause: error
3210
3250
  });
3211
3251
  }
3212
- options.onProgress?.("\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u7B49\u5F85 DSH web \u542F\u52A8\u2026");
3252
+ options.onProgress?.("\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\uFF0C\u6B63\u5728\u7B49\u5F85 DSH web \u542F\u52A8\u2026");
3213
3253
  await this.#waitForAgent(target, options.onProgress, options.signal);
3214
3254
  }
3215
3255
  async #waitForAgent(target, onProgress, signal) {
3216
3256
  const deadline = Date.now() + 12e4;
3217
3257
  let lastError;
3258
+ onProgress?.("\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u2026");
3218
3259
  while (Date.now() < deadline) {
3219
3260
  if (signal?.aborted === true) throw new LocalError("internal", "\u8FDC\u7AEF\u5B89\u88C5\u5DF2\u53D6\u6D88");
3220
3261
  try {
@@ -3224,10 +3265,13 @@ var RemoteProvisioner = class {
3224
3265
  strictHostKeyChecking: this.#config.strictHostKeyChecking,
3225
3266
  remoteStateFile: this.#config.remoteStateFile
3226
3267
  });
3227
- if (isAgentLive(discovered.state)) return;
3268
+ if (isAgentLive(discovered.state)) {
3269
+ onProgress?.("\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026");
3270
+ return;
3271
+ }
3228
3272
  } catch (error) {
3229
3273
  lastError = error;
3230
- onProgress?.("\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026");
3274
+ onProgress?.("\u4ECD\u5728\u7B49\u5F85\u8FDC\u7AEF dsh web \u5C31\u7EEA\u2026");
3231
3275
  }
3232
3276
  await new Promise((resolve4) => setTimeout(resolve4, 2e3));
3233
3277
  }
@@ -3237,6 +3281,30 @@ var RemoteProvisioner = class {
3237
3281
  });
3238
3282
  }
3239
3283
  };
3284
+ var PROVISION_PROGRESS = [
3285
+ ["uploading agent package", "\u6B63\u5728\u4E0A\u4F20\u8FDC\u7AEF agent \u5B89\u88C5\u5305\u2026"],
3286
+ ["agent package uploaded", "\u5B89\u88C5\u5305\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5F00\u59CB\u8FDC\u7AEF\u5B89\u88C5\u2026"],
3287
+ ["preparing remote install", "\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026"],
3288
+ ["downloading node.js", "\u6B63\u5728\u4E0B\u8F7D Node.js\u2026"],
3289
+ ["installed node.js", "Node.js \u5B89\u88C5\u5B8C\u6210\uFF0C\u7EE7\u7EED\u5B89\u88C5 pnpm/DSH\u2026"],
3290
+ ["node ready", "Node.js \u5DF2\u5C31\u7EEA\uFF0C\u51C6\u5907\u5B89\u88C5 pnpm/DSH\u2026"],
3291
+ ["installing pnpm@", "\u6B63\u5728\u5B89\u88C5 pnpm\u2026"],
3292
+ ["installing @deepseek-ai/dsh@", "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF DSH\uFF08\u4E0B\u8F7D\u4F9D\u8D56\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF09\u2026"],
3293
+ ["adding plugin to profile", "\u6B63\u5728\u5B89\u88C5\u8FDC\u7AEF agent\u2026"],
3294
+ ["restarting systemd user unit", "\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026"],
3295
+ ["starting dsh web under nohup", "\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web\u2026"],
3296
+ ["starting dsh web", "\u6B63\u5728\u542F\u52A8\u8FDC\u7AEF DSH Web \u670D\u52A1\u2026"],
3297
+ ["waiting for agent state", "\u6B63\u5728\u7B49\u5F85\u8FDC\u7AEF agent \u72B6\u6001\u6587\u4EF6\u2026"],
3298
+ ["agent ready", "\u8FDC\u7AEF agent \u5DF2\u5C31\u7EEA\uFF0C\u6B63\u5728\u5EFA\u7ACB\u8FDE\u63A5\u2026"],
3299
+ ["installed", "\u8FDC\u7AEF\u5B89\u88C5\u5B8C\u6210\u2026"]
3300
+ ];
3301
+ function progressForLine(line) {
3302
+ const normalized = line.toLowerCase();
3303
+ for (const [needle, message] of PROVISION_PROGRESS) {
3304
+ if (normalized.includes(needle)) return message;
3305
+ }
3306
+ return void 0;
3307
+ }
3240
3308
  function findAgentTarball() {
3241
3309
  const candidates = [
3242
3310
  new URL("../assets", import.meta.url),
@@ -3703,7 +3771,7 @@ var RemoteHostsService = class {
3703
3771
  phase: "installing",
3704
3772
  modelProxy: proxy !== void 0,
3705
3773
  modelDirect: direct !== void 0,
3706
- detail: "\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026"
3774
+ detail: "\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026"
3707
3775
  });
3708
3776
  try {
3709
3777
  await this.#provisioner.ensureInstalled(target, {
@@ -3881,13 +3949,14 @@ var RemoteHostsService = class {
3881
3949
  this.#setRuntime(id, {
3882
3950
  ...this.#runtimeFor(id),
3883
3951
  phase: "installing",
3884
- detail: "\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026"
3952
+ detail: "\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026"
3885
3953
  });
3886
3954
  try {
3887
3955
  await this.#provisioner.ensureInstalled(target, {
3888
3956
  ...this.#modelEnvironment(proxy, proxyToken, direct),
3889
3957
  onProgress: (detail) => this.#setRuntime(id, { ...this.#runtimeFor(id), detail })
3890
3958
  });
3959
+ this.#setRuntime(id, { ...this.#runtimeFor(id), detail: "\u6B63\u5728\u8BFB\u53D6\u8FDC\u7AEF agent \u72B6\u6001\u2026" });
3891
3960
  const discovery = await this.#discover(target);
3892
3961
  remotePort = discovery.state.port;
3893
3962
  token = discovery.state.token;
@@ -3924,6 +3993,7 @@ var RemoteHostsService = class {
3924
3993
  phase: "connecting",
3925
3994
  detail: "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026"
3926
3995
  });
3996
+ this.#setRuntime(id, { ...this.#runtimeFor(id), phase: "connecting", detail: "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u2026" });
3927
3997
  let tunnel;
3928
3998
  try {
3929
3999
  tunnel = await Tunnel.start({
@@ -3940,6 +4010,7 @@ var RemoteHostsService = class {
3940
4010
  token,
3941
4011
  hostId: id
3942
4012
  });
4013
+ this.#setRuntime(id, { ...this.#runtimeFor(id), detail: "\u6B63\u5728\u6821\u9A8C\u8FDC\u7AEF agent\u2026" });
3943
4014
  const health = await client.health();
3944
4015
  if (await this.#reconcileModelEnvironment(id, target, health, proxy, proxyToken, direct, tunnel)) {
3945
4016
  return this.#doConnect(id);
@@ -3977,6 +4048,7 @@ var RemoteHostsService = class {
3977
4048
  return false;
3978
4049
  }
3979
4050
  this.#modelReconciled.add(id);
4051
+ this.#setRuntime(id, { ...this.#runtimeFor(id), detail: "\u6B63\u5728\u540C\u6B65\u8FDC\u7AEF\u6A21\u578B\u73AF\u5883\u2026" });
3980
4052
  this.ctx.logger.warn(
3981
4053
  "dsh-remote: remote model environment for %s is %s, expected %s; reinstalling",
3982
4054
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-remote-local-plugin",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "DSH remote-development local plugin: host registry, SSH tunnels, remote workspace/session discovery, an authenticated Connection RPC surface, agent tools, and a Settings page.",
5
5
  "keywords": [
6
6
  "dsh",
@@ -1,114 +0,0 @@
1
- /**
2
- * RPC contract shared by the host handler and the browser Settings page.
3
- *
4
- * The channel rides DSH's authenticated Connection transport, so it works
5
- * through the web HTTP carrier and the desktop IPC carrier without a second
6
- * authentication scheme.
7
- *
8
- * @module dsh-remote-local/rpc/contract
9
- */
10
- import type { HostView } from '../hosts/types.ts';
11
- import type { HostInfo, RemoteSessionSummary, RemoteWorkspace, SessionSnapshot } from '@dsh-remote/protocol';
12
- /** Absolute Connection RPC channel. */
13
- export declare const RPC_CHANNEL = "/dsh-remote";
14
- /** Endpoint names. */
15
- export type RpcEndpoint = 'state' | 'target/validate' | 'host/add' | 'host/remove' | 'host/connect' | 'host/disconnect' | 'host/refresh' | 'host/workspaces' | 'host/sessions' | 'host/session' | 'host/web-url';
16
- /** Browser-safe state snapshot. */
17
- export interface RemoteHostsState {
18
- readonly protocolVersion: number;
19
- readonly hosts: readonly HostView[];
20
- }
21
- /** Result of parsing a destination. */
22
- export interface ValidatedTarget {
23
- readonly canonical: string;
24
- readonly host: string;
25
- readonly user: string | null;
26
- readonly port: number | null;
27
- }
28
- /** Result of listing workspaces. */
29
- export interface WorkspacesResult {
30
- readonly workspaces: readonly RemoteWorkspace[];
31
- }
32
- /** Result of listing sessions. */
33
- export interface SessionsResult {
34
- readonly sessions: readonly RemoteSessionSummary[];
35
- }
36
- /** Result of resolving the remote Web UI. */
37
- export interface WebUrlResult {
38
- readonly url: string;
39
- }
40
- /** Success/failure result used by the Connection RPC handler. */
41
- export type RpcResult<T> = {
42
- readonly ok: true;
43
- readonly value: T;
44
- } | {
45
- readonly ok: false;
46
- readonly error: {
47
- readonly code: string;
48
- readonly message: string;
49
- readonly details: Record<string, unknown>;
50
- };
51
- };
52
- /** Payloads accepted by each endpoint. */
53
- export interface RpcPayloads {
54
- state: undefined;
55
- 'target/validate': {
56
- sshTarget: string;
57
- };
58
- 'host/add': {
59
- sshTarget: string;
60
- label?: string;
61
- token?: string;
62
- remoteAgentPort?: number;
63
- autoConnect?: boolean;
64
- };
65
- 'host/remove': {
66
- hostId: string;
67
- };
68
- 'host/connect': {
69
- hostId: string;
70
- };
71
- 'host/disconnect': {
72
- hostId: string;
73
- };
74
- 'host/refresh': {
75
- hostId: string;
76
- };
77
- 'host/workspaces': {
78
- hostId: string;
79
- };
80
- 'host/sessions': {
81
- hostId: string;
82
- workspaceId?: string;
83
- };
84
- 'host/session': {
85
- hostId: string;
86
- sessionId: string;
87
- limit?: number;
88
- };
89
- 'host/web-url': {
90
- hostId: string;
91
- };
92
- }
93
- /** Values returned by each endpoint. */
94
- export interface RpcValues {
95
- state: RemoteHostsState;
96
- 'target/validate': ValidatedTarget;
97
- 'host/add': HostView;
98
- 'host/remove': {
99
- removed: true;
100
- };
101
- 'host/connect': HostView;
102
- 'host/disconnect': HostView;
103
- 'host/refresh': HostView;
104
- 'host/workspaces': WorkspacesResult;
105
- 'host/sessions': SessionsResult;
106
- 'host/session': SessionSnapshot;
107
- 'host/web-url': WebUrlResult;
108
- }
109
- /** Host capabilities advertised in the UI. */
110
- export declare const HOST_CAPABILITIES: readonly ["workspace.list", "session.list", "session.snapshot"];
111
- /** Informational type for the Settings page. */
112
- export interface HostSummary extends Pick<HostView, 'id' | 'label' | 'sshTarget' | 'phase'> {
113
- readonly hostInfo: HostInfo | null;
114
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Host-side RPC dispatch for the Settings page.
3
- *
4
- * @module dsh-remote-local/rpc/handler
5
- */
6
- import type { Context } from '@deepseek-ai/cordis';
7
- import type { RemoteHostsService } from '../service.ts';
8
- import { type RpcEndpoint, type RpcResult, type RpcValues } from './contract.ts';
9
- /** Register the channel; returns its asynchronous disposer. */
10
- export declare function registerHostRpc(ctx: Context, service: RemoteHostsService): () => Promise<void>;
11
- /** Narrow a result for callers that want a compiler-checked endpoint name. */
12
- export type HostRpcResult<K extends RpcEndpoint> = RpcResult<RpcValues[K]>;
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/client/index.tsx"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { Context } from '@deepseek-ai/cordis'\nimport { ClientTerminals } from '@deepseek-ai/dsh-api-terminal-controller/client'\n\nexport const inject = ['remote', 'remote.terminal', 'connection']\nconst ROUTE = '/api/dsh-remote-terminal'\n\ninterface Handle extends AsyncIterable<unknown> {\n send(item: unknown): void\n end(): void\n dispose(): void\n}\n\nexport function apply(ctx: Context): void {\n const original = (ctx.remote as any).terminal\n const routed = route(original)\n new ClientTerminals(ctx as any, routed)\n}\n\nfunction route(original: any): any {\n const mirrored = new Map<string, Promise<boolean>>()\n const isMirror = (sessionId: string): Promise<boolean> => {\n let pending = mirrored.get(sessionId)\n if (pending === undefined) {\n pending = post({ action: 'context', sessionId })\n .then(value => (value as { mirrored?: boolean } | undefined)?.mirrored === true)\n .catch(() => false)\n mirrored.set(sessionId, pending)\n }\n return pending\n }\n const unary = (name: string) => async (...args: any[]) => {\n const sessionId = String(args[0])\n if (!await isMirror(sessionId)) return original[name](...args)\n const value = await post({ action: name, sessionId, ...payload(name, args) })\n return value === undefined ? original[name](...args) : { ok: true, value }\n }\n return {\n ...original,\n environment: unary('environment'),\n shells: unary('shells'),\n list: unary('list'),\n create: unary('create'),\n rename: unary('rename'),\n close: unary('close'),\n retain: (sessionId: string, id: string, signal: AbortSignal) => {\n const local = original.retain(sessionId, id, signal)\n return (async function* () {\n if (await isMirror(sessionId)) {\n yield { type: 'retained' }\n return\n }\n yield* local\n })()\n },\n attach: (sessionId: string, id: string, signal: AbortSignal) => new RoutedHandle(original, sessionId, id, signal, isMirror),\n }\n}\n\nfunction payload(name: string, args: any[]): Record<string, unknown> {\n if (name === 'create') {\n const request = args[1] ?? {}\n return { id: request.id, cols: request.cols, rows: request.rows, shellPath: request.shellPath }\n }\n if (name === 'rename') return { id: args[1], title: args[2] }\n if (name === 'close') return { id: args[1] }\n return {}\n}\n\nasync function post(payload: Record<string, unknown>): Promise<any> {\n const response = await fetch(ROUTE, {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!response.ok) return undefined\n const body = await response.json() as { ok?: boolean; value?: unknown; mirrored?: boolean }\n if (body.mirrored !== undefined) return body\n return body.ok === true ? body.value : undefined\n}\n\nclass RoutedHandle implements Handle {\n #original: any\n #sessionId: string\n #id: string\n #signal: AbortSignal\n #isMirror: (sessionId: string) => Promise<boolean>\n #handle: Handle | undefined\n #pending: unknown[] = []\n #controller = new AbortController()\n #opening: Promise<Handle> | undefined\n\n constructor(original: any, sessionId: string, id: string, signal: AbortSignal, isMirror: (sessionId: string) => Promise<boolean>) {\n this.#original = original\n this.#sessionId = sessionId\n this.#id = id\n this.#signal = signal\n this.#isMirror = isMirror\n signal.addEventListener('abort', () => this.#controller.abort(), { once: true })\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<unknown> {\n const handle = await this.#open()\n yield* handle\n }\n\n send(item: unknown): void {\n if (this.#handle !== undefined) this.#handle.send(item)\n else this.#pending.push(item)\n }\n\n end(): void { this.#handle?.end() }\n\n dispose(): void {\n this.#controller.abort()\n this.#handle?.dispose()\n }\n\n async #open(): Promise<Handle> {\n if (this.#handle !== undefined) return this.#handle\n this.#opening ??= (async () => {\n const remote = await this.#isMirror(this.#sessionId)\n const handle: Handle = remote\n ? remoteHandle(this.#sessionId, this.#id, this.#controller.signal)\n : this.#original.attach(this.#sessionId, this.#id, this.#signal)\n this.#handle = handle\n for (const item of this.#pending.splice(0)) handle.send(item)\n return handle\n })()\n return this.#opening\n }\n}\n\nfunction remoteHandle(sessionId: string, id: string, signal: AbortSignal): Handle {\n let controller = new AbortController()\n const abort = (): void => controller.abort()\n signal.addEventListener('abort', abort, { once: true })\n return {\n async *[Symbol.asyncIterator]() {\n const response = await fetch(ROUTE, {\n method: 'POST',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json', accept: 'text/event-stream' },\n body: JSON.stringify({ action: 'attach', sessionId, id }),\n signal: controller.signal,\n })\n if (!response.ok || response.body === null) throw new Error('remote terminal attach failed')\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n try {\n for (;;) {\n const { done, value } = await reader.read()\n buffer += decoder.decode(value, { stream: !done })\n let newline = buffer.indexOf('\\n')\n while (newline >= 0) {\n const line = buffer.slice(0, newline).replace(/\\r$/u, '')\n buffer = buffer.slice(newline + 1)\n if (line.startsWith('data: ')) {\n const parsed = JSON.parse(line.slice(6).trim())\n if (parsed.message !== undefined && parsed.type === undefined) throw new Error(parsed.message)\n yield parsed\n }\n newline = buffer.indexOf('\\n')\n }\n if (done) return\n }\n } finally {\n reader.releaseLock()\n }\n },\n send(item: unknown) {\n const input = item as { type?: string; data?: string; cols?: number; rows?: number; sequence?: number }\n void post(input.type === 'write'\n ? { action: 'input', sessionId, id, data: input.data ?? '', sequence: input.sequence ?? 0 }\n : { action: 'resize', sessionId, id, cols: input.cols ?? 80, rows: input.rows ?? 24, sequence: input.sequence ?? 0 })\n },\n end() {},\n dispose() { controller.abort() },\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["payload"]
7
- }