ds-harness-remote 0.4.13 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +2 -2
  2. package/dist/acp.d.ts +75 -0
  3. package/dist/acp.d.ts.map +1 -0
  4. package/dist/acp.js +61 -0
  5. package/dist/acp.js.map +1 -0
  6. package/dist/client-runtime.d.ts +11 -0
  7. package/dist/client-runtime.d.ts.map +1 -1
  8. package/dist/client-runtime.js +24 -0
  9. package/dist/client-runtime.js.map +1 -1
  10. package/dist/client-secure-transport.d.ts +1 -0
  11. package/dist/client-secure-transport.d.ts.map +1 -1
  12. package/dist/client-secure-transport.js +12 -5
  13. package/dist/client-secure-transport.js.map +1 -1
  14. package/dist/client.d.ts.map +1 -1
  15. package/dist/client.github.js +474 -76
  16. package/dist/client.js +474 -76
  17. package/dist/client.js.map +1 -1
  18. package/dist/codex/method-policy.d.ts +10 -10
  19. package/dist/config.d.ts +36 -0
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +9 -0
  22. package/dist/config.js.map +1 -1
  23. package/dist/connection-controller.d.ts +5 -0
  24. package/dist/connection-controller.d.ts.map +1 -1
  25. package/dist/connection-controller.js +16 -1
  26. package/dist/connection-controller.js.map +1 -1
  27. package/dist/control-route.d.ts +10 -1
  28. package/dist/control-route.d.ts.map +1 -1
  29. package/dist/control-route.js +17 -4
  30. package/dist/control-route.js.map +1 -1
  31. package/dist/control-runtime.d.ts +12 -1
  32. package/dist/control-runtime.d.ts.map +1 -1
  33. package/dist/control-runtime.js +95 -1
  34. package/dist/control-runtime.js.map +1 -1
  35. package/dist/control-stream.d.ts +67 -0
  36. package/dist/control-stream.d.ts.map +1 -0
  37. package/dist/control-stream.js +179 -0
  38. package/dist/control-stream.js.map +1 -0
  39. package/dist/harness-api-bridge.d.ts.map +1 -1
  40. package/dist/harness-api-bridge.js +32 -8
  41. package/dist/harness-api-bridge.js.map +1 -1
  42. package/dist/harness-remote-bridge.d.ts +5 -2
  43. package/dist/harness-remote-bridge.d.ts.map +1 -1
  44. package/dist/harness-remote-bridge.js +85 -5
  45. package/dist/harness-remote-bridge.js.map +1 -1
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1872 -588
  49. package/dist/index.js.map +4 -4
  50. package/dist/loopback-host.d.ts +20 -0
  51. package/dist/loopback-host.d.ts.map +1 -0
  52. package/dist/loopback-host.js +220 -0
  53. package/dist/loopback-host.js.map +1 -0
  54. package/dist/loopback-preview.d.ts +19 -0
  55. package/dist/loopback-preview.d.ts.map +1 -0
  56. package/dist/loopback-preview.js +232 -0
  57. package/dist/loopback-preview.js.map +1 -0
  58. package/dist/remote-typert-gateway.js +16 -4
  59. package/dist/remote-typert-gateway.js.map +1 -1
  60. package/dist/rpc-router.d.ts +7 -2
  61. package/dist/rpc-router.d.ts.map +1 -1
  62. package/dist/rpc-router.js +27 -1
  63. package/dist/rpc-router.js.map +1 -1
  64. package/dist/server-api.d.ts +6 -3
  65. package/dist/server-api.d.ts.map +1 -1
  66. package/dist/server-api.js +50 -26
  67. package/dist/server-api.js.map +1 -1
  68. package/dist/server-connection.d.ts +1 -0
  69. package/dist/server-connection.d.ts.map +1 -1
  70. package/dist/server-connection.js +54 -7
  71. package/dist/server-connection.js.map +1 -1
  72. package/dist/server-credentials.d.ts +9 -0
  73. package/dist/server-credentials.d.ts.map +1 -1
  74. package/dist/server-credentials.js +34 -0
  75. package/dist/server-credentials.js.map +1 -1
  76. package/dist/service.d.ts +14 -0
  77. package/dist/service.d.ts.map +1 -1
  78. package/dist/service.js +48 -2
  79. package/dist/service.js.map +1 -1
  80. package/dist/status-stream.d.ts +48 -0
  81. package/dist/status-stream.d.ts.map +1 -0
  82. package/dist/status-stream.js +133 -0
  83. package/dist/status-stream.js.map +1 -0
  84. package/dist/terminal-policy.d.ts +21 -0
  85. package/dist/terminal-policy.d.ts.map +1 -0
  86. package/dist/terminal-policy.js +62 -0
  87. package/dist/terminal-policy.js.map +1 -0
  88. package/dist/tui-command.js +6 -0
  89. package/dist/tui-command.js.map +1 -1
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/dsh-plugin.json +1 -1
  93. package/package.json +44 -24
  94. package/public.d.ts +2 -0
@@ -1694,7 +1694,61 @@ Minimum version required to store current data is: ` + bestVersion + `.
1694
1694
  }
1695
1695
 
1696
1696
  // src/control-route.ts
1697
- var CONTROL_RPC_PREFIX = "/ds-harness-remote";
1697
+ var CONTROL_RPC_PREFIX = "/ds-harness-remote", STATUS_STREAM_ENDPOINT = "status.events", STATUS_STREAM_PATH = `${CONTROL_RPC_PREFIX}/${STATUS_STREAM_ENDPOINT}`;
1698
+
1699
+ // src/status-stream.ts
1700
+ function parseStatus(data) {
1701
+ if (typeof data == "string")
1702
+ try {
1703
+ let parsed = JSON.parse(data);
1704
+ return parsed === null || typeof parsed != "object" ? void 0 : parsed;
1705
+ } catch {
1706
+ return;
1707
+ }
1708
+ }
1709
+ function createStatusFeed(options) {
1710
+ let listeners = /* @__PURE__ */ new Set(), pollIntervalMs = options.pollIntervalMs ?? 1500, openTimeoutMs = options.openTimeoutMs ?? 4e3, createSource = options.createSource ?? ((url) => new EventSource(url)), status, source, openTimer, pollTimer, publish = (next) => {
1711
+ status = next;
1712
+ for (let listener of [...listeners]) listener(next);
1713
+ }, teardown = () => {
1714
+ openTimer !== void 0 && (clearTimeout(openTimer), openTimer = void 0), pollTimer !== void 0 && (clearInterval(pollTimer), pollTimer = void 0), source?.close(), source = void 0;
1715
+ }, poll = () => {
1716
+ options.readStatus().then((next) => {
1717
+ pollTimer !== void 0 && publish(next);
1718
+ }).catch(() => {
1719
+ });
1720
+ }, fallbackToPolling = (reason) => {
1721
+ pollTimer !== void 0 || listeners.size === 0 || (openTimer !== void 0 && (clearTimeout(openTimer), openTimer = void 0), source?.close(), source = void 0, options.onFallback?.(reason), poll(), pollTimer = setInterval(poll, pollIntervalMs));
1722
+ }, openStream = () => {
1723
+ let opened;
1724
+ try {
1725
+ opened = createSource(options.url);
1726
+ } catch {
1727
+ fallbackToPolling("unsupported");
1728
+ return;
1729
+ }
1730
+ source = opened;
1731
+ let received = !1;
1732
+ openTimer = setTimeout(() => {
1733
+ received || fallbackToPolling("silent");
1734
+ }, openTimeoutMs), opened.onmessage = (event) => {
1735
+ received = !0, openTimer !== void 0 && (clearTimeout(openTimer), openTimer = void 0);
1736
+ let next = parseStatus(event.data);
1737
+ next !== void 0 && publish(next);
1738
+ }, opened.onerror = () => {
1739
+ opened.readyState === 2 && fallbackToPolling("unavailable");
1740
+ };
1741
+ };
1742
+ return {
1743
+ getSnapshot: () => status,
1744
+ subscribe: (listener) => (listeners.add(listener), status !== void 0 && listener(status), listeners.size === 1 && openStream(), () => {
1745
+ listeners.delete(listener), listeners.size === 0 && teardown();
1746
+ }),
1747
+ close: () => {
1748
+ listeners.clear(), teardown();
1749
+ }
1750
+ };
1751
+ }
1698
1752
 
1699
1753
  // src/client.ts
1700
1754
  var clientModuleId = "ds-harness-remote", pendingWorkspaceSelectionKey = "dsh-remote:pending-workspace-selection", deepSeekWorkspaceIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAACVBMVEUAAADy8vXx8fUsA3vyAAAAAXRSTlMAQObYZgAAAOxJREFUWMPtlsEOwyAMQxP+/6OnTZMGxHGcot3wDYgfhkJbs6urprzveGtu9d1NgEdto8w93GuA7zPS2RFg7ynsCOAokbtAmLr2YaeKgJ6fxpT8jCAC0qSqPyNoO5DXdvyouj4ClFAdYaDxK09uiBDCIYBf6CxLesnR0uF2pG+JmhCqrUb0AOvjjoQaYDTCEAAgMg5grhAeAQIB+/M15IcKlnUIp4CkTCd0AKb4zVsRSJVGYEVzT0agK107IMGMEurvromEpaX4wzk/IKw378kq4LafED6Oowxfk7AP2q8W16EdM3p29Eyvrv6vF0WIBfBziKyCAAAAAElFTkSuQmCC", gptWorkspaceIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAclBMVEX////v7+++wMf////s7O3k5OTExcrX2Ny6vMTv8PPW19yXmqRqboJmaHd3eoaqrLPs7fGMjpfb3eKlpq1eYXOxs7pWWWd+gY5HSVdRVGVub3k+QU74+Pg4OkcvMT1ISlUoKjUiIy4fICueoKkaGyUSFB21Bp+qAAAAAXRSTlMAQObYZgAABChJREFUWMPtl916qjoQhlsdEwIECEgMYCBxx/u/xT1DrFUL1LO1DtY8LSLJvPnmB4gfH//sr7TPDfvdebf/xXZbkE/4zZ0MVhGHd9zJDsv+7F3//Z69tT5wljAu3tXw+TIlzXKJlhflYmJ+5uF5WpWhb54X+C/zZCmT2wJA4dJZwus0UYjIGqWyMhVbEp4yKNRRqlv0opW5PJLJ4rFJXvP4JKDRMqturBZjkMVJFYjR6iHQDQDI4+k2k51wZbWrqwp4mR/1CVYATyko9TFGxBUqL5IvMbXSWt0T8bkGgFLqgqZBI/Uxbx5UAxLaNcCNLBpttM7wbCc1pmJOpWAsDtey68UaQMyrFgb9uxLPjqg3+nGluyKN0XUdWw0BJ0NvTFFqgzpr9J+Dh4w0Ga1qkqBNswagxdRgFPAbgHRQG5jumGWyMxrTIaRRa4AzFs0YrNMXgI4MJWnsKYFhGNMnIh/mBOPsV8CZBFjN9w8ArowxnZzDr5IcvxTaKpxJCVsoI3SWysz1UM4AqQdzlCgho1IKrK+x1mKFzisANtgEU8E7GwHDgOqxe4w9tnM166wbaA2xBMBLrTWcAGYGdCiYVUJUrMCF+7l6gklrW1RwPi8ByjuActANqhKzVWoYrFGcCFzbDgNaBrTWpncFQMcI2DfWoAiDqRCiHcYM/ZdCEKkZqfR8spj4F0CiJjv2gHL6URNoCQDaYacL6O1oFDO2eQDUFP7AKdCRdC4CRDZaNifbjA41l/uYb/SZMAFNTBGzY7sG4J2jTsJgToOzIzXF3HZqBpSOAGduXbkGEKV1OqVGqRJpRxtvwbSw5IqAiUJItwBCOT9R32FCGo3nCkAN3t0UTBRC6zDONcBeOee9pr4T+ASbnDOdc5OmtREwEEB5w8Wqgt4bOV5c7Lt9iiK8zdP/IsBbutUGX4gNwEVCqd3FUt/xYnRWt7BvPBYQjwjAGXa3ASguHVAZ/WXKlA3eZDX1wRegFsr7E2wAlB9xFbx/7MV70kFT8aqNgLL3F8nFBmA3hrn/IJHjKJOK2oj1WI4Z4FzwN/81AOhgYD6v2xbIvaZIurLC9AfvB1WLTYAoXejF1xeBL5cpBIyEbmxkZymIXwDQh1BAfOrhI1lfwtgz8tozF5T4ttf3+32AyxC6klwAgw/YVTHpoK82xYt3wLP/Bw4AAI4D7/01TLKXBj9NEz2wEuGiKpoy68S/FwCLMQAZ3gXhihaCa25vQo6RyToOz9PE6wYjJgGi1YnqZX8qMIK+TDlnargGzSv4NvFjlwXf/nfLLMrAB4O/Xl3BH0d+RIASFgDA8IYMIXgr2+cBsbDdPSwAAHjSZFmb3txETCKIxc0uWwI8rxsLJcTiVndNwyMgKljbbNN2/zcEAja2+4RgIDYN2Bu/WtAOaEodnu2dnzz/7I/Y/w/LaEcX/MdfAAAAAElFTkSuQmCC";
