dsh-remote-local-plugin 0.8.4 → 0.8.6
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 +1 -1
- package/assets/goldenpigeon-dsh-remote-agent-0.2.3.tgz +0 -0
- package/lib/client.js +22 -0
- package/lib/client.js.map +2 -2
- package/lib/index.js +167 -3
- package/lib/types/host-api/contract.d.ts +5 -1
- package/lib/types/remote/restart.d.ts +7 -0
- package/lib/types/service.d.ts +2 -0
- package/package.json +1 -1
- package/assets/dsh-remote-agent-0.2.2.tgz +0 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Remote development that appears as ordinary local sessions.
|
|
|
7
7
|
This plugin targets the official **DSH Desktop** application. Tested/supported versions:
|
|
8
8
|
|
|
9
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.
|
|
10
|
+
- Plugin [`0.8.6`](https://www.npmjs.com/package/dsh-remote-local-plugin)
|
|
11
11
|
- Remote DSH [`0.2.1-rc.2`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-rc.2)
|
|
12
12
|
- Remote [Node.js `22.19+`](https://nodejs.org/en/download) on Linux/macOS
|
|
13
13
|
|
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -56,6 +56,7 @@ function createHostsApi(ctx) {
|
|
|
56
56
|
disconnect: (hostId) => call("host/disconnect", { hostId }),
|
|
57
57
|
refresh: (hostId) => call("host/refresh", { hostId }),
|
|
58
58
|
provision: (hostId) => call("host/provision", { hostId }),
|
|
59
|
+
restart: (hostId) => call("host/restart", { hostId }),
|
|
59
60
|
workspaces: (hostId) => call("host/workspaces", { hostId }),
|
|
60
61
|
listDirectories: (hostId, path) => call("host/directory/list", { hostId, ...path === void 0 ? {} : { path } }),
|
|
61
62
|
listLocalDirectories: (path) => call("local/directory/list", { ...path === void 0 ? {} : { path } }),
|
|
@@ -133,6 +134,7 @@ var DETAIL_KEYS = {
|
|
|
133
134
|
"\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026": "detail.installing",
|
|
134
135
|
"\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026": "detail.connecting",
|
|
135
136
|
"\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026": "detail.reprovisioning",
|
|
137
|
+
"\u6B63\u5728\u91CD\u542F\u8FDC\u7AEF DSH\u2026": "detail.restarting",
|
|
136
138
|
"SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026": "detail.reconnecting",
|
|
137
139
|
"\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026": "detail.preparing",
|
|
138
140
|
"\u6B63\u5728\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026": "detail.proxyPreparing",
|
|
@@ -518,6 +520,20 @@ function RemoteDevelopmentPanel({ api, t }) {
|
|
|
518
520
|
}
|
|
519
521
|
)
|
|
520
522
|
] }),
|
|
523
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
524
|
+
"button",
|
|
525
|
+
{
|
|
526
|
+
type: "button",
|
|
527
|
+
className: "dshr-button",
|
|
528
|
+
disabled: busy !== null || host.phase === "installing",
|
|
529
|
+
onClick: () => {
|
|
530
|
+
void run(`restart:${host.id}`, async () => {
|
|
531
|
+
await api.restart(host.id);
|
|
532
|
+
});
|
|
533
|
+
},
|
|
534
|
+
children: busy === `restart:${host.id}` ? t("settings.restarting") : t("settings.restart")
|
|
535
|
+
}
|
|
536
|
+
),
|
|
521
537
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
522
538
|
"button",
|
|
523
539
|
{
|
|
@@ -757,6 +773,8 @@ var zh = {
|
|
|
757
773
|
"settings.connect": "\u8FDE\u63A5",
|
|
758
774
|
"settings.installing": "\u6B63\u5728\u5B89\u88C5\u2026",
|
|
759
775
|
"settings.connecting": "\u6B63\u5728\u8FDE\u63A5\u2026",
|
|
776
|
+
"settings.restart": "\u91CD\u542F\u8FDC\u7AEF DSH",
|
|
777
|
+
"settings.restarting": "\u91CD\u542F\u4E2D\u2026",
|
|
760
778
|
"settings.reprovision": "\u91CD\u65B0\u5B89\u88C5",
|
|
761
779
|
"settings.remove": "\u5220\u9664",
|
|
762
780
|
"settings.workspacePathPlaceholder": "\u8FDC\u7AEF\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/user/project",
|
|
@@ -781,6 +799,7 @@ var zh = {
|
|
|
781
799
|
"detail.installing": "\u6B63\u5728\u8FDC\u7AEF\u5B89\u88C5 DSH \u4E0E dsh-remote-agent\uFF0C\u9996\u6B21\u7EA6 1\u20133 \u5206\u949F\u2026",
|
|
782
800
|
"detail.connecting": "\u6B63\u5728\u5EFA\u7ACB SSH \u96A7\u9053\u5E76\u6821\u9A8C\u8FDC\u7AEF agent\u2026",
|
|
783
801
|
"detail.reprovisioning": "\u6B63\u5728\u91CD\u65B0\u5B89\u88C5\u8FDC\u7AEF agent\u2026",
|
|
802
|
+
"detail.restarting": "\u6B63\u5728\u91CD\u542F\u8FDC\u7AEF DSH\u2026",
|
|
784
803
|
"detail.reconnecting": "SSH \u96A7\u9053\u65AD\u5F00\uFF0C\u7B49\u5F85\u91CD\u8FDE\u2026",
|
|
785
804
|
"detail.connectedProxy": "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7ECF\u672C\u5730\u51ED\u636E\u4EE3\u7406\u8F6C\u53D1",
|
|
786
805
|
"detail.connectedDirect": "\u5DF2\u8FDE\u63A5\uFF1B\u6A21\u578B\u8BF7\u6C42\u7531\u8FDC\u7AEF\u76F4\u8FDE DeepSeek",
|
|
@@ -881,6 +900,8 @@ var en = {
|
|
|
881
900
|
"settings.connect": "Connect",
|
|
882
901
|
"settings.installing": "Installing\u2026",
|
|
883
902
|
"settings.connecting": "Connecting\u2026",
|
|
903
|
+
"settings.restart": "Restart remote DSH",
|
|
904
|
+
"settings.restarting": "Restarting\u2026",
|
|
884
905
|
"settings.reprovision": "Reinstall",
|
|
885
906
|
"settings.remove": "Remove",
|
|
886
907
|
"settings.workspacePathPlaceholder": "Absolute remote path, e.g. /home/user/project",
|
|
@@ -905,6 +926,7 @@ var en = {
|
|
|
905
926
|
"detail.installing": "Installing DSH and dsh-remote-agent remotely; this takes about 1\u20133 minutes\u2026",
|
|
906
927
|
"detail.connecting": "Opening the SSH tunnel and checking the remote agent\u2026",
|
|
907
928
|
"detail.reprovisioning": "Reinstalling the remote agent\u2026",
|
|
929
|
+
"detail.restarting": "Restarting remote DSH\u2026",
|
|
908
930
|
"detail.reconnecting": "SSH tunnel closed; waiting to reconnect\u2026",
|
|
909
931
|
"detail.connectedProxy": "Connected; model requests go through the local credential proxy",
|
|
910
932
|
"detail.connectedDirect": "Connected; the remote host calls DeepSeek directly",
|
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, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileRead: (sessionId: string, path: string, offset?: number, limit?: number, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileStat: (sessionId: string, path: string, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileBytes: (sessionId: string, path: string, offset?: number, length?: number, signal?: AbortSignal) => 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, signal) => call('host/files/list', { sessionId, path }, signal),\n fileRead: (sessionId, path, offset, limit, signal) => call('host/files/read', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...limit === undefined ? {} : { limit },\n }, signal),\n fileStat: (sessionId, path, signal) => call('host/files/stat', { sessionId, path }, signal),\n fileBytes: (sessionId, path, offset, length, signal) => call('host/files/bytes', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...length === undefined ? {} : { length },\n }, signal),\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 signal?: AbortSignal,\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 ...signal === undefined ? {} : { signal },\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\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026': 'detail.proxyPreparing',\n '\u5DF2\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026': 'detail.proxy',\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, signal)\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, signal)\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, signal)\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, signal)\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.proxyPreparing': '\u6B63\u5728\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026',\n 'detail.proxy': '\u5DF2\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\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.proxyPreparing': 'Configuring the remote installation proxy\u2026',\n 'detail.proxy': 'Remote installation proxy configured\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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",
|
|
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/restart'\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/restart': { 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/restart': 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 restart: (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, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileRead: (sessionId: string, path: string, offset?: number, limit?: number, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileStat: (sessionId: string, path: string, signal?: AbortSignal) => Promise<MirrorFileResult>\n readonly fileBytes: (sessionId: string, path: string, offset?: number, length?: number, signal?: AbortSignal) => 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 restart: hostId => call('host/restart', { 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, signal) => call('host/files/list', { sessionId, path }, signal),\n fileRead: (sessionId, path, offset, limit, signal) => call('host/files/read', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...limit === undefined ? {} : { limit },\n }, signal),\n fileStat: (sessionId, path, signal) => call('host/files/stat', { sessionId, path }, signal),\n fileBytes: (sessionId, path, offset, length, signal) => call('host/files/bytes', {\n sessionId,\n path,\n ...offset === undefined ? {} : { offset },\n ...length === undefined ? {} : { length },\n }, signal),\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 signal?: AbortSignal,\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 ...signal === undefined ? {} : { signal },\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 '\u6B63\u5728\u91CD\u542F\u8FDC\u7AEF DSH\u2026': 'detail.restarting',\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\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026': 'detail.proxyPreparing',\n '\u5DF2\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026': 'detail.proxy',\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(`restart:${host.id}`, async () => { await api.restart(host.id) }) }}\n >\n {busy === `restart:${host.id}` ? t('settings.restarting') : t('settings.restart')}\n </button>\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, signal)\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, signal)\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, signal)\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, signal)\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.restart': '\u91CD\u542F\u8FDC\u7AEF DSH',\n 'settings.restarting': '\u91CD\u542F\u4E2D\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.restarting': '\u6B63\u5728\u91CD\u542F\u8FDC\u7AEF DSH\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.proxyPreparing': '\u6B63\u5728\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026',\n 'detail.proxy': '\u5DF2\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\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.restart': 'Restart remote DSH',\n 'settings.restarting': 'Restarting\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.restarting': 'Restarting remote DSH\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.proxyPreparing': 'Configuring the remote installation proxy\u2026',\n 'detail.proxy': 'Remote installation proxy configured\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.
|
|
1
|
+
/* dsh-remote-local-plugin@0.8.6 — bundled host entry */
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
@@ -295,6 +295,8 @@ async function dispatch(ctx, service, mirrors, endpoint, payload, signal) {
|
|
|
295
295
|
return service.refresh(requireStringField(payload, "hostId"));
|
|
296
296
|
case "host/provision":
|
|
297
297
|
return service.provision(requireStringField(payload, "hostId"));
|
|
298
|
+
case "host/restart":
|
|
299
|
+
return service.restart(requireStringField(payload, "hostId"));
|
|
298
300
|
case "host/workspaces": {
|
|
299
301
|
const hostId = requireStringField(payload, "hostId");
|
|
300
302
|
const workspaces = await service.workspaces(hostId);
|
|
@@ -3205,6 +3207,9 @@ PNPM_VERSION=${shellLiteral(options.pnpmVersion)}
|
|
|
3205
3207
|
NODE_VERSION=${shellLiteral(options.nodeVersion)}
|
|
3206
3208
|
AUTO_INSTALL_NODE=${options.autoInstallNode ? "1" : "0"}
|
|
3207
3209
|
AGENT_TARBALL=${shellLiteral(options.agentTarball)}
|
|
3210
|
+
AGENT_NPM_SPEC=${options.agentPackageSpec === void 0 ? "''" : shellLiteral(options.agentPackageSpec)}
|
|
3211
|
+
AGENT_ARTIFACT_DIR="$INSTALL_DIR/artifacts"
|
|
3212
|
+
AGENT_ARTIFACT="$AGENT_ARTIFACT_DIR/$(basename "$AGENT_TARBALL")"
|
|
3208
3213
|
MODEL_BASE_URL=${options.modelBaseUrl === void 0 ? "''" : shellLiteral(options.modelBaseUrl)}
|
|
3209
3214
|
MODEL_API_KEY=${options.modelApiKey === void 0 ? "''" : shellLiteral(options.modelApiKey)}
|
|
3210
3215
|
PROXY_URL=${options.proxyUrl === void 0 ? "''" : shellLiteral(options.proxyUrl)}
|
|
@@ -3317,8 +3322,57 @@ if [ ! -x "$DSH_BIN" ]; then
|
|
|
3317
3322
|
exit 24
|
|
3318
3323
|
fi
|
|
3319
3324
|
|
|
3325
|
+
PROFILE_DIR="$DSH_HOME/profiles/$PROFILE"
|
|
3326
|
+
LEGACY_CHANGED=0
|
|
3327
|
+
if [ -f "$PROFILE_DIR/package.json" ]; then
|
|
3328
|
+
if node - "$PROFILE_DIR/package.json" <<'DSH_REMOTE_MIGRATE'
|
|
3329
|
+
const fs = require('node:fs')
|
|
3330
|
+
const file = process.argv[2]
|
|
3331
|
+
const raw = fs.readFileSync(file, 'utf8')
|
|
3332
|
+
const data = JSON.parse(raw)
|
|
3333
|
+
let changed = false
|
|
3334
|
+
if (data.dependencies !== undefined && Object.hasOwn(data.dependencies, 'dsh-remote-agent')) {
|
|
3335
|
+
delete data.dependencies['dsh-remote-agent']
|
|
3336
|
+
changed = true
|
|
3337
|
+
}
|
|
3338
|
+
const bundles = data.dsh?.profile?.bundles
|
|
3339
|
+
if (Array.isArray(bundles)) {
|
|
3340
|
+
const next = bundles.filter(name => name !== 'dsh-remote-agent')
|
|
3341
|
+
if (next.length !== bundles.length) {
|
|
3342
|
+
data.dsh.profile.bundles = next
|
|
3343
|
+
changed = true
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
if (!changed) process.exit(1)
|
|
3347
|
+
fs.writeFileSync(file + '.legacy-backup', raw)
|
|
3348
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\\n')
|
|
3349
|
+
DSH_REMOTE_MIGRATE
|
|
3350
|
+
then
|
|
3351
|
+
LEGACY_CHANGED=1
|
|
3352
|
+
else
|
|
3353
|
+
MIGRATE_STATUS=$?
|
|
3354
|
+
if [ "$MIGRATE_STATUS" -ne 1 ]; then
|
|
3355
|
+
echo "dsh-remote: could not migrate the legacy remote-agent profile entry" >&2
|
|
3356
|
+
exit 26
|
|
3357
|
+
fi
|
|
3358
|
+
fi
|
|
3359
|
+
fi
|
|
3360
|
+
if [ "$LEGACY_CHANGED" -eq 1 ]; then
|
|
3361
|
+
if [ -f "$PROFILE_DIR/pnpm-lock.yaml" ]; then
|
|
3362
|
+
mv "$PROFILE_DIR/pnpm-lock.yaml" "$PROFILE_DIR/pnpm-lock.yaml.legacy-backup"
|
|
3363
|
+
fi
|
|
3364
|
+
echo "dsh-remote: removed the legacy dsh-remote-agent profile entry"
|
|
3365
|
+
fi
|
|
3366
|
+
|
|
3320
3367
|
echo "dsh-remote: adding plugin to profile $PROFILE"
|
|
3321
|
-
|
|
3368
|
+
mkdir -p "$AGENT_ARTIFACT_DIR"
|
|
3369
|
+
cp -f "$AGENT_TARBALL" "$AGENT_ARTIFACT"
|
|
3370
|
+
if [ -n "$AGENT_NPM_SPEC" ] && "$DSH_BIN" plugin --profile "$PROFILE" add "$AGENT_NPM_SPEC"; then
|
|
3371
|
+
echo "dsh-remote: installed agent from npm"
|
|
3372
|
+
else
|
|
3373
|
+
echo "dsh-remote: npm agent unavailable; installing the bundled agent" >&2
|
|
3374
|
+
"$DSH_BIN" plugin --profile "$PROFILE" add "$AGENT_ARTIFACT"
|
|
3375
|
+
fi
|
|
3322
3376
|
|
|
3323
3377
|
echo "dsh-remote: starting dsh web"
|
|
3324
3378
|
${startBlock}
|
|
@@ -3542,6 +3596,7 @@ async function provisionRemoteAgent(options) {
|
|
|
3542
3596
|
agentTarball: remoteTarball,
|
|
3543
3597
|
...options.modelBaseUrl === void 0 ? {} : { modelBaseUrl: options.modelBaseUrl },
|
|
3544
3598
|
...options.modelApiKey === void 0 ? {} : { modelApiKey: options.modelApiKey },
|
|
3599
|
+
...options.agentPackageSpec === void 0 ? {} : { agentPackageSpec: options.agentPackageSpec },
|
|
3545
3600
|
...options.proxyUrl === void 0 ? {} : { proxyUrl: options.proxyUrl },
|
|
3546
3601
|
...options.noProxy === void 0 ? {} : { noProxy: options.noProxy }
|
|
3547
3602
|
});
|
|
@@ -3703,6 +3758,8 @@ var RemoteProvisioner = class {
|
|
|
3703
3758
|
*/
|
|
3704
3759
|
async ensureInstalled(target, options = {}) {
|
|
3705
3760
|
const tarball = findAgentTarball();
|
|
3761
|
+
const version = /dsh-remote-agent-(.+)\.tgz$/u.exec(tarball)?.[1];
|
|
3762
|
+
const agentPackageSpec = version === void 0 ? void 0 : `@goldenpigeon/dsh-remote-agent@${version}`;
|
|
3706
3763
|
options.onProgress?.("\u6B63\u5728\u51C6\u5907\u8FDC\u7AEF\u5B89\u88C5\u73AF\u5883\u2026");
|
|
3707
3764
|
const proxyUrl = this.#config.remoteInstallProxy.trim() || void 0;
|
|
3708
3765
|
if (proxyUrl !== void 0) options.onProgress?.("\u6B63\u5728\u914D\u7F6E\u8FDC\u7AEF\u5B89\u88C5\u4EE3\u7406\u2026");
|
|
@@ -3710,6 +3767,7 @@ var RemoteProvisioner = class {
|
|
|
3710
3767
|
await provisionRemoteAgent({
|
|
3711
3768
|
target: target.canonical,
|
|
3712
3769
|
agentTarballPath: tarball,
|
|
3770
|
+
...agentPackageSpec === void 0 ? {} : { agentPackageSpec },
|
|
3713
3771
|
sshBinary: this.#config.sshBinary,
|
|
3714
3772
|
strictHostKeyChecking: this.#config.strictHostKeyChecking,
|
|
3715
3773
|
profile: "web",
|
|
@@ -3815,6 +3873,40 @@ function findAgentTarball() {
|
|
|
3815
3873
|
);
|
|
3816
3874
|
}
|
|
3817
3875
|
|
|
3876
|
+
// src/remote/restart.ts
|
|
3877
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
3878
|
+
import { promisify as promisify3 } from "node:util";
|
|
3879
|
+
var execFileAsync3 = promisify3(execFile4);
|
|
3880
|
+
var RESTART_COMMAND = [
|
|
3881
|
+
"if command -v systemctl >/dev/null 2>&1 && systemctl --user cat dsh-remote-agent.service >/dev/null 2>&1; then",
|
|
3882
|
+
" systemctl --user restart dsh-remote-agent.service",
|
|
3883
|
+
"else",
|
|
3884
|
+
' echo "dsh-remote: dsh-remote-agent.service was not found; use Reinstall or restart the remote DSH manually" >&2',
|
|
3885
|
+
" exit 4",
|
|
3886
|
+
"fi"
|
|
3887
|
+
].join("\n");
|
|
3888
|
+
async function restartRemoteAgent(target, options) {
|
|
3889
|
+
try {
|
|
3890
|
+
await execFileAsync3(
|
|
3891
|
+
options.sshBinary,
|
|
3892
|
+
[
|
|
3893
|
+
...baseSshArgs(target, { timeoutMs: 6e4, strictHostKeyChecking: options.strictHostKeyChecking }),
|
|
3894
|
+
target.destination,
|
|
3895
|
+
RESTART_COMMAND
|
|
3896
|
+
],
|
|
3897
|
+
{ timeout: 75e3, maxBuffer: 64 * 1024, encoding: "utf8", windowsHide: true }
|
|
3898
|
+
);
|
|
3899
|
+
} catch (error) {
|
|
3900
|
+
const failure = error;
|
|
3901
|
+
const detail = (failure.stderr ?? "").trim().split("\n")[0] ?? "";
|
|
3902
|
+
throw new LocalError(
|
|
3903
|
+
"agent/unavailable",
|
|
3904
|
+
failure.killed === true ? "remote DSH restart timed out" : detail !== "" ? `remote DSH restart failed: ${detail}` : `remote DSH restart failed: ${failure.message}`,
|
|
3905
|
+
{ details: { host: target.canonical }, retryable: true, cause: error }
|
|
3906
|
+
);
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3818
3910
|
// src/ssh/target.ts
|
|
3819
3911
|
var USER_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}$/u;
|
|
3820
3912
|
var HOST_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,252}[A-Za-z0-9])?$/u;
|
|
@@ -4278,6 +4370,60 @@ var RemoteHostsService = class {
|
|
|
4278
4370
|
}
|
|
4279
4371
|
return this.connect(id);
|
|
4280
4372
|
}
|
|
4373
|
+
/** Restart the remote DSH service and reconnect without reinstalling. */
|
|
4374
|
+
async restart(id) {
|
|
4375
|
+
this.#assertAlive();
|
|
4376
|
+
const record = this.#requireRecord(id);
|
|
4377
|
+
const target = parseSshTarget(record.sshTarget);
|
|
4378
|
+
this.#manualDisconnect.delete(id);
|
|
4379
|
+
this.#clearReconnect(id);
|
|
4380
|
+
await this.#stopTransport(id);
|
|
4381
|
+
this.#setRuntime(id, {
|
|
4382
|
+
...this.#runtimeFor(id),
|
|
4383
|
+
phase: "connecting",
|
|
4384
|
+
localPort: null,
|
|
4385
|
+
localUrl: null,
|
|
4386
|
+
hostInfo: null,
|
|
4387
|
+
connectedAt: null,
|
|
4388
|
+
error: null,
|
|
4389
|
+
errorCode: null,
|
|
4390
|
+
detail: "\u6B63\u5728\u91CD\u542F\u8FDC\u7AEF DSH\u2026"
|
|
4391
|
+
});
|
|
4392
|
+
try {
|
|
4393
|
+
await restartRemoteAgent(target, {
|
|
4394
|
+
sshBinary: this.config.sshBinary,
|
|
4395
|
+
strictHostKeyChecking: this.config.strictHostKeyChecking
|
|
4396
|
+
});
|
|
4397
|
+
} catch (error) {
|
|
4398
|
+
const failure2 = this.#markError(id, error, "agent/unavailable");
|
|
4399
|
+
throw failure2;
|
|
4400
|
+
}
|
|
4401
|
+
const deadline = Date.now() + 9e4;
|
|
4402
|
+
let lastError;
|
|
4403
|
+
while (Date.now() < deadline) {
|
|
4404
|
+
try {
|
|
4405
|
+
const discovery = await this.#discover(target);
|
|
4406
|
+
if (isAgentLive(discovery.state)) {
|
|
4407
|
+
await this.#saveRecord(id, {
|
|
4408
|
+
remoteAgentPort: discovery.state.port,
|
|
4409
|
+
token: discovery.state.token
|
|
4410
|
+
});
|
|
4411
|
+
return this.connect(id);
|
|
4412
|
+
}
|
|
4413
|
+
} catch (error) {
|
|
4414
|
+
lastError = error;
|
|
4415
|
+
}
|
|
4416
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
4417
|
+
}
|
|
4418
|
+
const failure = new LocalError("agent/unavailable", "\u8FDC\u7AEF DSH \u91CD\u542F\u540E\u672A\u80FD\u5728 90 \u79D2\u5185\u5C31\u7EEA", {
|
|
4419
|
+
details: {
|
|
4420
|
+
host: target.canonical,
|
|
4421
|
+
reason: lastError instanceof Error ? lastError.message : String(lastError ?? "state file not ready")
|
|
4422
|
+
},
|
|
4423
|
+
retryable: true
|
|
4424
|
+
});
|
|
4425
|
+
throw this.#markError(id, failure, "agent/unavailable");
|
|
4426
|
+
}
|
|
4281
4427
|
/** List remote workspaces. */
|
|
4282
4428
|
async workspaces(id) {
|
|
4283
4429
|
const client = this.#requireClient(id);
|
|
@@ -4838,7 +4984,7 @@ var renderJson = (_args, value) => [
|
|
|
4838
4984
|
function toJsonObject(value) {
|
|
4839
4985
|
return JSON.parse(JSON.stringify(value));
|
|
4840
4986
|
}
|
|
4841
|
-
var HOST_WRITE_TOOLS = /* @__PURE__ */ new Set(["remote_host_add", "remote_host_connect", "remote_host_provision"]);
|
|
4987
|
+
var HOST_WRITE_TOOLS = /* @__PURE__ */ new Set(["remote_host_add", "remote_host_connect", "remote_host_provision", "remote_host_restart"]);
|
|
4842
4988
|
function installRemoteToolGuards(ctx) {
|
|
4843
4989
|
ctx.effect(() => ctx.on("tools/pre-execute", (exec, next) => {
|
|
4844
4990
|
if (!HOST_WRITE_TOOLS.has(exec.name)) return next();
|
|
@@ -4939,6 +5085,24 @@ function registerRemoteTools(ctx, service) {
|
|
|
4939
5085
|
return { host: toJsonObject(await service.provision(args.hostId)) };
|
|
4940
5086
|
}
|
|
4941
5087
|
}));
|
|
5088
|
+
ctx.tools.register(defineTool({
|
|
5089
|
+
name: "remote_host_restart",
|
|
5090
|
+
description: "Restart the remote DSH service without reinstalling it, preserving installed remote plugins, then reconnect. Requires the installer-owned systemd user unit; use remote_host_provision for nohup installs.",
|
|
5091
|
+
parameters: {
|
|
5092
|
+
hostId: { type: "string", required: true, description: "Registered host id." }
|
|
5093
|
+
},
|
|
5094
|
+
output: {
|
|
5095
|
+
schema: {
|
|
5096
|
+
type: "object",
|
|
5097
|
+
additionalProperties: false,
|
|
5098
|
+
properties: { host: { type: "object", additionalProperties: true, required: true } }
|
|
5099
|
+
},
|
|
5100
|
+
render: renderJson
|
|
5101
|
+
},
|
|
5102
|
+
async execute(args) {
|
|
5103
|
+
return { host: toJsonObject(await service.restart(args.hostId)) };
|
|
5104
|
+
}
|
|
5105
|
+
}));
|
|
4942
5106
|
ctx.tools.register(defineTool({
|
|
4943
5107
|
name: "remote_workspaces_list",
|
|
4944
5108
|
description: "List workspaces registered on a connected remote DSH host. Use the returned workspaceId with remote_sessions_list.",
|
|
@@ -12,7 +12,7 @@ import type { AcceptedResult, CreateSessionResult, HostInfo, PromptDeliveryMode,
|
|
|
12
12
|
/** Exact authenticated Fetch route owned by the local plugin. */
|
|
13
13
|
export declare const HOST_API_PATH = "/api/dsh-remote";
|
|
14
14
|
/** Endpoint names. */
|
|
15
|
-
export type HostApiEndpoint = 'state' | 'target/validate' | 'host/add' | 'host/remove' | 'host/connect' | 'host/disconnect' | 'host/refresh' | 'host/provision' | 'host/workspaces' | 'host/workspace/create' | 'host/directory/list' | 'local/directory/list' | 'local/workspace/create' | 'host/sessions' | 'host/session' | 'host/session/create' | 'host/session/prompt' | 'host/session/cancel' | 'host/web-url' | 'host/files/list' | 'host/files/read' | 'host/files/stat' | 'host/files/bytes';
|
|
15
|
+
export type HostApiEndpoint = 'state' | 'target/validate' | 'host/add' | 'host/remove' | 'host/connect' | 'host/disconnect' | 'host/refresh' | 'host/provision' | 'host/restart' | 'host/workspaces' | 'host/workspace/create' | 'host/directory/list' | 'local/directory/list' | 'local/workspace/create' | 'host/sessions' | 'host/session' | 'host/session/create' | 'host/session/prompt' | 'host/session/cancel' | 'host/web-url' | 'host/files/list' | 'host/files/read' | 'host/files/stat' | 'host/files/bytes';
|
|
16
16
|
/** Browser-safe state snapshot. */
|
|
17
17
|
export interface RemoteHostsState {
|
|
18
18
|
readonly protocolVersion: number;
|
|
@@ -94,6 +94,9 @@ export interface HostApiPayloads {
|
|
|
94
94
|
'host/provision': {
|
|
95
95
|
hostId: string;
|
|
96
96
|
};
|
|
97
|
+
'host/restart': {
|
|
98
|
+
hostId: string;
|
|
99
|
+
};
|
|
97
100
|
'host/workspaces': {
|
|
98
101
|
hostId: string;
|
|
99
102
|
};
|
|
@@ -177,6 +180,7 @@ export interface HostApiValues {
|
|
|
177
180
|
'host/disconnect': HostView;
|
|
178
181
|
'host/refresh': HostView;
|
|
179
182
|
'host/provision': HostView;
|
|
183
|
+
'host/restart': HostView;
|
|
180
184
|
'host/workspaces': WorkspacesResult;
|
|
181
185
|
'host/workspace/create': RemoteWorkspace;
|
|
182
186
|
'host/directory/list': {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParsedSshTarget } from '../ssh/target.ts';
|
|
2
|
+
export interface RestartRemoteOptions {
|
|
3
|
+
readonly sshBinary: string;
|
|
4
|
+
readonly strictHostKeyChecking: 'yes' | 'no' | 'ask' | 'accept-new';
|
|
5
|
+
}
|
|
6
|
+
/** Restart the installer-owned remote DSH unit. */
|
|
7
|
+
export declare function restartRemoteAgent(target: ParsedSshTarget, options: RestartRemoteOptions): Promise<void>;
|
package/lib/types/service.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ export declare class RemoteHostsService {
|
|
|
55
55
|
refresh(id: string): Promise<HostView>;
|
|
56
56
|
/** Force a fresh remote install/restart, then reconnect. */
|
|
57
57
|
provision(id: string): Promise<HostView>;
|
|
58
|
+
/** Restart the remote DSH service and reconnect without reinstalling. */
|
|
59
|
+
restart(id: string): Promise<HostView>;
|
|
58
60
|
/** List remote workspaces. */
|
|
59
61
|
workspaces(id: string): Promise<readonly RemoteWorkspace[]>;
|
|
60
62
|
/** List remote sessions, optionally within one workspace. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-remote-local-plugin",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
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",
|
|
Binary file
|