@@ -1716,6 +1770,17 @@ Minimum version required to store current data is: ` + bestVersion + `.
1716
1770
  return snapshot.baselinesReady === !0 || snapshot.phase === "ready";
1717
1771
  }
1718
1772
  var localeNamespace = "ds-harness-remote", en = {
1773
+ terminalRemote: "Remote terminal",
1774
+ terminalRemoteHint: "Allow trusted Remote devices to run an interactive shell as this Host user. Off by default; independent of Agent approvals. Toggling saves and applies immediately.",
1775
+ terminalRemoteUnavailable: "Remote terminal is unavailable. Enable Remote terminal in the Host Remote settings, then restart the Host.",
1776
+ previewPorts: "Remote preview ports",
1777
+ previewPortsHint: "Allow specific Host ports (1024\u201365535), separated by commas. Empty disables preview. HTTP, WebSocket and hot reload are supported.",
1778
+ developmentSave: "Save access settings",
1779
+ developmentSaved: "Remote preview ports saved. Changes apply immediately.",
1780
+ terminalSaved: "Remote terminal setting saved. Applies immediately.",
1781
+ previewOpen: "Preview service",
1782
+ previewPort: "Host port",
1783
+ previewLocalOnly: "Preview requires Desktop or a browser connected to Harness on this computer.",
1719
1784
  pluginTitle: "DeepSeek Remote",
1720
1785
  pluginDescription: "Connect once. Available anytime.",
1721
1786
  expandSettings: "Show settings: {name}",
@@ -1787,6 +1852,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
1787
1852
  reconnectingAction: "Reconnecting\u2026",
1788
1853
  reconnectStarted: "Reconnect requested.",
1789
1854
  connectionAuthorizationExpired: "Authorization expired. Sign out and authorize this Host again.",
1855
+ connectionReplaced: "Another instance is using this Host identity. Stop it or use a separate DSH_HOME before reconnecting.",
1856
+ connectionCredentialsBusy: "Credential refresh is locked. Stop other instances. After a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again.",
1790
1857
  connectionDeviceRevoked: "This Host was revoked on the Server. Sign out and authorize it again.",
1791
1858
  connectionOwnershipRequired: "The Server no longer recognizes this Host as an owned device.",
1792
1859
  connectionRateLimited: "The Server is receiving too many requests. Automatic retry will continue.",
@@ -1909,6 +1976,22 @@ Minimum version required to store current data is: ` + bestVersion + `.
1909
1976
  signInClientDescription: "Connect once. Available anytime.",
1910
1977
  startSignIn: "Start sign-in",
1911
1978
  allowControlCurrentDevice: "Allow control of this device",
1979
+ connectedClientCount: "{count} connected",
1980
+ checkAcp: "Check ACP",
1981
+ checkingAcp: "Checking ACP\u2026",
1982
+ acpAvailable: "ACP available",
1983
+ acpUnavailable: "ACP unavailable",
1984
+ acpHint: "Connect compatible coding agents through the Agent Client Protocol.",
1985
+ acpCheckPassed: "Check passed",
1986
+ acpCheckFailed: "Check failed",
1987
+ addAcp: "Add ACP",
1988
+ acpName: "Service name",
1989
+ acpCommand: "Command",
1990
+ acpArguments: "Arguments",
1991
+ removeAcp: "Remove",
1992
+ currentConnectedDevices: "Currently connected devices",
1993
+ noConnectedClients: "No devices are currently connected to this Host.",
1994
+ unknownDevice: "Unknown device",
1912
1995
  exitRemoteAccount: "Sign out",
1913
1996
  githubLogin: "GitHub QR",
1914
1997
  zhihuLogin: "Zhihu QR",
@@ -1923,6 +2006,17 @@ Minimum version required to store current data is: ` + bestVersion + `.
1923
2006
  codexVirtualWorkspace: "CodeX virtual workspace",
1924
2007
  codexVirtualSessions: "Sessions"
1925
2008
  }, zh = {
2009
+ terminalRemote: "\u8FDC\u7A0B\u7EC8\u7AEF",
2010
+ terminalRemoteHint: "\u5141\u8BB8\u53D7\u4FE1\u4EFB\u7684 Remote \u8BBE\u5907\u4EE5\u6B64 Host \u7528\u6237\u8EAB\u4EFD\u8FD0\u884C\u4EA4\u4E92\u5F0F Shell\u3002\u9ED8\u8BA4\u5173\u95ED\uFF0C\u72EC\u7ACB\u4E8E Agent \u5BA1\u6279\uFF1B\u5207\u6362\u540E\u7ACB\u5373\u4FDD\u5B58\u5E76\u751F\u6548\u3002",
2011
+ terminalRemoteUnavailable: "\u8FDC\u7A0B\u7EC8\u7AEF\u4E0D\u53EF\u7528\u3002\u8BF7\u5728 Host \u7684 Remote \u8BBE\u7F6E\u4E2D\u5F00\u542F\u300C\u8FDC\u7A0B\u7EC8\u7AEF\u300D\uFF0C\u7136\u540E\u91CD\u542F Host\u3002",
2012
+ previewPorts: "\u8FDC\u7A0B\u9884\u89C8\u7AEF\u53E3",
2013
+ previewPortsHint: "\u586B\u5199\u5141\u8BB8\u8BBF\u95EE\u7684 Host \u7AEF\u53E3\uFF081024\u201365535\uFF09\uFF0C\u4EE5\u9017\u53F7\u5206\u9694\u3002\u7559\u7A7A\u7981\u7528\u9884\u89C8\uFF1B\u652F\u6301 HTTP\u3001WebSocket \u548C\u70ED\u66F4\u65B0\u3002",
2014
+ developmentSave: "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E",
2015
+ developmentSaved: "\u8FDC\u7A0B\u9884\u89C8\u7AEF\u53E3\u5DF2\u4FDD\u5B58\uFF0C\u7ACB\u5373\u751F\u6548\u3002",
2016
+ terminalSaved: "\u8FDC\u7A0B\u7EC8\u7AEF\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u7ACB\u5373\u751F\u6548\u3002",
2017
+ previewOpen: "\u9884\u89C8\u670D\u52A1",
2018
+ previewPort: "Host \u7AEF\u53E3",
2019
+ previewLocalOnly: "\u9884\u89C8\u9700\u8981 Desktop\uFF0C\u6216\u8FDE\u63A5\u5230\u672C\u673A Harness \u7684\u6D4F\u89C8\u5668\u3002",
1926
2020
  pluginTitle: "DeepSeek \u8FDC\u7A0B\u8FDE\u63A5",
1927
2021
  pluginDescription: "\u4E00\u6B21\u8FDE\u63A5\uFF0C\u968F\u65F6\u53EF\u7528\u3002",
1928
2022
  expandSettings: "\u5C55\u5F00\u8BBE\u7F6E\uFF1A{name}",
@@ -1994,6 +2088,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
1994
2088
  reconnectingAction: "\u6B63\u5728\u91CD\u8FDE\u2026",
1995
2089
  reconnectStarted: "\u5DF2\u53D1\u8D77\u91CD\u8FDE\u3002",
1996
2090
  connectionAuthorizationExpired: "\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u8BF7\u9000\u51FA\u6388\u6743\u540E\u91CD\u65B0\u8FDE\u63A5\u6B64 Host\u3002",
2091
+ connectionReplaced: "\u53E6\u4E00\u5B9E\u4F8B\u6B63\u5728\u4F7F\u7528\u6B64 Host \u8EAB\u4EFD\u3002\u8BF7\u505C\u6B62\u8BE5\u5B9E\u4F8B\uFF0C\u6216\u4F7F\u7528\u72EC\u7ACB\u7684 DSH_HOME \u540E\u91CD\u65B0\u8FDE\u63A5\u3002",
2092
+ connectionCredentialsBusy: "\u51ED\u636E\u5237\u65B0\u88AB\u9501\u5B9A\uFF0C\u8BF7\u505C\u6B62\u5176\u4ED6\u5B9E\u4F8B\u3002\u82E5\u6B64\u524D\u5F02\u5E38\u9000\u51FA\uFF0C\u8BF7\u505C\u6B62\u6240\u6709\u5B9E\u4F8B\u540E\u79FB\u9664 server-credentials.json.refresh-lock \u5E76\u91CD\u65B0\u6388\u6743\u3002",
1997
2093
  connectionDeviceRevoked: "\u6B64 Host \u5DF2\u5728 Server \u4E0A\u88AB\u64A4\u9500\uFF0C\u8BF7\u9000\u51FA\u6388\u6743\u540E\u91CD\u65B0\u8FDE\u63A5\u3002",
1998
2094
  connectionOwnershipRequired: "Server \u5DF2\u4E0D\u518D\u5C06\u6B64 Host \u8BC6\u522B\u4E3A\u5F53\u524D\u8D26\u53F7\u7684\u8BBE\u5907\u3002",
1999
2095
  connectionRateLimited: "Server \u8BF7\u6C42\u8FC7\u591A\uFF0C\u63D2\u4EF6\u5C06\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002",
@@ -2116,6 +2212,22 @@ Minimum version required to store current data is: ` + bestVersion + `.
2116
2212
  signInClientDescription: "\u4E00\u6B21\u8FDE\u63A5\uFF0C\u968F\u65F6\u53EF\u7528\u3002",
2117
2213
  startSignIn: "\u5F00\u59CB\u767B\u5F55",
2118
2214
  allowControlCurrentDevice: "\u5141\u8BB8\u63A7\u5236\u5F53\u524D\u8BBE\u5907",
2215
+ connectedClientCount: "{count} \u53F0\u5DF2\u8FDE\u63A5",
2216
+ checkAcp: "\u68C0\u6D4B ACP",
2217
+ checkingAcp: "\u6B63\u5728\u68C0\u6D4B ACP\u2026",
2218
+ acpAvailable: "ACP \u53EF\u7528",
2219
+ acpUnavailable: "ACP \u4E0D\u53EF\u7528",
2220
+ acpHint: "\u901A\u8FC7 Agent Client Protocol \u8FDE\u63A5\u517C\u5BB9\u7684\u7F16\u7801 Agent\u3002",
2221
+ acpCheckPassed: "\u68C0\u6D4B\u901A\u8FC7",
2222
+ acpCheckFailed: "\u68C0\u6D4B\u5931\u8D25",
2223
+ addAcp: "\u6DFB\u52A0 ACP",
2224
+ acpName: "\u670D\u52A1\u540D\u79F0",
2225
+ acpCommand: "\u542F\u52A8\u547D\u4EE4",
2226
+ acpArguments: "\u547D\u4EE4\u53C2\u6570",
2227
+ removeAcp: "\u79FB\u9664",
2228
+ currentConnectedDevices: "\u5F53\u524D\u8FDE\u63A5\u7684\u8BBE\u5907",
2229
+ noConnectedClients: "\u76EE\u524D\u6CA1\u6709\u8BBE\u5907\u8FDE\u63A5\u5230\u8FD9\u53F0\u4E3B\u673A\u3002",
2230
+ unknownDevice: "\u672A\u77E5\u8BBE\u5907",
2119
2231
  exitRemoteAccount: "\u9000\u51FA\u8D26\u53F7",
2120
2232
  githubLogin: "GitHub \u626B\u7801",
2121
2233
  zhihuLogin: "\u77E5\u4E4E\u626B\u7801",
@@ -2141,7 +2253,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2141
2253
  controlUnavailable: !0,
2142
2254
  connected: !1,
2143
2255
  transport: "Disconnected",
2144
- remoteFeatures: { commandList: !1, fileViewer: !1, codex: !1 },
2256
+ remoteFeatures: { commandList: !1, fileViewer: !1, terminal: !1, codex: !1 },
2145
2257
  hostAuthorizationAvailable: !1
2146
2258
  };
2147
2259
  }
@@ -2234,7 +2346,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2234
2346
  };
2235
2347
  }
2236
2348
  function connectionErrorMessage(code, t) {
2237
- return t(code === "ACCOUNT_AUTH_REQUIRED" || code === "AUTH_INVALID" || code === "TOKEN_EXPIRED" ? "connectionAuthorizationExpired" : code === "DEVICE_REVOKED" ? "connectionDeviceRevoked" : code === "DEVICE_OWNERSHIP_REQUIRED" ? "connectionOwnershipRequired" : code === "RATE_LIMITED" ? "connectionRateLimited" : code === "UNSUPPORTED_VERSION" ? "connectionVersionMismatch" : code === "INVALID_MESSAGE" ? "connectionInvalidResponse" : code === "CONNECTION_FAILED" || code === "SERVER_NOT_CONFIGURED" ? "connectionReachability" : "connectionUnexpected");
2349
+ return t(code === "ACCOUNT_AUTH_REQUIRED" || code === "AUTH_INVALID" || code === "TOKEN_EXPIRED" ? "connectionAuthorizationExpired" : code === "CONNECTION_REPLACED" ? "connectionReplaced" : code === "SERVER_CREDENTIALS_BUSY" ? "connectionCredentialsBusy" : code === "DEVICE_REVOKED" ? "connectionDeviceRevoked" : code === "DEVICE_OWNERSHIP_REQUIRED" ? "connectionOwnershipRequired" : code === "RATE_LIMITED" ? "connectionRateLimited" : code === "UNSUPPORTED_VERSION" ? "connectionVersionMismatch" : code === "INVALID_MESSAGE" ? "connectionInvalidResponse" : code === "CONNECTION_FAILED" || code === "SERVER_NOT_CONFIGURED" ? "connectionReachability" : "connectionUnexpected");
2238
2350
  }
2239
2351
  function connectionStatusLabel(status, t) {
2240
2352
  return status === void 0 ? t("checkingConnection") : status.online ? t("online") : status.reconnecting ? t(status.lastActiveAt === void 0 && status.error === void 0 ? "connecting" : "reconnecting") : t("offline");
@@ -2322,8 +2434,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
2322
2434
  }
2323
2435
  }
2324
2436
  function RemotePluginOptions(props) {
2325
- let { t } = props, [open, setOpen] = React.useState(!1), [serverUrl, setServerUrl] = React.useState(""), [codexEnabled, setCodexEnabled] = React.useState(!0), role = "host", [registrationCode, setRegistrationCode] = React.useState(""), [associations, setAssociations] = React.useState({}), [loaded, setLoaded] = React.useState(!1), [writable, setWritable] = React.useState(!1), [busy, setBusy] = React.useState(!1), [codexBusy, setCodexBusy] = React.useState(!1), [reconnectBusy, setReconnectBusy] = React.useState(!1), [hostStatus, setHostStatus] = React.useState(void 0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0), [settingsView, setSettingsView] = React.useState(void 0), persistedServerUrl = settingsView?.config.serverUrl ?? "https://dsh.r2049.cn", association = associations.client ?? associations.host, serverDirty = settingsView !== void 0 && serverUrl !== persistedServerUrl, draftDirty = serverDirty, applyView = (view) => {
2326
- setSettingsView(view), setServerUrl(view.config.serverUrl ?? "https://dsh.r2049.cn"), setCodexEnabled(view.config.codex?.enabled ?? !0), setAssociations(view.associations ?? (view.association === void 0 ? {} : { host: view.association })), setWritable(view.writable), setLoaded(!0);
2437
+ let { t } = props, [open, setOpen] = React.useState(props.view === "page"), [serverUrl, setServerUrl] = React.useState(""), [codexEnabled, setCodexEnabled] = React.useState(!0), [terminalEnabled, setTerminalEnabled] = React.useState(!1), [terminalBusy, setTerminalBusy] = React.useState(!1), [portsBusy, setPortsBusy] = React.useState(!1), [previewPorts, setPreviewPorts] = React.useState(""), role = "host", [registrationCode, setRegistrationCode] = React.useState(""), [associations, setAssociations] = React.useState({}), [loaded, setLoaded] = React.useState(!1), [writable, setWritable] = React.useState(!1), [busy, setBusy] = React.useState(!1), [codexBusy, setCodexBusy] = React.useState(!1), [acpBackends, setAcpBackends] = React.useState([]), [acpAvailability, setAcpAvailability] = React.useState({}), [acpChecking, setAcpChecking] = React.useState({}), [acpCheckResults, setAcpCheckResults] = React.useState({}), [addingAcp, setAddingAcp] = React.useState(!1), [acpName, setAcpName] = React.useState(""), [acpCommand, setAcpCommand] = React.useState(""), [acpArguments, setAcpArguments] = React.useState("acp"), [reconnectBusy, setReconnectBusy] = React.useState(!1), [hostStatus, setHostStatus] = React.useState(void 0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0), [settingsView, setSettingsView] = React.useState(void 0), persistedServerUrl = settingsView?.config.serverUrl ?? "https://dsh.r2049.cn", association = associations.client ?? associations.host, serverDirty = settingsView !== void 0 && serverUrl !== persistedServerUrl, draftDirty = serverDirty, applyView = (view) => {
2438
+ setSettingsView(view), setServerUrl(view.config.serverUrl ?? "https://dsh.r2049.cn"), setCodexEnabled(view.config.codex?.enabled ?? !0), setTerminalEnabled(view.config.terminal?.enabled ?? !1), setPreviewPorts((view.config.loopback?.ports ?? []).join(", ")), setAcpBackends((view.config.acp?.backends ?? []).map((item) => ({ id: item.id, enabled: item.enabled !== !1 }))), setAcpAvailability(view.acpAvailability ?? {}), setAssociations(view.associations ?? (view.association === void 0 ? {} : { host: view.association })), setWritable(view.writable), setLoaded(!0);
2327
2439
  }, load = async () => {
2328
2440
  let [view, status] = await Promise.all([
2329
2441
  props.control("settings.get"),
@@ -2331,20 +2443,12 @@ Minimum version required to store current data is: ` + bestVersion + `.
2331
2443
  })
2332
2444
  ]);
2333
2445
  applyView(view), setHostStatus(status?.host);
2334
- }, refreshHostStatus = async () => {
2335
- setHostStatus((await props.control("status")).host);
2336
2446
  };
2337
2447
  React.useEffect(() => {
2338
2448
  load().catch((reason) => setError(messageOf(reason)));
2339
2449
  }, []), React.useEffect(() => {
2340
- if (association === void 0) return;
2341
- refreshHostStatus().catch(() => {
2342
- });
2343
- let timer = window.setInterval(() => {
2344
- refreshHostStatus().catch(() => {
2345
- });
2346
- }, 3e4);
2347
- return () => window.clearInterval(timer);
2450
+ if (association !== void 0)
2451
+ return props.statusFeed.subscribe((status) => setHostStatus(status.host));
2348
2452
  }, [association !== void 0]);
2349
2453
  let save = async (event) => {
2350
2454
  if (event?.preventDefault(), !(!writable || !serverDirty)) {
@@ -2401,9 +2505,114 @@ Minimum version required to store current data is: ` + bestVersion + `.
2401
2505
  } finally {
2402
2506
  setCodexBusy(!1);
2403
2507
  }
2508
+ }, setTerminalRemote = async (enabled) => {
2509
+ let previous = terminalEnabled, portsDraft = previewPorts;
2510
+ setTerminalEnabled(enabled), setTerminalBusy(!0), setError(void 0), setNotice(void 0);
2511
+ try {
2512
+ let view = await props.control("settings.development.set", { terminalEnabled: enabled });
2513
+ applyView(view), setPreviewPorts(portsDraft), setNotice({ key: "terminalSaved" });
2514
+ } catch (reason) {
2515
+ setTerminalEnabled(previous), setError(messageOf(reason));
2516
+ } finally {
2517
+ setTerminalBusy(!1);
2518
+ }
2519
+ }, savePreviewPorts = async () => {
2520
+ setPortsBusy(!0), setError(void 0), setNotice(void 0);
2521
+ try {
2522
+ let ports = previewPorts.trim() === "" ? [] : previewPorts.split(/[,,]/).map((value) => Number(value.trim())), view = await props.control("settings.development.set", { ports });
2523
+ applyView(view), setNotice({ key: "developmentSaved" });
2524
+ } catch (reason) {
2525
+ setError(messageOf(reason));
2526
+ } finally {
2527
+ setPortsBusy(!1);
2528
+ }
2529
+ }, checkAcp = async (backend) => {
2530
+ setAcpChecking((current) => ({ ...current, [backend]: !0 })), setAcpCheckResults((current) => ({ ...current, [backend]: void 0 })), setError(void 0);
2531
+ try {
2532
+ let view = await props.control("settings.get");
2533
+ applyView(view);
2534
+ let available = view.acpAvailability?.[backend] === !0;
2535
+ if (!available) {
2536
+ let disabledView = await props.control("settings.acp.set", { backend, enabled: !1 });
2537
+ applyView(disabledView);
2538
+ }
2539
+ setAcpCheckResults((current) => ({ ...current, [backend]: available })), setNotice({ key: available ? "acpAvailable" : "acpUnavailable" }), window.setTimeout(() => setAcpCheckResults((current) => ({ ...current, [backend]: void 0 })), 5e3);
2540
+ } catch (reason) {
2541
+ setError(messageOf(reason));
2542
+ } finally {
2543
+ setAcpChecking((current) => ({ ...current, [backend]: !1 }));
2544
+ }
2545
+ }, addAcp = async () => {
2546
+ setBusy(!0), setError(void 0);
2547
+ try {
2548
+ let view = await props.control("settings.acp.add", {
2549
+ id: acpName.trim(),
2550
+ command: acpCommand.trim(),
2551
+ args: acpArguments.trim() === "" ? [] : acpArguments.trim().split(/\s+/)
2552
+ });
2553
+ applyView(view), setAddingAcp(!1), setAcpName(""), setAcpCommand(""), setAcpArguments("acp");
2554
+ } catch (reason) {
2555
+ setError(messageOf(reason));
2556
+ } finally {
2557
+ setBusy(!1);
2558
+ }
2559
+ }, removeAcp = async (backend) => {
2560
+ setBusy(!0), setError(void 0);
2561
+ try {
2562
+ let view = await props.control("settings.acp.remove", { id: backend });
2563
+ applyView(view);
2564
+ } catch (reason) {
2565
+ setError(messageOf(reason));
2566
+ } finally {
2567
+ setBusy(!1);
2568
+ }
2404
2569
  }, discard = () => {
2405
2570
  settingsView !== void 0 && applyView(settingsView), setRegistrationCode(""), setNotice(void 0), setError(void 0);
2406
- }, codexSetting = React.createElement(
2571
+ }, developmentSetting = React.createElement(
2572
+ "section",
2573
+ { className: "dshRemoteDevelopmentSettings" },
2574
+ React.createElement(
2575
+ "div",
2576
+ { className: "dshRemoteAuthorizationSetting" },
2577
+ React.createElement(
2578
+ "div",
2579
+ null,
2580
+ React.createElement("strong", null, t("terminalRemote")),
2581
+ React.createElement("p", null, t("terminalRemoteHint"))
2582
+ ),
2583
+ React.createElement("input", {
2584
+ type: "checkbox",
2585
+ role: "switch",
2586
+ checked: terminalEnabled,
2587
+ disabled: busy || terminalBusy || !writable,
2588
+ "aria-label": t("terminalRemote"),
2589
+ onChange: (event) => void setTerminalRemote(event.target.checked)
2590
+ })
2591
+ ),
2592
+ React.createElement(
2593
+ "div",
2594
+ { className: "dshRemotePathField" },
2595
+ React.createElement("span", null, t("previewPorts")),
2596
+ React.createElement(
2597
+ "div",
2598
+ { className: "dshRemotePortsRow" },
2599
+ React.createElement("input", {
2600
+ value: previewPorts,
2601
+ maxLength: 128,
2602
+ disabled: !writable,
2603
+ placeholder: "3000, 5173",
2604
+ "aria-label": t("previewPorts"),
2605
+ onChange: (event) => setPreviewPorts(event.target.value)
2606
+ }),
2607
+ React.createElement("button", {
2608
+ type: "button",
2609
+ disabled: portsBusy || terminalBusy || !writable,
2610
+ onClick: () => void savePreviewPorts()
2611
+ }, t("developmentSave"))
2612
+ ),
2613
+ React.createElement("small", null, t("previewPortsHint"))
2614
+ )
2615
+ ), codexSetting = React.createElement(
2407
2616
  "div",
2408
2617
  { className: "dshRemoteAuthorizationSetting" },
2409
2618
  React.createElement(
@@ -2420,10 +2629,65 @@ Minimum version required to store current data is: ` + bestVersion + `.
2420
2629
  checked: codexEnabled,
2421
2630
  onChange: (event) => void setCodexRemote(event.target.checked)
2422
2631
  })
2632
+ ), acpSetting = React.createElement(
2633
+ "details",
2634
+ { className: "dshRemoteAuthorizationSetting dshRemoteAcpSetting" },
2635
+ React.createElement(
2636
+ "summary",
2637
+ null,
2638
+ React.createElement(
2639
+ "div",
2640
+ { className: "dshRemoteAcpSummaryText" },
2641
+ React.createElement("strong", null, "ACP Agent backends"),
2642
+ React.createElement("p", null, t("acpHint"))
2643
+ )
2644
+ ),
2645
+ React.createElement(
2646
+ "div",
2647
+ { className: "dshRemoteAcpList" },
2648
+ acpBackends.map((item) => React.createElement(
2649
+ "div",
2650
+ { key: item.id, className: "dshRemoteAuthorizationSetting" },
2651
+ React.createElement("span", null, item.id),
2652
+ React.createElement(
2653
+ "span",
2654
+ { className: "dshRemoteAcpCheckCell" },
2655
+ React.createElement(
2656
+ "a",
2657
+ { href: `#check-acp-${item.id}`, className: `dshRemoteAcpCheckLink${acpChecking[item.id] ? " isChecking" : acpCheckResults[item.id] === void 0 ? "" : acpCheckResults[item.id] ? " isPassed" : " isFailed"}`, onClick: (event) => {
2658
+ event.preventDefault(), checkAcp(item.id);
2659
+ } },
2660
+ acpChecking[item.id] ? t("checkingAcp") : acpCheckResults[item.id] === void 0 ? t("checkAcp") : t(acpCheckResults[item.id] ? "acpCheckPassed" : "acpCheckFailed")
2661
+ )
2662
+ ),
2663
+ ["codex", "cursor", "kimi"].includes(item.id) ? null : React.createElement("a", { href: `#remove-acp-${item.id}`, className: "dshRemoteAcpRemoveLink", onClick: (event) => {
2664
+ event.preventDefault(), removeAcp(item.id);
2665
+ } }, t("removeAcp")),
2666
+ React.createElement("input", {
2667
+ type: "checkbox",
2668
+ role: "switch",
2669
+ checked: item.enabled && acpAvailability[item.id] === !0,
2670
+ disabled: busy || !writable || !acpAvailability[item.id],
2671
+ onChange: (event) => void props.control("settings.acp.set", { backend: item.id, enabled: event.target.checked }).then((view) => {
2672
+ applyView(view), setAcpBackends((view.config.acp?.backends ?? []).map((v) => ({ id: v.id, enabled: v.enabled !== !1 })));
2673
+ }).catch((reason) => setError(messageOf(reason)))
2674
+ })
2675
+ )),
2676
+ addingAcp ? React.createElement(
2677
+ "div",
2678
+ { className: "dshRemoteAcpAddForm" },
2679
+ React.createElement("input", { value: acpName, placeholder: t("acpName"), "aria-label": t("acpName"), onChange: (event) => setAcpName(event.target.value) }),
2680
+ React.createElement("input", { value: acpCommand, placeholder: t("acpCommand"), "aria-label": t("acpCommand"), onChange: (event) => setAcpCommand(event.target.value) }),
2681
+ React.createElement("input", { value: acpArguments, placeholder: t("acpArguments"), "aria-label": t("acpArguments"), onChange: (event) => setAcpArguments(event.target.value) }),
2682
+ React.createElement("button", { type: "button", disabled: busy || acpName.trim() === "" || acpCommand.trim() === "", onClick: () => void addAcp() }, t("addAcp"))
2683
+ ) : React.createElement("a", { href: "#add-acp", className: "dshRemoteAcpAddLink", onClick: (event) => {
2684
+ event.preventDefault(), setAddingAcp(!0);
2685
+ } }, t("addAcp"))
2686
+ )
2423
2687
  );
2424
- return React.createElement(
2425
- "li",
2426
- { className: `dshRemotePluginCard${open ? " isOpen" : ""}` },
2688
+ return props.view === "summary" ? React.createElement("span", null, t("pluginDescription")) : React.createElement(
2689
+ props.view === "page" ? "div" : "li",
2690
+ { className: `dshRemotePluginCard${open ? " isOpen" : ""}${props.view === "page" ? " dshRemotePluginModern" : ""}` },
2427
2691
  React.createElement(
2428
2692
  "div",
2429
2693
  { className: "dshRemotePluginCardHeader" },
@@ -2482,7 +2746,9 @@ Minimum version required to store current data is: ` + bestVersion + `.
2482
2746
  }),
2483
2747
  React.createElement("p", null, t("serverUrlHint"))
2484
2748
  ),
2749
+ developmentSetting,
2485
2750
  codexSetting,
2751
+ acpSetting,
2486
2752
  React.createElement(
2487
2753
  "div",
2488
2754
  { className: "dshRemoteAuthorizationSetting" },
@@ -2531,7 +2797,10 @@ Minimum version required to store current data is: ` + bestVersion + `.
2531
2797
  React.createElement(
2532
2798
  "div",
2533
2799
  { className: "dshRemoteSettingsFooter" },
2534
- error !== void 0 ? React.createElement("p", { className: "dshRemoteError", role: "alert" }, error) : notice === void 0 ? null : React.createElement("p", { className: "dshRemoteNotice", role: "status" }, t(notice.key, notice.params)),
2800
+ React.createElement("p", {
2801
+ className: error !== void 0 ? "dshRemoteError" : "dshRemoteNotice",
2802
+ role: error !== void 0 ? "alert" : "status"
2803
+ }, error ?? (notice === void 0 ? "" : t(notice.key, notice.params))),
2535
2804
  draftDirty ? React.createElement(
2536
2805
  React.Fragment,
2537
2806
  null,
@@ -2564,13 +2833,18 @@ Minimum version required to store current data is: ` + bestVersion + `.
2564
2833
  }),
2565
2834
  React.createElement("p", null, t("serverUrlHint"))
2566
2835
  ),
2836
+ developmentSetting,
2567
2837
  codexSetting,
2838
+ acpSetting,
2568
2839
  React.createElement("p", { className: "dshRemoteSettingsState" }, t("authorizeFromRemote")),
2569
2840
  writable ? null : React.createElement("p", { className: "dshRemoteError" }, t("readOnly")),
2570
2841
  React.createElement(
2571
2842
  "div",
2572
2843
  { className: "dshRemoteSettingsFooter" },
2573
- error !== void 0 ? React.createElement("p", { className: "dshRemoteError", role: "alert" }, error) : notice === void 0 ? null : React.createElement("p", { className: "dshRemoteNotice", role: "status" }, t(notice.key, notice.params)),
2844
+ React.createElement("p", {
2845
+ className: error !== void 0 ? "dshRemoteError" : "dshRemoteNotice",
2846
+ role: error !== void 0 ? "alert" : "status"
2847
+ }, error ?? (notice === void 0 ? "" : t(notice.key, notice.params))),
2574
2848
  React.createElement("button", { type: "button", className: "dshRemoteDiscard", disabled: busy || !draftDirty, onClick: discard }, t("discard")),
2575
2849
  React.createElement("button", { type: "submit", className: "dshRemoteSave", disabled: busy || !writable || !serverDirty }, t(busy ? "saving" : "save"))
2576
2850
  )
@@ -2579,17 +2853,36 @@ Minimum version required to store current data is: ` + bestVersion + `.
2579
2853
  );
2580
2854
  }
2581
2855
  function RemoteWorkspaceAction(props) {
2582
- let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [loginMethod, setLoginMethod] = React.useState(props.preferredQrProvider), [loginMethodManuallySelected, setLoginMethodManuallySelected] = React.useState(!1), [qrSession, setQrSession] = React.useState(void 0), [qrImage, setQrImage] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), qrFlowRun = React.useRef(0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0);
2856
+ let { t } = props, [open, setOpen] = React.useState(!1), [status, setStatus] = React.useState(void 0), [devices, setDevices] = React.useState([]), [selectedHost, setSelectedHost] = React.useState(void 0), [workspaces, setWorkspaces] = React.useState([]), [codexWorkspaces, setCodexWorkspaces] = React.useState([]), [workspaceBackend, setWorkspaceBackend] = React.useState("harness"), [codexWorkspaceId, setCodexWorkspaceId] = React.useState(void 0), [directory, setDirectory] = React.useState(void 0), [path, setPath] = React.useState(""), [addingWorkspace, setAddingWorkspace] = React.useState(!1), [showAllWorkspaces, setShowAllWorkspaces] = React.useState(!1), [showAllCodexWorkspaces, setShowAllCodexWorkspaces] = React.useState(!1), [devicesOpen, setDevicesOpen] = React.useState(!1), workspaceListId = "dsh-remote-workspace-list", codexWorkspaceHeadingId = "dsh-remote-codex-workspace-heading", codexWorkspaceListId = "dsh-remote-codex-workspace-list", [busy, setBusy] = React.useState(!1), [needsAuthorization, setNeedsAuthorization] = React.useState(!1), [email, setEmail] = React.useState(""), [password, setPassword] = React.useState(""), [loginServerUrl, setLoginServerUrl] = React.useState("https://dsh.r2049.cn"), [loginMethod, setLoginMethod] = React.useState(props.preferredQrProvider), [loginMethodManuallySelected, setLoginMethodManuallySelected] = React.useState(!1), [qrSession, setQrSession] = React.useState(void 0), [qrImage, setQrImage] = React.useState(void 0), [qrExpired, setQrExpired] = React.useState(!1), [progress, setProgress] = React.useState(void 0), progressRun = React.useRef(0), qrFlowRun = React.useRef(0), [notice, setNotice] = React.useState(void 0), [error, setError] = React.useState(void 0);
2583
2857
  React.useEffect(() => {
2858
+ open || setDevicesOpen(!1);
2859
+ }, [open]), React.useEffect(() => {
2584
2860
  if (!open) return;
2585
2861
  let closeOnEscape = (event) => {
2586
- event.key === "Escape" && setOpen(!1);
2862
+ if (event.key === "Escape") {
2863
+ if (devicesOpen) {
2864
+ event.stopPropagation(), setDevicesOpen(!1);
2865
+ return;
2866
+ }
2867
+ setOpen(!1);
2868
+ }
2587
2869
  };
2588
2870
  return window.addEventListener("keydown", closeOnEscape), () => window.removeEventListener("keydown", closeOnEscape);
2589
- }, [open]), React.useEffect(() => {
2871
+ }, [open, devicesOpen]), React.useEffect(() => {
2872
+ if (!devicesOpen) return;
2873
+ let closeOnPointer = (event) => {
2874
+ let target = event.target;
2875
+ target instanceof Element && target.closest(".dshRemoteConnectedMenu") === null && setDevicesOpen(!1);
2876
+ };
2877
+ return window.addEventListener("mousedown", closeOnPointer), () => window.removeEventListener("mousedown", closeOnPointer);
2878
+ }, [devicesOpen]), React.useEffect(() => {
2879
+ (status?.host?.connectedClients?.length ?? 0) === 0 && setDevicesOpen(!1);
2880
+ }, [status?.host?.connectedClients?.length]), React.useEffect(() => {
2590
2881
  props.control("status").then(setStatus).catch(() => {
2591
2882
  });
2592
2883
  }, []), React.useEffect(() => {
2884
+ status?.serverUrl !== void 0 && loginServerUrl === "https://dsh.r2049.cn" && setLoginServerUrl(status.serverUrl);
2885
+ }, [status?.serverUrl]), React.useEffect(() => {
2593
2886
  let remoteActive = status?.mode === "remote";
2594
2887
  return document.documentElement.classList.toggle("dshRemoteTargetActive", remoteActive), () => {
2595
2888
  remoteActive && document.documentElement.classList.remove("dshRemoteTargetActive");
@@ -2727,13 +3020,23 @@ Minimum version required to store current data is: ` + bestVersion + `.
2727
3020
  }
2728
3021
  }, show = async () => {
2729
3022
  setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setOpen(!0), await refreshRemote();
2730
- }, chooseAnotherHost = () => {
3023
+ };
3024
+ React.useEffect(() => {
3025
+ if (!(!open || selectedHost !== void 0))
3026
+ return props.statusFeed.subscribe(setStatus);
3027
+ }, [open, selectedHost]);
3028
+ let chooseAnotherHost = () => {
2731
3029
  setSelectedHost(void 0), setWorkspaces([]), setCodexWorkspaces([]), setShowAllWorkspaces(!1), setShowAllCodexWorkspaces(!1), setWorkspaceBackend("harness"), setCodexWorkspaceId(void 0), setDirectory(void 0), setPath(""), setAddingWorkspace(!1), setError(void 0);
2732
3030
  }, signInClient = async () => {
2733
- if (!(email.trim() === "" || password === "")) {
3031
+ if (!(email.trim() === "" || password === "" || loginServerUrl.trim() === "")) {
2734
3032
  setBusy(!0), setError(void 0);
2735
3033
  try {
2736
- await props.control("client.account.login", { email: email.trim(), password }), setDevices(await props.control("devices")), setStatus(await props.control("status")), setNeedsAuthorization(!1), setPassword("");
3034
+ await props.control("settings.configure", {
3035
+ role: "client",
3036
+ serverUrl: loginServerUrl.trim(),
3037
+ email: email.trim(),
3038
+ password
3039
+ }), setDevices(await props.control("devices")), setStatus(await props.control("status")), setNeedsAuthorization(!1), setPassword("");
2737
3040
  } catch (reason) {
2738
3041
  setError(messageOf(reason));
2739
3042
  } finally {
@@ -2789,7 +3092,54 @@ Minimum version required to store current data is: ` + bestVersion + `.
2789
3092
  formatPlatform(selectedHost.platform),
2790
3093
  selectedHost.harnessVersion === void 0 ? void 0 : t("harnessVersion", { version: selectedHost.harnessVersion }),
2791
3094
  selectedHost.clientVersion === void 0 ? void 0 : t("pluginVersion", { version: selectedHost.clientVersion })
2792
- ].filter(Boolean).join(" \xB7 ");
3095
+ ].filter(Boolean).join(" \xB7 "), connectedClients = status?.host?.connectedClients ?? [], connectedCount = connectedClients.length, connectedCountLabel = t("connectedClientCount", { count: connectedCount }), connectedMenu = !needsAuthorization && status?.hostAuthorizationAvailable === !0 ? React.createElement(
3096
+ "div",
3097
+ {
3098
+ className: `dshRemoteConnectedMenu${devicesOpen ? " isOpen" : ""}`
3099
+ },
3100
+ React.createElement("a", {
3101
+ href: connectedCount > 0 ? "#connected-devices" : void 0,
3102
+ className: `dshRemoteConnectedCount${connectedCount > 0 ? " isOnline isInteractive" : ""}`,
3103
+ "aria-expanded": connectedCount > 0 ? devicesOpen : void 0,
3104
+ "aria-haspopup": connectedCount > 0 ? "dialog" : void 0,
3105
+ title: connectedCountLabel,
3106
+ "aria-label": connectedCountLabel,
3107
+ onClick: (event) => {
3108
+ event.preventDefault(), connectedCount !== 0 && setDevicesOpen((current) => !current);
3109
+ }
3110
+ }, connectedCountLabel),
3111
+ devicesOpen && connectedCount > 0 ? React.createElement(
3112
+ "div",
3113
+ {
3114
+ className: "dshRemoteConnectedPanel",
3115
+ id: "connected-devices",
3116
+ role: "dialog",
3117
+ "aria-label": t("currentConnectedDevices")
3118
+ },
3119
+ React.createElement("strong", { className: "dshRemoteConnectedPanelTitle" }, t("currentConnectedDevices")),
3120
+ React.createElement(
3121
+ "div",
3122
+ { className: "dshRemoteClientList", "aria-label": t("currentConnectedDevices") },
3123
+ ...connectedClients.map((client) => React.createElement(
3124
+ "div",
3125
+ {
3126
+ key: client.deviceId,
3127
+ className: "dshRemoteClientRow"
3128
+ },
3129
+ React.createElement(
3130
+ "span",
3131
+ null,
3132
+ React.createElement("strong", null, client.name.trim() === "" ? t("unknownDevice") : client.name),
3133
+ React.createElement("small", null, [
3134
+ client.platform === void 0 ? void 0 : formatPlatform(client.platform),
3135
+ connectedClientModeLabel(client.mode, t)
3136
+ ].filter(Boolean).join(" \xB7 "))
3137
+ ),
3138
+ React.createElement("small", { className: "dshRemoteClientOnline" }, t("connected"))
3139
+ ))
3140
+ )
3141
+ ) : null
3142
+ ) : null;
2793
3143
  return React.createElement(
2794
3144
  React.Fragment,
2795
3145
  null,
@@ -2958,6 +3308,15 @@ Minimum version required to store current data is: ` + bestVersion + `.
2958
3308
  id: "dsh-remote-password-panel",
2959
3309
  "aria-labelledby": "dsh-remote-password-tab"
2960
3310
  },
3311
+ React.createElement("input", {
3312
+ type: "url",
3313
+ value: loginServerUrl,
3314
+ disabled: busy,
3315
+ autoComplete: "url",
3316
+ placeholder: t("serverUrl"),
3317
+ "aria-label": t("serverUrl"),
3318
+ onChange: (event) => setLoginServerUrl(event.target.value)
3319
+ }),
2961
3320
  React.createElement("input", {
2962
3321
  type: "email",
2963
3322
  value: email,
@@ -2976,7 +3335,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
2976
3335
  "aria-label": t("password"),
2977
3336
  onChange: (event) => setPassword(event.target.value)
2978
3337
  }),
2979
- React.createElement("button", { type: "button", disabled: busy || email.trim() === "" || password === "", onClick: () => void signInClient() }, t(busy ? "signingIn" : "startSignIn"))
3338
+ React.createElement("button", { type: "button", disabled: busy || loginServerUrl.trim() === "" || email.trim() === "" || password === "", onClick: () => void signInClient() }, t(busy ? "signingIn" : "startSignIn"))
2980
3339
  )
2981
3340
  ) : null,
2982
3341
  needsAuthorization ? null : React.createElement(
@@ -3010,7 +3369,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
3010
3369
  className: "dshRemoteAccountExit",
3011
3370
  disabled: busy,
3012
3371
  onClick: () => void logoutRemote()
3013
- }, t("exitRemoteAccount"))
3372
+ }, t("exitRemoteAccount")),
3373
+ connectedMenu
3014
3374
  )
3015
3375
  ),
3016
3376
  React.createElement("div", { className: "dshRemoteHostList" }, devices.length === 0 ? React.createElement("p", null, t(busy ? "checkingConnection" : "noRemoteHosts")) : devices.map((device) => React.createElement(
@@ -3218,12 +3578,8 @@ Minimum version required to store current data is: ` + bestVersion + `.
3218
3578
  setError(messageOf(reason)), setSupported(!1);
3219
3579
  });
3220
3580
  }, []), React.useEffect(() => {
3221
- if (!open) return;
3222
- refreshStatus();
3223
- let timer = window.setInterval(() => {
3224
- refreshStatus();
3225
- }, 1500);
3226
- return () => window.clearInterval(timer);
3581
+ if (open)
3582
+ return props.statusFeed.subscribe(setStatus);
3227
3583
  }, [open]);
3228
3584
  let switchMode = async (mode, targetDeviceId) => {
3229
3585
  setBusy(!0), setError(void 0);
@@ -3356,20 +3712,12 @@ Minimum version required to store current data is: ` + bestVersion + `.
3356
3712
  ) : null;
3357
3713
  }
3358
3714
  function RemoteSessionHeaderAction(props) {
3359
- let { t } = props, [status, setStatus] = React.useState(void 0), [busy, setBusy] = React.useState(!1), [routeOpen, setRouteOpen] = React.useState(!1);
3715
+ let { t } = props, status = React.useSyncExternalStore(
3716
+ props.statusFeed.subscribe,
3717
+ props.statusFeed.getSnapshot,
3718
+ props.statusFeed.getSnapshot
3719
+ ), [busy, setBusy] = React.useState(!1), [routeOpen, setRouteOpen] = React.useState(!1);
3360
3720
  if (React.useEffect(() => {
3361
- let active = !0, refresh = () => {
3362
- props.control("status").then((next) => {
3363
- active && setStatus(next);
3364
- }).catch(() => {
3365
- });
3366
- };
3367
- refresh();
3368
- let timer = window.setInterval(refresh, 1500);
3369
- return () => {
3370
- active = !1, window.clearInterval(timer);
3371
- };
3372
- }, []), React.useEffect(() => {
3373
3721
  if (status?.mode === "remote")
3374
3722
  return hideLocalSessionActions();
3375
3723
  }, [status?.mode]), React.useEffect(() => (document.documentElement.classList.toggle(
@@ -3539,6 +3887,16 @@ Minimum version required to store current data is: ` + bestVersion + `.
3539
3887
  'html.dshRemoteTargetActive button[aria-label="\u6DFB\u52A0\u5DE5\u4F5C\u533A"],html.dshRemoteTargetActive button[aria-label="Add workspace"]{display:none!important}',
3540
3888
  'html.dshRemoteCodexTargetActive [data-composer-card] button[aria-haspopup="listbox"][aria-label="\u6307\u4EE4"],html.dshRemoteCodexTargetActive [data-composer-card] button[aria-haspopup="listbox"][aria-label="Commands"]{display:none!important}',
3541
3889
  "[data-dsh-remote-hidden-action]{display:none!important}",
3890
+ // The 0.1.6 right sidebar occupies the same shell header area as the
3891
+ // remote status strip. Hide the strip while that sidebar is expanded;
3892
+ // the sidebar itself remains the active context for terminal/files.
3893
+ ":has([data-sidebar-right-open]) .dshRemoteSessionHeader{display:none!important}",
3894
+ // TerminalRecovery renders a retry action in the conversation header.
3895
+ // A failed remote terminal is reported when its tab is opened/closed;
3896
+ // keeping this background recovery action out of the top-left header
3897
+ // avoids overlapping the session controls.
3898
+ '[title^="\u6062\u590D\u7EC8\u7AEF\u5931\u8D25"],[title^="Terminal recovery failed"]{display:none!important}',
3899
+ ".dshRemoteTerminalUnavailable{box-sizing:border-box;margin:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font-size:13px;line-height:1.5}",
3542
3900
  ".dshRemoteModeButton{min-height:36px;border:0;background:transparent;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:8px;padding:0 10px;border-radius:8px}.dshRemoteModeButton:is(button){cursor:pointer}",
3543
3901
  ".dshRemoteModeButton:is(button):hover{background:var(--dsw-alias-interactive-bg-hover)}",
3544
3902
  ".dshRemoteSidebarEntry{box-sizing:border-box;position:relative;min-width:0;display:block;overflow:hidden}.dshRemoteSidebarEntry .dshRemoteModeButton{box-sizing:border-box;width:100%;min-width:0}.dshRemoteSidebarEntry.isWide{width:calc(100% + 8px);height:34px;margin:4px -4px}.dshRemoteSidebarEntry.isWide .dshRemoteModeButton{height:34px;min-height:34px;padding:6px 48px 6px 10px;border-radius:12px}.dshRemoteSidebarEntry.isRail{width:36px;height:54px}.dshRemoteSidebarEntry.isRail .dshRemoteModeButton{width:36px;height:36px;min-height:36px;justify-content:center;gap:0;margin:8px 0 10px;padding:0;border-radius:50%}.dshRemoteSidebarEntry.isActive .dshRemoteModeButton{color:var(--dsw-alias-label-secondary);background:transparent}.dshRemoteSidebarLabel{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dshRemoteExitLink{position:absolute;top:50%;right:10px;transform:translateY(-50%);white-space:nowrap;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:0;font:inherit;font-size:12px;line-height:20px;cursor:pointer}.dshRemoteExitLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteExitLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;border-radius:2px}.dshRemoteExitLink:disabled{opacity:.45;cursor:default;text-decoration:none}",
@@ -3549,14 +3907,24 @@ Minimum version required to store current data is: ` + bestVersion + `.
3549
3907
  ".dshRemotePage{width:min(720px,100%);max-height:min(760px,calc(100vh - 40px));display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:14px;overflow:hidden;animation:dshRemotePageIn .18s cubic-bezier(.25,1,.5,1)}",
3550
3908
  ".dshRemotePageHeader{min-height:72px;display:flex;align-items:center;justify-content:space-between;gap:24px;padding:14px 24px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dshRemotePageIntro{min-width:0;flex:1}.dshRemotePageHeader strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:18px;line-height:1.4}.dshRemotePageHeader p{min-width:0;max-width:70ch;margin:3px 0 0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5}.dshRemotePageActions{flex:0 0 auto;display:flex;align-items:center;gap:4px}.dshRemotePageActions>button{height:40px;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;border:0;border-radius:8px;background:transparent;color:inherit;line-height:1;cursor:pointer}.dshRemotePageActions>button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dshRemotePageActions>button:disabled{opacity:.45;cursor:default}.dshRemotePageBack,.dshRemotePageRefresh{min-width:48px;padding:0 10px;font:inherit;font-size:13px}.dshRemotePageBack{color:var(--dsw-alias-label-secondary)!important}.dshRemotePageClose{width:40px;padding:0;font-size:24px}",
3551
3909
  ".dshRemotePageBody{padding:24px;overflow:auto;display:flex;flex-direction:column;gap:24px}.dshRemotePageBody button{font:inherit;color:inherit}",
3910
+ ".dshRemotePageTitleRow{display:flex;align-items:center;gap:10px;min-width:0}.dshRemotePageTitleRow>strong{min-width:0}",
3552
3911
  ".dshRemoteSectionHeading{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:10px}.dshRemoteSectionTitle{min-width:0;display:flex;align-items:center;gap:10px}.dshRemoteSectionTitle>strong{font-size:14px}.dshRemoteSectionActions{display:flex;align-items:center;gap:14px}.dshRemoteSectionActions>button{border:0;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:5px 0;font-size:12px}.dshRemoteSectionActions>button:hover:not(:disabled){color:var(--dsw-alias-label-primary);text-decoration:underline}",
3912
+ ".dshRemoteSectionTitle{flex:1}.dshRemoteSectionTitle>.dshRemoteConnectedMenu{margin-left:auto}",
3913
+ '.dshRemoteAcpSetting{display:block!important;width:100%;padding:0}.dshRemoteAcpSetting>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;cursor:pointer;list-style:none}.dshRemoteAcpSetting>summary::-webkit-details-marker{display:none}.dshRemoteAcpSetting>summary::after{content:"\u2304";color:var(--dsw-alias-label-secondary);transition:transform .18s ease-out}.dshRemoteAcpSetting[open]>summary::after{transform:rotate(180deg)}.dshRemoteAcpSummaryText{min-width:0}.dshRemoteAcpSummaryText strong{display:block}.dshRemoteAcpSummaryText p{margin:4px 0 0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.4}.dshRemoteAcpCheckLink{grid-column:2;grid-row:1;color:var(--dsw-alias-brand-primary);font-size:12px;text-decoration:underline;text-underline-offset:3px}.dshRemoteAcpCheckLink:hover{color:var(--dsw-alias-label-primary)}.dshRemoteAcpCheckResult{grid-column:3;grid-row:1;font-size:11px;line-height:1.4;white-space:nowrap}.dshRemoteAcpCheckResult.isPassed{color:var(--dsw-alias-state-success-primary)}.dshRemoteAcpCheckResult.isFailed{color:var(--dsw-alias-state-danger-primary)}.dshRemoteAcpList{display:flex;flex-direction:column;gap:0;margin-top:12px;padding-top:0;border-top:0}.dshRemoteAcpList>.dshRemoteAuthorizationSetting{display:grid;width:100%;grid-template-columns:minmax(0,1fr) auto auto auto auto;align-items:center;column-gap:18px;line-height:1.4}.dshRemoteAcpList>.dshRemoteAuthorizationSetting>span:first-child{min-width:0;line-height:1.4}.dshRemoteAcpList>.dshRemoteAuthorizationSetting>input{grid-column:4;grid-row:1}',
3914
+ ".dshRemoteAcpAddLink{align-self:flex-start;margin-top:12px;color:var(--dsw-alias-brand-primary);font-size:12px;text-decoration:underline;text-underline-offset:3px}.dshRemoteAcpAddForm{display:grid;grid-template-columns:minmax(100px,.8fr) minmax(140px,1fr) minmax(100px,1fr) auto;gap:8px;margin-top:12px}.dshRemoteAcpAddForm input{min-width:0;height:34px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:inherit;padding:0 10px}.dshRemoteAcpAddForm button{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:var(--dsw-alias-label-primary);padding:0 12px;cursor:pointer;font:inherit;font-size:12px}.dshRemoteAcpAddForm button:hover{background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteAcpRemoveLink{grid-column:3;grid-row:1;color:var(--dsw-alias-label-secondary);font-size:12px;text-decoration:underline;text-underline-offset:3px}.dshRemoteAcpRemoveLink:hover{color:var(--dsw-alias-state-danger-primary)}",
3915
+ ".dshRemoteAcpCheckResult.isPassed,.dshRemoteAcpCheckResult.isFailed{color:var(--dsw-alias-label-secondary)}",
3916
+ ".dshRemoteAcpList>.dshRemoteAuthorizationSetting{grid-template-columns:minmax(0,1fr) 190px 40px 56px;column-gap:18px}.dshRemoteAcpCheckCell{grid-column:2;display:flex;align-items:center;gap:10px;min-width:0}.dshRemoteAcpCheckLink,.dshRemoteAcpRemoveLink{justify-self:start}.dshRemoteAcpCheckResult{position:static}",
3917
+ ".dshRemoteAcpCheckLink.isPassed{color:var(--dsw-alias-state-success-primary)}.dshRemoteAcpCheckLink.isFailed{color:var(--dsw-alias-state-danger-primary)}.dshRemoteAcpCheckLink.isChecking{color:var(--dsw-alias-label-secondary)}",
3553
3918
  ".dshRemoteCancelWorkspace{min-height:36px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:6px 0;cursor:pointer}.dshRemoteCancelWorkspace:hover:not(:disabled){color:var(--dsw-alias-label-primary);text-decoration:underline}.dshRemoteCancelWorkspace:disabled{opacity:.5;cursor:default}",
3554
3919
  ".dshRemoteHostList{display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.dshRemoteHostList>button{min-height:58px;display:flex;align-items:center;justify-content:space-between;gap:16px;text-align:left;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:10px 4px;cursor:pointer}.dshRemoteHostList>button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteHostList>button:disabled{opacity:.5;cursor:default}.dshRemoteHostList>button>span{min-width:0;display:flex;flex-direction:column;gap:3px}.dshRemoteHostList>button strong{font-size:14px;font-weight:500}.dshRemoteHostList small{color:var(--dsw-alias-label-secondary);font-size:12px}",
3920
+ '.dshRemoteConnectedMenu{position:relative;flex:0 0 auto;margin-right:4px}.dshRemoteConnectedCount{appearance:none;display:inline-flex;align-items:center;justify-content:center;height:22px;border:0;border-radius:999px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);box-shadow:0 1px 2px rgba(0,0,0,.18),0 0 0 1px rgba(255,255,255,.04) inset;padding:0 10px;font:inherit;font-size:11px;font-weight:500;line-height:17px;white-space:nowrap;cursor:default}.dshRemoteConnectedCount.isOnline{color:var(--dsw-alias-state-success-primary)}.dshRemoteConnectedCount.isInteractive{cursor:pointer}.dshRemoteConnectedCount.isInteractive:hover{filter:brightness(1.08)}.dshRemoteConnectedCount:disabled{opacity:1;cursor:default}.dshRemoteConnectedCount:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.dshRemoteConnectedPanel{position:absolute;top:calc(100% + 8px);right:0;z-index:4;width:min(320px,calc(100vw - 48px));max-height:min(280px,40vh);overflow:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-2);box-shadow:0 10px 28px rgba(0,0,0,.28);padding:10px 12px}.dshRemoteConnectedPanelTitle{display:block;margin:0 0 6px;color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600}.dshRemoteClientList{display:flex;flex-direction:column}.dshRemoteClientRow{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px solid var(--dsw-alias-border-l2)}.dshRemoteClientRow:last-child{border-bottom:0}.dshRemoteClientRow>span{min-width:0;display:flex;flex-direction:column;gap:2px}.dshRemoteClientRow strong{font-size:13px;font-weight:500}.dshRemoteClientRow small{color:var(--dsw-alias-label-secondary);font-size:11px}.dshRemoteClientOnline{display:inline-flex;align-items:center;gap:6px;color:var(--dsw-alias-state-success-primary)!important}.dshRemoteClientOnline::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}',
3921
+ ".dshRemoteConnectedCount{height:auto;border-radius:0;background:transparent;box-shadow:none;padding:0 4px;text-decoration:underline;text-decoration-color:color-mix(in srgb,currentColor 45%,transparent);text-underline-offset:3px}.dshRemoteConnectedCount.isInteractive:hover{filter:none;text-decoration-color:currentColor}.dshRemoteConnectedCount:focus-visible{outline-offset:3px;border-radius:3px}",
3555
3922
  '.dshRemoteProgress{display:flex;flex-direction:column;gap:8px;margin:12px 0;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2)}.dshRemoteProgressHeader{display:flex;align-items:center;justify-content:space-between;gap:12px}.dshRemoteProgressHeader strong{font-size:13px;font-weight:600}.dshRemoteProgressHeader span{color:var(--dsw-alias-label-secondary);font-size:12px}.dshRemoteProgressBar{height:6px;overflow:hidden;border-radius:999px;background:var(--dsw-alias-bg-layer-3)}.dshRemoteProgressBar>span{display:block;width:100%;height:100%;border-radius:inherit;background:var(--dsw-alias-brand-primary);transform-origin:left center;transition:transform .22s ease-out}[dir="rtl"] .dshRemoteProgressBar>span{transform-origin:right center}.dshRemoteProgress p{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45}.dshRemoteProgressRoute{font-weight:500}.dshRemoteProgressRoute .isActive{color:var(--dsw-alias-state-success-primary);font-weight:700}.dshRemoteProgressRouteArrow{color:var(--dsw-alias-label-tertiary)}@media(prefers-reduced-motion:reduce){.dshRemoteProgressBar>span{transition:none}}',
3556
3923
  '.dshRemoteBrowser{display:flex;flex-direction:column}.dshRemoteCrumbs{display:flex;align-items:center;gap:4px;overflow:auto;padding:2px 0 10px}.dshRemoteCrumbs>button{flex:0 0 auto;border:0;background:transparent;color:var(--dsw-alias-label-secondary);padding:5px 7px;border-radius:6px;cursor:pointer}.dshRemoteCrumbs>button:not(:last-child)::after{content:" /";color:var(--dsw-alias-label-tertiary)}.dshRemoteCrumbs>button:disabled{color:var(--dsw-alias-label-primary);font-weight:600}',
3557
3924
  ".dshRemoteWorkspaceLists{overflow:visible}",
3558
3925
  ".dshRemoteDirectoryList{min-height:72px;display:flex;flex-direction:column;border-top:1px solid var(--dsw-alias-border-l2)}.dshRemoteDirectoryList>button{min-height:52px;display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:10px;text-align:left;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:8px 4px;cursor:pointer}.dshRemoteDirectoryList>button:hover,.dshRemoteDirectoryList>button.isSelected{background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteDirectoryList>button.isSelected{color:var(--dsw-alias-label-primary)}.dshRemoteDirectoryList>button>span:first-child,.dshRemoteDirectoryList>button>.dshRemoteWorkspaceIcon{grid-row:1/3}.dshRemoteWorkspaceIcon{box-sizing:border-box;width:22px;height:22px;align-self:center;object-fit:contain}.dshRemoteWorkspaceIcon.isGpt{border-radius:6px}.dshRemoteDirectoryList>button>span:not(:first-child),.dshRemoteDirectoryList>button>small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dshRemoteDirectoryList>button>small{grid-column:2;color:var(--dsw-alias-label-secondary)}.dshRemoteDirectoryList>p,.dshRemoteHint{margin:12px 0;color:var(--dsw-alias-label-secondary);font-size:13px}",
3559
3926
  ".dshRemoteAddWorkspace{box-sizing:border-box;width:40px;height:40px;display:inline-grid;place-items:center;flex:0 0 auto;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);padding:0;cursor:pointer}.dshRemoteAddWorkspace:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteAddWorkspace:disabled{opacity:.5;cursor:default}.dshRemoteAddWorkspaceIcon{width:20px;height:20px}.dshRemoteCodexWorkspaceGroup{margin-top:16px}.dshRemoteWorkspaceSourceHeading{min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 4px 7px}.dshRemoteWorkspaceSourceText{min-width:0;display:flex;flex-direction:column;gap:2px}.dshRemoteWorkspaceSourceText>strong{font-size:13px}.dshRemoteWorkspaceSourceText>small{color:var(--dsw-alias-label-secondary);font-size:11px}.dshRemoteCodexWorkspaceList{min-height:0}.dshRemoteDirectoryList>.dshRemoteWorkspaceMore,.dshRemoteCodexWorkspaceGroup>.dshRemoteWorkspaceMore{box-sizing:border-box;width:100%;min-height:48px;display:flex;align-items:center;justify-content:center;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-secondary);padding:8px 4px;text-align:center;font-size:16px;cursor:pointer}.dshRemoteWorkspaceMore>span{display:block;line-height:1;transform:translateY(-2px)}.dshRemoteWorkspaceMore:hover:not(:disabled){color:var(--dsw-alias-label-primary);background:transparent}.dshRemoteWorkspaceMore:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.dshRemoteWorkspaceMore:disabled{opacity:.5;cursor:default}",
3927
+ ".dshRemotePreviewAction,.dshRemotePreviewForm{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.dshRemotePreviewForm input{width:90px;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);padding:6px}.dshRemotePreviewForm [role=alert]{max-width:360px;white-space:normal}.dshRemoteDevelopmentSettings{margin:16px 0}.dshRemoteDevelopmentSettings .dshRemotePathField{margin:8px 0}.dshRemoteDevelopmentSettings .dshRemotePortsRow{display:flex;gap:8px;align-items:stretch}.dshRemoteDevelopmentSettings .dshRemotePortsRow>input{flex:1;min-width:0;min-height:40px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:inherit;padding:0 12px;font:inherit}.dshRemoteDevelopmentSettings button{min-height:36px;color:inherit;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:6px 12px;white-space:nowrap}",
3560
3928
  ".dshRemoteFolderBrowser{margin-top:14px}.dshRemoteFolderBrowser>p,.dshRemoteFolderList>p{margin:12px 0;color:var(--dsw-alias-label-secondary);font-size:13px}.dshRemoteFolderList{max-height:260px;overflow:auto;border-block:1px solid var(--dsw-alias-border-l2)}.dshRemoteFolderList>button{width:100%;min-height:42px;display:flex;align-items:center;gap:9px;border:0;border-bottom:1px solid var(--dsw-alias-border-l2);background:transparent;padding:7px 6px;text-align:left;cursor:pointer}.dshRemoteFolderList>button:hover{background:var(--dsw-alias-interactive-bg-hover)}.dshRemoteFolderBrowser>small{display:block;margin-top:8px;color:var(--dsw-alias-state-warn-label)}",
3561
3929
  ".dshRemotePathField{display:flex;flex-direction:column;gap:6px;margin-top:20px}.dshRemotePathField>span{font-size:13px;font-weight:600}.dshRemotePathField>input{min-height:40px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:inherit;padding:0 12px;font:inherit}.dshRemotePathField>small{color:var(--dsw-alias-label-secondary)}",
3562
3930
  ".dshRemoteOpenBar{position:sticky;bottom:-96px;display:flex;align-items:center;justify-content:space-between;gap:20px;margin-top:20px;padding:14px 0;background:var(--dsw-alias-bg-layer-1);border-top:1px solid var(--dsw-alias-border-l2)}.dshRemoteOpenBar>div{min-width:0;display:flex;flex-direction:column;gap:3px}.dshRemoteOpenBar span{color:var(--dsw-alias-label-secondary);font-size:12px}.dshRemoteOpenBar strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.dshRemoteOpenBar>button,.dshRemoteEnable>button{min-height:40px;flex:0 0 auto;border:0;border-radius:8px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-1);padding:8px 16px;cursor:pointer}.dshRemoteOpenBar>button:disabled,.dshRemoteEnable>button:disabled{opacity:.5;cursor:default}",
@@ -3579,6 +3947,7 @@ Minimum version required to store current data is: ` + bestVersion + `.
3579
3947
  ".dshRemoteHostAccount{display:grid;gap:8px;border-top:1px solid var(--dsw-alias-border-l3);padding-top:12px}.dshRemoteHostAccount p{margin:0;color:var(--dsw-alias-label-secondary);font-size:13px}",
3580
3948
  ".dshRemoteLogin{display:grid;grid-template-columns:1fr 1fr;gap:8px}.dshRemoteLogin button{grid-column:1/-1}",
3581
3949
  ".dshRemotePluginCard{list-style:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;transition:border-color .16s,background .16s}",
3950
+ ".dshRemotePluginModern{border:0!important;border-radius:0;background:transparent;box-shadow:none}.dshRemotePluginModern>.dshRemotePluginCardHeader{display:none}.dshRemotePluginModern>.dshRemotePluginCardBody{border-top:0;margin:0;padding:0}",
3582
3951
  ".dshRemotePluginCard:hover{border-color:var(--dsw-alias-label-dimmed)}.dshRemotePluginCard.isOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}",
3583
3952
  ".dshRemotePluginCardHeader{display:flex;align-items:center}.dshRemotePluginCardToggle{appearance:none;width:100%;min-width:0;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;display:flex;align-items:center;gap:12px;padding:14px 16px}.dshRemotePluginCardToggle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}",
3584
3953
  ".dshRemotePluginCardHeading{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}.dshRemotePluginCardHeading>strong{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.dshRemotePluginCardHeading>span{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.dshRemotePluginCardStatus{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.dshRemotePluginCardStatus.isOnline{color:var(--dsw-alias-state-success-primary)}.dshRemotePluginCardStatus.isReconnecting{color:var(--dsw-alias-state-warn-label)}.dshRemotePluginCardStatus.isOffline{color:var(--dsw-alias-state-error-primary)}.dshRemotePluginCardChevron{color:var(--dsw-alias-label-tertiary);font-size:18px;line-height:14px;transition:transform .16s}.dshRemotePluginCard.isOpen .dshRemotePluginCardChevron{transform:rotate(180deg)}",
@@ -3610,8 +3979,14 @@ Minimum version required to store current data is: ` + bestVersion + `.
3610
3979
  }
3611
3980
  if (!result.ok) throw new Error(result.error?.message ?? t("remoteRequestFailed"));
3612
3981
  return result.value;
3613
- };
3614
- ctx.effect(() => {
3982
+ }, statusFeed = createStatusFeed({
3983
+ url: STATUS_STREAM_PATH,
3984
+ readStatus: () => control("status"),
3985
+ onFallback: (reason) => {
3986
+ console.warn("ds-harness-remote: status event stream unavailable, polling status instead:", reason);
3987
+ }
3988
+ });
3989
+ ctx.effect(() => () => statusFeed.close(), "ds-harness-remote: status stream"), ctx.effect(() => {
3615
3990
  let disposed = !1, unsubscribeWorkspaces, unsubscribeSessions, selection, opening = !1, reconcile = () => {
3616
3991
  if (disposed || opening || selection === void 0) return;
3617
3992
  let pending = selection, workspaceSnapshot = ctx.workspaces.list.getSnapshot();
@@ -3635,32 +4010,23 @@ Minimum version required to store current data is: ` + bestVersion + `.
3635
4010
  }, "ds-harness-remote: resume selected workspace"), ctx.inject(["fileViewer"], (fileViewerContext) => {
3636
4011
  let viewer = fileViewerContext.get("fileViewer");
3637
4012
  viewer !== void 0 && fileViewerContext.effect(() => {
3638
- let active = !0, unregister, latestSaveAsAllowed = !1, latestSaveAsMaxBytes = REMOTE_FILE_SAVE_AS_MAX_BYTES, sync = async () => {
3639
- try {
3640
- let status = await control("status");
3641
- if (!active) return;
3642
- let supported = shouldUseRemoteFileViewer(status);
3643
- latestSaveAsAllowed = shouldAllowRemoteFileSaveAs(status), latestSaveAsMaxBytes = remoteFileSaveAsMaxBytes(status), supported && unregister === void 0 ? unregister = viewer.registerContentProvider(createRemoteFileContentProvider(
3644
- (endpoint, payload) => control(endpoint, payload),
3645
- { saveAsAllowed: () => latestSaveAsAllowed, saveAsMaxBytes: () => latestSaveAsMaxBytes }
3646
- )) : !supported && unregister !== void 0 && (unregister(), unregister = void 0, latestSaveAsAllowed = !1, latestSaveAsMaxBytes = REMOTE_FILE_SAVE_AS_MAX_BYTES);
3647
- } catch {
3648
- }
3649
- };
3650
- sync();
3651
- let timer = window.setInterval(() => {
3652
- sync();
3653
- }, 1500);
4013
+ let unregister, latestSaveAsAllowed = !1, latestSaveAsMaxBytes = REMOTE_FILE_SAVE_AS_MAX_BYTES, unsubscribe = statusFeed.subscribe((status) => {
4014
+ let supported = shouldUseRemoteFileViewer(status);
4015
+ latestSaveAsAllowed = shouldAllowRemoteFileSaveAs(status), latestSaveAsMaxBytes = remoteFileSaveAsMaxBytes(status), supported && unregister === void 0 ? unregister = viewer.registerContentProvider(createRemoteFileContentProvider(
4016
+ (endpoint, payload) => control(endpoint, payload),
4017
+ { saveAsAllowed: () => latestSaveAsAllowed, saveAsMaxBytes: () => latestSaveAsMaxBytes }
4018
+ )) : !supported && unregister !== void 0 && (unregister(), unregister = void 0, latestSaveAsAllowed = !1, latestSaveAsMaxBytes = REMOTE_FILE_SAVE_AS_MAX_BYTES);
4019
+ });
3654
4020
  return () => {
3655
- active = !1, window.clearInterval(timer), unregister?.();
4021
+ unsubscribe(), unregister?.();
3656
4022
  };
3657
4023
  }, "ds-harness-remote: remote file viewer provider");
3658
- }), ctx.effect(() => ctx.locale.register(localeNamespace, { zh, en }), "ds-harness-remote: dictionaries"), ctx.effect(installStyle, "ds-harness-remote: client styles"), ctx.slots.inject("shell.overlay", () => ctx.slots.register({
4024
+ }), ctx.effect(() => ctx.locale.register(localeNamespace, { zh, en }), "ds-harness-remote: dictionaries"), ctx.effect(installStyle, "ds-harness-remote: client styles"), ctx.effect(() => installTerminalAvailabilityNotice(statusFeed, t), "ds-harness-remote: terminal availability notice"), ctx.slots.inject("shell.overlay", () => ctx.slots.register({
3659
4025
  name: "shell.overlay",
3660
4026
  id: "ds-harness-remote-global-context",
3661
4027
  order: 20,
3662
4028
  locale: localeNamespace,
3663
- inject: () => ({ control })
4029
+ inject: () => ({ control, statusFeed })
3664
4030
  }, RemoteSessionHeaderAction)), ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
3665
4031
  name: "sidebar.footer.action",
3666
4032
  id: "ds-harness-remote-workspace",
@@ -3668,26 +4034,58 @@ Minimum version required to store current data is: ` + bestVersion + `.
3668
4034
  locale: localeNamespace,
3669
4035
  inject: () => ({
3670
4036
  control,
4037
+ statusFeed,
3671
4038
  preferredQrProvider: ctx.locale.getLocale().active === "zh" ? "zhihu" : "github"
3672
4039
  })
3673
- }, RemoteWorkspaceAction)), ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
3674
- name: "settings.plugin.item",
4040
+ }, RemoteWorkspaceAction));
4041
+ let registerSettingsItem = (slotName) => {
4042
+ try {
4043
+ ctx.slots.inject(slotName, () => ctx.slots.register({
4044
+ name: slotName,
4045
+ label: () => t("pluginTitle"),
4046
+ key: "ds-harness-remote",
4047
+ id: "ds-harness-remote",
4048
+ order: 30,
4049
+ locale: localeNamespace,
4050
+ inject: () => ({ control, statusFeed })
4051
+ }, RemotePluginOptions));
4052
+ } catch {
4053
+ }
4054
+ };
4055
+ registerSettingsItem("plugins.item"), registerSettingsItem("settings.plugin.item"), ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
4056
+ name: "plugins.bundle.config",
3675
4057
  key: "ds-harness-remote",
3676
- id: "ds-harness-remote",
3677
- order: 30,
3678
4058
  locale: localeNamespace,
3679
- inject: () => ({ control })
4059
+ inject: () => ({ control, statusFeed })
3680
4060
  }, RemotePluginOptions));
3681
4061
  }
3682
4062
  function isMissingControlRoute(reason) {
3683
4063
  return reason instanceof Error && reason.message.startsWith(`transport failure for ${CONTROL_RPC_PREFIX}/`) && (reason.message.endsWith(": HTTP 404") || reason.message.endsWith(": HTTP 405"));
3684
4064
  }
4065
+ function installTerminalAvailabilityNotice(feed, t) {
4066
+ let latest, notice, update = () => {
4067
+ let terminal = document.querySelector("[data-sidebar-terminal]"), unavailable = latest?.mode === "remote" && latest.remoteFeatures?.terminal !== !0;
4068
+ if (!terminal || !unavailable) {
4069
+ notice?.remove(), notice = void 0;
4070
+ return;
4071
+ }
4072
+ notice !== void 0 && notice.isConnected || (notice = document.createElement("div"), notice.className = "dshRemoteTerminalUnavailable", notice.setAttribute("role", "alert"), notice.textContent = t("terminalRemoteUnavailable"), terminal.prepend(notice));
4073
+ }, unsubscribe = feed.subscribe((status) => {
4074
+ latest = status, update();
4075
+ }), observer = new MutationObserver(update);
4076
+ return observer.observe(document.body, { childList: !0, subtree: !0 }), update(), () => {
4077
+ unsubscribe(), observer.disconnect(), notice?.remove();
4078
+ };
4079
+ }
3685
4080
  function messageOf(reason) {
3686
4081
  return reason instanceof Error ? reason.message : String(reason);
3687
4082
  }
3688
4083
  function formatPlatform(value) {
3689
4084
  let normalized = value.toLowerCase();
3690
- return normalized === "darwin" || normalized === "macos" ? "macOS" : normalized === "win32" || normalized === "windows" ? "Windows" : normalized === "linux" ? "Linux" : value;
4085
+ return normalized === "darwin" || normalized === "macos" ? "macOS" : normalized === "win32" || normalized === "windows" ? "Windows" : normalized === "linux" ? "Linux" : normalized === "android" ? "Android" : value;
4086
+ }
4087
+ function connectedClientModeLabel(mode, t) {
4088
+ return t(mode === "LAN" ? "remoteNetworkLan" : mode === "P2P" ? "remoteNetworkP2p" : mode === "TURN" ? "remoteNetworkTurn" : mode === "Relay" ? "remoteNetworkRelay" : "connected");
3691
4089
  }
3692
4090
  return module.exports.apply = apply, module.exports.inject = inject, module.exports;
3693
4091
  }