@xmanrui/dsh-im 4.19.1 → 4.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.en.md +96 -4
  2. package/README.md +96 -4
  3. package/lib/client.js +666 -217
  4. package/lib/index.js +277 -273
  5. package/package.json +13 -1
  6. package/plugin-src/client/channels/weixin/api.js +35 -17
  7. package/plugin-src/client/channels/weixin/connection-error.js +68 -0
  8. package/plugin-src/client/channels/weixin/index.js +36 -12
  9. package/plugin-src/client/i18n.js +2 -0
  10. package/plugin-src/client/index.js +15 -0
  11. package/plugin-src/client/interface-language.js +89 -0
  12. package/plugin-src/host/channels/shared/startup.mjs +30 -4
  13. package/plugin-src/host/channels/weixin/connection-supervisor.mjs +13 -1
  14. package/plugin-src/host/channels/weixin/index.mjs +12 -3
  15. package/plugin-src/host/channels/weixin/production.mjs +53 -3
  16. package/plugin-src/host/channels/weixin/rpc.mjs +22 -17
  17. package/plugin-src/host/host-language-rpc.mjs +71 -0
  18. package/plugin-src/host/host-language.mjs +157 -0
  19. package/plugin-src/host/index.mjs +15 -2
  20. package/scripts/verify-interface-language.mjs +333 -0
  21. package/src/channels/dingtalk/dingtalk-bridge.mjs +4 -1
  22. package/src/channels/dingtalk/dingtalk-menu.mjs +8 -4
  23. package/src/channels/discord/discord-runtime.mjs +1 -1
  24. package/src/channels/feishu/bridge.mjs +44 -13
  25. package/src/channels/qq/qq-bridge.mjs +12 -4
  26. package/src/channels/qq/qq-menu.mjs +11 -8
  27. package/src/channels/shared/bot-workspace-store.mjs +532 -40
  28. package/src/channels/shared/command-catalog.mjs +5 -0
  29. package/src/channels/shared/compact-command.mjs +14 -4
  30. package/src/channels/shared/control-command.mjs +1 -1
  31. package/src/channels/shared/deferred-delivery-coordinator.mjs +1 -1
  32. package/src/channels/shared/history-command.mjs +1 -1
  33. package/src/channels/shared/i18n-en/discord.mjs +2 -0
  34. package/src/channels/shared/i18n-en/shared-a.mjs +41 -0
  35. package/src/channels/shared/i18n-en/telegram.mjs +5 -0
  36. package/src/channels/shared/i18n-en/weixin.mjs +2 -0
  37. package/src/channels/shared/i18n.mjs +46 -3
  38. package/src/channels/shared/interface-language-store.mjs +127 -0
  39. package/src/channels/shared/interface-language.mjs +51 -0
  40. package/src/channels/shared/model-command.mjs +5 -3
  41. package/src/channels/shared/token-bot-controller.mjs +26 -0
  42. package/src/channels/shared/workspace-command.mjs +114 -9
  43. package/src/channels/shared/workspace-session.mjs +55 -5
  44. package/src/channels/telegram/telegram-runtime.mjs +76 -14
  45. package/src/channels/wecom/wecom-bridge.mjs +2 -2
  46. package/src/channels/weixin/connection-error.en.mjs +116 -0
  47. package/src/channels/weixin/connection-error.mjs +204 -0
  48. package/src/channels/weixin/diagnostic-details.mjs +40 -0
  49. package/src/channels/weixin/state-store.mjs +4 -3
  50. package/src/channels/weixin/weixin-api.mjs +20 -8
  51. package/src/channels/weixin/weixin-bridge.mjs +3 -2
  52. package/src/channels/weixin/weixin-controller.mjs +133 -104
  53. package/src/channels/weixin/weixin-runtime.mjs +35 -24
package/lib/client.js CHANGED
@@ -577,12 +577,12 @@ function callManagementRpc(connection, channel5, method, payload, signal) {
577
577
  }
578
578
 
579
579
  // plugin-src/client/index.js
580
- var React30 = __toESM(require("react"), 1);
580
+ var React31 = __toESM(require("react"), 1);
581
581
 
582
582
  // package.json
583
583
  var package_default = {
584
584
  name: "@xmanrui/dsh-im",
585
- version: "4.19.1",
585
+ version: "4.20.0",
586
586
  description: "\u628A\u5341\u4E00\u79CD IM \u6E20\u9053\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.",
587
587
  keywords: [
588
588
  "deepseek-harness",
@@ -634,6 +634,18 @@ var package_default = {
634
634
  {
635
635
  name: "geekyfoxlab",
636
636
  url: "https://github.com/geekyfoxlab"
637
+ },
638
+ {
639
+ name: "grloper",
640
+ url: "https://github.com/grloper"
641
+ },
642
+ {
643
+ name: "baijian",
644
+ url: "https://github.com/baijian"
645
+ },
646
+ {
647
+ name: "lyzhu86",
648
+ url: "https://github.com/lyzhu86"
637
649
  }
638
650
  ],
639
651
  license: "MIT",
@@ -911,10 +923,128 @@ function normalizeBotAlias(bot) {
911
923
  // plugin-src/client/agent-preset.js
912
924
  var React3 = __toESM(require("react"), 1);
913
925
 
926
+ // src/channels/weixin/connection-error.en.mjs
927
+ var connection_error_en_default = {
928
+ "\u63D2\u4EF6\u7248\u672C": "Plugin version",
929
+ "DSH \u5FAE\u4FE1\u7BA1\u7406\u63A5\u53E3\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u3002": "The DSH WeChat management endpoint returned an invalid response. Refresh the status.",
930
+ "\u65E0\u6CD5\u8BFB\u53D6\u73B0\u6709\u767B\u5F55\u51ED\u636E\u3002\u8BF7\u68C0\u67E5 DSH \u51ED\u636E\u5B58\u50A8\u3002": "Could not read the saved login credential. Check the DSH credential store.",
931
+ "\u767B\u5F55\u51ED\u636E\u65E0\u6CD5\u5199\u5165 DSH \u51ED\u636E\u5B58\u50A8\u3002\u8BF7\u68C0\u67E5\u51ED\u636E\u5B58\u50A8\u662F\u5426\u53EF\u5199\u3002": "Could not save the login credential to DSH. Check whether the credential store is writable.",
932
+ "\u8D26\u53F7\u914D\u7F6E\u65E0\u6CD5\u5199\u5165\u672C\u673A\u3002\u8BF7\u68C0\u67E5 DSH_HOME \u76EE\u5F55\u6743\u9650\u3002": "Could not save the account configuration. Check permissions on DSH_HOME.",
933
+ "\u65E0\u6CD5\u521D\u59CB\u5316\u8D26\u53F7\u72B6\u6001\u6216\u5DE5\u4F5C\u533A\u3002\u8BF7\u68C0\u67E5 DSH_HOME \u548C\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002": "Could not prepare the account state or workspace. Check DSH_HOME and the workspace directory.",
934
+ "\u63D2\u4EF6\u65E0\u6CD5\u8FDE\u63A5\u5F53\u524D Harness\uFF0C\u8BF7\u68C0\u67E5\u5BBF\u4E3B\u662F\u5426\u5DF2\u5C31\u7EEA\u3002": "The plugin could not connect to this Harness. Check whether the host is ready.",
935
+ "Harness \u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\u3002\u8BF7\u786E\u8BA4 dsh web \u672A\u963B\u585E\u3002": "The Harness health check timed out. Check whether the host is responsive.",
936
+ "Harness \u5065\u5EB7\u68C0\u67E5\u9700\u8981\u8EAB\u4EFD\u8BA4\u8BC1\u3002\u8BF7\u68C0\u67E5\u4EE3\u7406\u3001\u7F51\u5173\u6216\u81EA\u5B9A\u4E49\u9274\u6743\u914D\u7F6E\u3002": "The Harness health check requires authentication. Check proxy, gateway or custom authentication settings.",
937
+ "\u672C\u673A Harness \u8BF7\u6C42\u88AB\u4EE3\u7406\u8981\u6C42\u8BA4\u8BC1\u3002\u8BF7\u8BA9\u56DE\u73AF\u5730\u5740\u7ED5\u8FC7\u4EE3\u7406\uFF0C\u5E76\u68C0\u67E5 NO_PROXY \u914D\u7F6E\u3002": "The proxy requires authentication for the local Harness request. Bypass the proxy for loopback addresses and check NO_PROXY.",
938
+ "Harness \u5F02\u5E38\u62D2\u7EDD\u4E86\u56DE\u73AF\u5730\u5740\u7684\u5065\u5EB7\u68C0\u67E5\u3002\u8BF7\u68C0\u67E5 HTTP \u4EE3\u7406\u3001Harness \u6E90\u7801\u7248\u672C\u548C\u6784\u5EFA\u4EA7\u7269\u3002": "Harness rejected a loopback health check. Check the HTTP proxy and the running Harness build.",
939
+ "Harness \u7684 Host \u4FE1\u4EFB\u68C0\u67E5\u62D2\u7EDD\u4E86\u975E\u56DE\u73AF\u5730\u5740\u8BF7\u6C42\u3002\u8BF7\u68C0\u67E5 harnessBaseUrl \u4E0E trustedHosts \u914D\u7F6E\u3002": "Harness rejected the non-loopback Host. Check harnessBaseUrl and trustedHosts.",
940
+ "\u5065\u5EB7\u68C0\u67E5\u6536\u5230\u4E86\u975E Harness \u6807\u51C6\u7684 403 \u62D2\u7EDD\u54CD\u5E94\u3002\u8BF7\u68C0\u67E5\u4EE3\u7406\u6216\u7F51\u5173\u914D\u7F6E\u3002": "The health check received a non-standard HTTP 403 response. Check proxy or gateway settings.",
941
+ "\u627E\u4E0D\u5230 Harness \u5065\u5EB7\u68C0\u67E5\u63A5\u53E3\u3002\u8BF7\u786E\u8BA4 Harness \u4E0E\u63D2\u4EF6\u7248\u672C\u517C\u5BB9\u3002": "The Harness health check endpoint was not found. Check Harness and plugin compatibility.",
942
+ "Harness \u5065\u5EB7\u68C0\u67E5\u8FD4\u56DE\u670D\u52A1\u9519\u8BEF\u3002\u8BF7\u67E5\u770B DSH \u65E5\u5FD7\u3002": "The Harness health check returned a service error. Check the DSH logs.",
943
+ "Harness \u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\u3002\u8BF7\u786E\u8BA4 Harness \u4E0E\u63D2\u4EF6\u7248\u672C\u517C\u5BB9\u3002": "Harness returned an unrecognized response. Check Harness and plugin compatibility.",
944
+ "Harness \u62D2\u7EDD\u4E86\u5065\u5EB7\u68C0\u67E5\u8BF7\u6C42\u3002\u8BF7\u67E5\u770B DSH \u65E5\u5FD7\u3002": "Harness rejected the health check. Check the DSH logs.",
945
+ "Harness \u5065\u5EB7\u68C0\u67E5\u53D1\u751F\u672A\u77E5\u9519\u8BEF\u3002\u8BF7\u67E5\u770B DSH \u65E5\u5FD7\u3002": "The Harness health check failed with an unknown error. Check the DSH logs.",
946
+ "\u6D88\u606F\u8FDE\u63A5\u521D\u59CB\u5316\u5931\u8D25\u3002\u8BF7\u67E5\u770B DSH \u65E5\u5FD7\u540E\u91CD\u8BD5\u3002": "The message connection could not start. Check the DSH logs and retry.",
947
+ "\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE\u5FAE\u4FE1\u670D\u52A1\u3002": "The WeChat service is temporarily unreachable.",
948
+ "\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u8D85\u65F6\u3002": "The WeChat service request timed out.",
949
+ "\u5FAE\u4FE1\u670D\u52A1\u8BF7\u6C42\u5931\u8D25\uFF08HTTP {status}\uFF09\u3002": "The WeChat service request failed (HTTP {status}).",
950
+ "\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\u3002": "The WeChat service returned an unparseable response.",
951
+ "\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u4E8C\u7EF4\u7801\u3002": "The WeChat service did not return a valid QR code.",
952
+ "\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u626B\u7801\u5730\u5740\u3002": "The WeChat service returned an untrusted QR-code URL.",
953
+ "\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u8FDE\u63A5\u5730\u5740\u3002": "The WeChat service returned an invalid connection URL.",
954
+ "\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u53D7\u4FE1\u4EFB\u7684\u8FDE\u63A5\u5730\u5740\u3002": "The WeChat service returned an untrusted connection URL.",
955
+ "\u62D2\u7EDD\u8BBF\u95EE\u4E0D\u53D7\u4FE1\u4EFB\u7684\u5FAE\u4FE1\u670D\u52A1\u5730\u5740\u3002": "Refusing to access an untrusted WeChat service URL.",
956
+ "\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u626B\u7801\u72B6\u6001\u3002": "The WeChat service returned an unrecognized QR-code status.",
957
+ "\u5FAE\u4FE1\u6388\u6743\u6210\u529F\uFF0C\u4F46\u8FD4\u56DE\u7684\u8D26\u53F7\u51ED\u636E\u4E0D\u5B8C\u6574\u3002": "WeChat authorized the account but returned incomplete credentials.",
958
+ "\u5FAE\u4FE1\u670D\u52A1\u62D2\u7EDD\u4E86\u4E8C\u7EF4\u7801\u7533\u8BF7\u3002": "The WeChat service rejected the QR-code request.",
959
+ "\u5DF2\u53D6\u5F97\u626B\u7801\u5730\u5740\uFF0C\u4F46\u672C\u673A\u4E8C\u7EF4\u7801\u56FE\u7247\u751F\u6210\u5931\u8D25\u3002": "The login URL was obtained, but the QR-code image could not be generated locally.",
960
+ "\u65E0\u6CD5\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u3002": "Could not generate a WeChat QR code.",
961
+ "\u8FD9\u6B21\u5FAE\u4FE1\u7ED1\u5B9A\u4EFB\u52A1\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u6216\u751F\u6210\u4E8C\u7EF4\u7801\u3002": "This WeChat setup attempt no longer exists. Refresh the status or generate a new QR code.",
962
+ "\u8FD9\u6B21\u5FAE\u4FE1\u7ED1\u5B9A\u4EFB\u52A1\u5F53\u524D\u4E0D\u80FD\u63D0\u4EA4\u914D\u5BF9\u7801\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u3002": "This setup attempt is not waiting for a pairing code. Refresh the status.",
963
+ "\u767B\u5F55\u51ED\u636E\u7F3A\u5931\uFF0C\u8BF7\u79FB\u9664\u8D26\u53F7\u540E\u91CD\u65B0\u626B\u7801\u3002": "The login credential is missing. Remove the account and scan a new QR code.",
964
+ "\u5FAE\u4FE1\u767B\u5F55\u51ED\u636E\u5DF2\u5931\u6548\uFF0C\u8BF7\u79FB\u9664\u8D26\u53F7\u540E\u91CD\u65B0\u626B\u7801\u3002": "The WeChat login credential has expired. Remove the account and scan a new QR code.",
965
+ "\u65E0\u6CD5\u4ECE DSH \u51ED\u636E\u5B58\u50A8\u79FB\u9664\u5FAE\u4FE1\u767B\u5F55\u51ED\u636E\u3002": "Could not remove the WeChat login credential from the DSH credential store.",
966
+ "\u5FAE\u4FE1\u8D26\u53F7\u914D\u7F6E\u79FB\u9664\u5931\u8D25\u3002": "Could not remove the WeChat account configuration.",
967
+ "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u8D26\u53F7\u72B6\u6001\u6587\u4EF6\u3002": "Could not read the WeChat account state file.",
968
+ "\u65E0\u6CD5\u4FDD\u5B58\u5FAE\u4FE1\u8D26\u53F7\u72B6\u6001\u6587\u4EF6\u3002": "Could not save the WeChat account state file.",
969
+ "\u5FAE\u4FE1\u8D26\u53F7\u5DF2\u79FB\u9664\uFF0C\u4F46\u672C\u673A\u72B6\u6001\u6587\u4EF6\u6E05\u7406\u5931\u8D25\u3002": "The WeChat account was removed, but its local state file could not be cleaned up.",
970
+ "\u65E0\u6CD5\u4FDD\u5B58\u5FAE\u4FE1\u5DE5\u4F5C\u533A\u8BBE\u7F6E\u3002": "Could not save the WeChat workspace settings.",
971
+ "\u5FAE\u4FE1\u8D26\u53F7\u5DF2\u79FB\u9664\uFF0C\u4F46\u5DE5\u4F5C\u533A\u8BBE\u7F6E\u6E05\u7406\u5931\u8D25\u3002": "The WeChat account was removed, but its workspace settings could not be cleaned up.",
972
+ "\u5FAE\u4FE1\u8D26\u53F7\u8FDE\u63A5\u542F\u52A8\u5931\u8D25\u3002": "The WeChat account connection could not start.",
973
+ "\u5FAE\u4FE1\u6D88\u606F\u540C\u6B65\u8BF7\u6C42\u88AB\u62D2\u7EDD\u3002": "WeChat rejected the message sync request.",
974
+ "\u5FAE\u4FE1\u670D\u52A1\u672A\u786E\u8BA4\u505C\u6B62\u901A\u77E5\u3002": "WeChat did not confirm the stop notification.",
975
+ "\u5FAE\u4FE1\u6D88\u606F\u8FDE\u63A5\u505C\u6B62\u65F6\u53D1\u751F\u9519\u8BEF\u3002": "An error occurred while stopping the WeChat connection.",
976
+ "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u8FDE\u63A5\u72B6\u6001\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\uFF1B\u4E4B\u524D\u7684\u64CD\u4F5C\u53EF\u80FD\u5DF2\u5B8C\u6210\u3002": "Could not read the WeChat connection status. Refresh it; the previous operation may have completed.",
977
+ "\u5FAE\u4FE1\u6FC0\u6D3B\u8FC7\u7A0B\u4E2D\u53D1\u751F\u672A\u77E5\u9519\u8BEF\u3002": "An unknown error occurred while activating the WeChat account.",
978
+ "\u5FAE\u4FE1\u64CD\u4F5C\u53D1\u751F\u672A\u77E5\u9519\u8BEF\u3002": "The WeChat operation failed with an unknown error.",
979
+ "\u5FAE\u4FE1\u914D\u7F6E\u683C\u5F0F\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5 config.json \u548C workspaces.json \u540E\u91CD\u542F DSH\u3002": "The WeChat configuration is invalid. Check config.json and workspaces.json, then restart DSH.",
980
+ "\u5FAE\u4FE1\u914D\u7F6E\u8BBF\u95EE\u6743\u9650\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6570\u636E\u76EE\u5F55\u6743\u9650\u540E\u91CD\u542F DSH\u3002": "The WeChat configuration could not be accessed. Check the data directory permissions and restart DSH.",
981
+ "\u5FAE\u4FE1\u521D\u59CB\u5316\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B DSH \u542F\u52A8\u65E5\u5FD7\u3002": "WeChat initialization failed. Check the DSH startup logs.",
982
+ "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA\u3002": "The WeChat connection is not ready.",
983
+ "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\u540E\uFF0C\u539F\u8D26\u53F7\u72B6\u6001\u6062\u590D\u5931\u8D25\u3002": "The previous account state could not be restored after the failed WeChat operation.",
984
+ "\u539F\u8D26\u53F7\u72B6\u6001\u5C1A\u672A\u786E\u8BA4\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\uFF0C\u5E76\u53CD\u9988\u8BCA\u65AD\u4FE1\u606F\u3002": "The previous account state has not been confirmed. Refresh the status and share the diagnostic information.",
985
+ "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u5DF2\u6062\u590D\u4E4B\u524D\u7684\u672C\u673A\u72B6\u6001\u3002\u8BF7\u5904\u7406\u4E0A\u8FF0\u95EE\u9898\u540E\u91CD\u8BD5\u3002": "The operation did not complete. The previous local state was restored. Resolve the problem above before retrying.",
986
+ "\u51ED\u636E\u7531\u53EA\u8BFB\u6765\u6E90\u63D0\u4F9B\uFF0C\u8BF7\u5728\u542F\u52A8 DSH \u7684\u914D\u7F6E\u4E2D\u68C0\u67E5\u8BE5\u6765\u6E90\u3002": "The credential comes from a read-only source. Check that source in the configuration used to launch DSH.",
987
+ "\u5FAE\u4FE1\u670D\u52A1\u57DF\u540D\u89E3\u6790\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8FD0\u884C DSH \u7684\u673A\u5668\u7684\u7F51\u7EDC\u548C DNS \u8BBE\u7F6E\u540E\u91CD\u8BD5\u3002": "The WeChat service domain could not be resolved. Check the network and DNS settings on the machine running DSH and retry.",
988
+ "\u8BF7\u68C0\u67E5 DSH \u6570\u636E\u76EE\u5F55\u548C\u5BF9\u5E94\u6587\u4EF6\u7684\u8BFB\u5199\u6743\u9650\u3002": "Check read and write permissions on the DSH data directory and the affected file.",
989
+ "\u78C1\u76D8\u7A7A\u95F4\u4E0D\u8DB3\uFF0C\u8BF7\u91CA\u653E\u7A7A\u95F4\u540E\u91CD\u8BD5\u3002": "The disk is full. Free up space and retry.",
990
+ "\u672C\u673A\u6587\u4EF6\u683C\u5F0F\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u5BF9\u5E94\u914D\u7F6E\u6216\u72B6\u6001\u6587\u4EF6\uFF1B\u4E0D\u8981\u6E05\u7A7A\u767B\u5F55\u51ED\u636E\u3002": "The local file format is invalid. Check the affected configuration or state file; do not clear login credentials.",
991
+ "\u8BF7\u68C0\u67E5\u8FD0\u884C DSH \u7684\u673A\u5668\u7684\u7CFB\u7EDF\u65F6\u95F4\u3001\u8BC1\u4E66\u548C\u7F51\u7EDC\u8BBE\u7F6E\u3002": "Check the system time, certificates and network settings on the machine running DSH.",
992
+ "\u8BF7\u79FB\u9664\u5931\u6548\u63A5\u5165\u5E76\u91CD\u65B0\u626B\u7801\u7ED1\u5B9A\u3002": "Remove the invalid connection and scan a new QR code to connect.",
993
+ "\u8BF7\u786E\u8BA4\u5F53\u524D DSH \u5BBF\u4E3B\u5DF2\u5C31\u7EEA\uFF0C\u5E76\u68C0\u67E5 Harness \u4E0E\u63D2\u4EF6\u5B9E\u9645\u8FD0\u884C\u7248\u672C\u53CA\u5BBF\u4E3B\u65E5\u5FD7\u3002": "Check that this DSH host is ready, and check the running Harness and plugin versions and the host logs.",
994
+ "\u5FAE\u4FE1\u670D\u52A1\u9650\u6D41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "The WeChat service is rate-limiting requests. Try again later.",
995
+ "\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF0C\u8BF7\u68C0\u67E5\u670D\u52A1\u8BBF\u95EE\u9650\u5236\uFF1BHTTP \u72B6\u6001\u672C\u8EAB\u4E0D\u80FD\u786E\u8BA4\u767B\u5F55\u51ED\u636E\u5DF2\u5931\u6548\u3002": "Access was denied. Check service access restrictions; the HTTP status alone does not establish that the login credential expired.",
996
+ "\u8BF7\u68C0\u67E5\u8FD0\u884C DSH \u7684\u673A\u5668\u80FD\u5426\u8BBF\u95EE\u5FAE\u4FE1\u670D\u52A1\uFF0C\u7136\u540E\u91CD\u8BD5\u3002": "Check whether the machine running DSH can access the WeChat service, then retry.",
997
+ "\u8BF7\u6309\u53C2\u8003\u53F7\u67E5\u770B DSH \u65E5\u5FD7\uFF0C\u5E76\u590D\u5236\u8BCA\u65AD\u4FE1\u606F\u53CD\u9988\u3002": "Find this reference in the DSH logs and copy the diagnostic information when reporting the issue.",
998
+ "\u65E0\u6CD5\u5B8C\u6210\u5FAE\u4FE1\u7BA1\u7406\u8BF7\u6C42\uFF0C\u8BF7\u68C0\u67E5 DSH \u8FDE\u63A5\u540E\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u3002": "The WeChat management request could not be completed. Check the DSH connection and refresh the status.",
999
+ "\u67E5\u8BE2\u5FAE\u4FE1\u626B\u7801\u72B6\u6001\u5931\u8D25": "Could not query WeChat QR-code status",
1000
+ "\u65E0\u6CD5\u5B8C\u6210\u5FAE\u4FE1\u7BA1\u7406\u8BF7\u6C42": "Could not complete the WeChat management request",
1001
+ "\u65E0\u6CD5\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801": "Could not generate a WeChat QR code",
1002
+ "\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u4EE5\u4E0B\u662F\u4E0A\u6B21\u8BFB\u53D6\u7684\u72B6\u6001\u3002": "Status refresh failed. The last known status is shown below.",
1003
+ "\u8BCA\u65AD\u8BE6\u60C5": "Diagnostic details",
1004
+ "\u8BCA\u65AD\u4FE1\u606F": "Diagnostic information",
1005
+ "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F": "Copy diagnostic information",
1006
+ "\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236": "Diagnostic information copied",
1007
+ "\u672A\u53D6\u5F97 Host \u8BCA\u65AD\u53C2\u8003\u53F7\u3002": "No diagnostic reference was received from the host.",
1008
+ "\u65E0\u6CD5\u8BBF\u95EE\u526A\u8D34\u677F\uFF0C\u8BF7\u9009\u62E9\u5E76\u590D\u5236\u4EE5\u4E0B\u8BCA\u65AD\u4FE1\u606F\u3002": "Clipboard access is unavailable. Select and copy the diagnostic information below.",
1009
+ "\u9519\u8BEF\u7801": "Error code",
1010
+ "\u5931\u8D25\u9636\u6BB5": "Failed step",
1011
+ "\u5E95\u5C42\u539F\u56E0": "Underlying cause",
1012
+ "HTTP \u72B6\u6001": "HTTP status",
1013
+ "\u5FAE\u4FE1\u8FD4\u56DE\u7801": "WeChat return code",
1014
+ "\u53C2\u8003\u53F7": "Reference",
1015
+ "\u53D1\u751F\u65F6\u95F4": "Time",
1016
+ "\u52A0\u8F7D\u5FAE\u4FE1\u914D\u7F6E": "Load WeChat configuration",
1017
+ "\u7533\u8BF7\u4E8C\u7EF4\u7801": "Request QR code",
1018
+ "\u751F\u6210\u4E8C\u7EF4\u7801\u56FE\u7247": "Generate QR-code image",
1019
+ "\u67E5\u8BE2\u626B\u7801\u72B6\u6001": "Query QR-code status",
1020
+ "\u63D0\u4EA4\u914D\u5BF9\u7801": "Submit pairing code",
1021
+ "\u53D6\u6D88\u7ED1\u5B9A": "Cancel setup",
1022
+ "\u8BFB\u53D6\u767B\u5F55\u51ED\u636E": "Read login credential",
1023
+ "\u4FDD\u5B58\u767B\u5F55\u51ED\u636E": "Save login credential",
1024
+ "\u79FB\u9664\u767B\u5F55\u51ED\u636E": "Remove login credential",
1025
+ "\u4FDD\u5B58\u8D26\u53F7\u914D\u7F6E": "Save account configuration",
1026
+ "\u79FB\u9664\u8D26\u53F7\u914D\u7F6E": "Remove account configuration",
1027
+ "\u8BFB\u53D6\u8D26\u53F7\u72B6\u6001": "Read account state",
1028
+ "\u4FDD\u5B58\u8D26\u53F7\u72B6\u6001": "Save account state",
1029
+ "\u6E05\u7406\u8D26\u53F7\u72B6\u6001": "Clean up account state",
1030
+ "\u4FDD\u5B58\u5DE5\u4F5C\u533A\u8BBE\u7F6E": "Save workspace settings",
1031
+ "\u6E05\u7406\u5DE5\u4F5C\u533A\u8BBE\u7F6E": "Clean up workspace settings",
1032
+ "\u51C6\u5907\u6D88\u606F\u8FDE\u63A5": "Prepare message connection",
1033
+ "\u6FC0\u6D3B\u5FAE\u4FE1\u8D26\u53F7": "Activate WeChat account",
1034
+ "\u68C0\u67E5 DSH \u5BBF\u4E3B": "Check DSH host",
1035
+ "\u542F\u52A8\u5FAE\u4FE1\u8FDE\u63A5": "Start WeChat connection",
1036
+ "\u540C\u6B65\u5FAE\u4FE1\u6D88\u606F": "Sync WeChat messages",
1037
+ "\u505C\u6B62\u5FAE\u4FE1\u8FDE\u63A5": "Stop WeChat connection",
1038
+ "\u8BFB\u53D6\u8FDE\u63A5\u72B6\u6001": "Read connection status",
1039
+ "\u6062\u590D\u539F\u8D26\u53F7\u72B6\u6001": "Restore previous account state",
1040
+ "\u8BBF\u95EE DSH \u7BA1\u7406\u63A5\u53E3": "Access DSH management API"
1041
+ };
1042
+
914
1043
  // plugin-src/client/i18n.js
915
1044
  var React2 = __toESM(require("react"), 1);
916
1045
  var IM_LOCALE_NAMESPACE = "dsh-im";
917
1046
  var EN = Object.freeze({
1047
+ ...connection_error_en_default,
918
1048
  "$locale": "en",
919
1049
  "\u4FEE\u6539\u522B\u540D": "Edit alias",
920
1050
  "\u5173\u95ED\u4FEE\u6539\u522B\u540D": "Close alias editor",
@@ -11881,9 +12011,111 @@ function WecomAppSettingsTab({ rpcCall }) {
11881
12011
  )));
11882
12012
  }
11883
12013
 
11884
- // plugin-src/client/channels/weixin/index.js
12014
+ // plugin-src/client/channels/weixin/connection-error.js
11885
12015
  var React22 = __toESM(require("react"), 1);
11886
12016
 
12017
+ // src/channels/weixin/diagnostic-details.mjs
12018
+ var OPERATIONS = /* @__PURE__ */ new Set([
12019
+ "startup",
12020
+ "connection.restore",
12021
+ "connection.monitor",
12022
+ "connection.close",
12023
+ "connection.status",
12024
+ "provision.begin",
12025
+ "provision.poll",
12026
+ "provision.verify",
12027
+ "provision.cancel",
12028
+ "bot.reconnect",
12029
+ "bot.delete",
12030
+ "bot.workspace.set",
12031
+ "bot.model.set",
12032
+ "bot.agent-preset.set",
12033
+ "bot.context-enhancement.set",
12034
+ "bot.access-policy.set",
12035
+ "bot.alias.set"
12036
+ ]);
12037
+ var STAGES = /* @__PURE__ */ new Set([
12038
+ "startup.load",
12039
+ "qr.begin",
12040
+ "qr.encode",
12041
+ "qr.poll",
12042
+ "qr.verify",
12043
+ "qr.cancel",
12044
+ "credential.read",
12045
+ "credential.save",
12046
+ "credential.remove",
12047
+ "account.save",
12048
+ "account.remove",
12049
+ "state.load",
12050
+ "state.write",
12051
+ "state.cleanup",
12052
+ "workspace.write",
12053
+ "workspace.cleanup",
12054
+ "runtime.prepare",
12055
+ "activation",
12056
+ "harness.check",
12057
+ "connection.start",
12058
+ "connection.poll",
12059
+ "connection.stop",
12060
+ "status.read",
12061
+ "rollback",
12062
+ "management.request"
12063
+ ]);
12064
+ var REASONS = /* @__PURE__ */ new Set([
12065
+ "ENOTFOUND",
12066
+ "EAI_AGAIN",
12067
+ "ECONNREFUSED",
12068
+ "ECONNRESET",
12069
+ "EHOSTUNREACH",
12070
+ "ENETUNREACH",
12071
+ "ETIMEDOUT",
12072
+ "EPIPE",
12073
+ "UND_ERR_CONNECT_TIMEOUT",
12074
+ "UND_ERR_HEADERS_TIMEOUT",
12075
+ "UND_ERR_BODY_TIMEOUT",
12076
+ "UND_ERR_SOCKET",
12077
+ "CERT_HAS_EXPIRED",
12078
+ "CERT_NOT_YET_VALID",
12079
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
12080
+ "SELF_SIGNED_CERT_IN_CHAIN",
12081
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
12082
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
12083
+ "ERR_TLS_CERT_ALTNAME_INVALID",
12084
+ "ERR_SSL_WRONG_VERSION_NUMBER",
12085
+ "ENOENT",
12086
+ "EACCES",
12087
+ "EPERM",
12088
+ "ENOSPC",
12089
+ "EROFS",
12090
+ "ENOTDIR",
12091
+ "EISDIR",
12092
+ "EBUSY",
12093
+ "EIO",
12094
+ "EXDEV",
12095
+ "EMFILE",
12096
+ "ENFILE",
12097
+ "EEXIST",
12098
+ "invalid-json",
12099
+ "read-only"
12100
+ ]);
12101
+ var RESOURCES = /* @__PURE__ */ new Set(["credential-store", "account-config", "account-state", "workspace-config", "workspace-directory"]);
12102
+ function normalizeWeixinDiagnosticDetails(value) {
12103
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
12104
+ const result = {};
12105
+ for (const [field, allowed] of [["operation", OPERATIONS], ["stage", STAGES], ["reason", REASONS], ["resource", RESOURCES]]) {
12106
+ if (allowed.has(value[field])) result[field] = value[field];
12107
+ }
12108
+ if (/^WX-CONN-[A-F0-9]{8}$/.test(value.referenceId ?? "")) result.referenceId = value.referenceId;
12109
+ if (typeof value.occurredAt === "string" && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(value.occurredAt) && Number.isFinite(Date.parse(value.occurredAt))) result.occurredAt = value.occurredAt;
12110
+ if (Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599) result.httpStatus = value.httpStatus;
12111
+ const provider = typeof value.providerCode === "number" && Number.isSafeInteger(value.providerCode) ? String(value.providerCode) : value.providerCode;
12112
+ if (typeof provider === "string" && /^-?\d{1,12}$/.test(provider)) result.providerCode = provider;
12113
+ if (typeof value.pluginVersion === "string" && /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(value.pluginVersion) && value.pluginVersion.length <= 64) result.pluginVersion = value.pluginVersion;
12114
+ if (["succeeded", "failed", "not-attempted", "unknown"].includes(value.rollback)) result.rollback = value.rollback;
12115
+ if (typeof value.hint === "string" && value.hint.trim()) result.hint = value.hint.trim().slice(0, 500);
12116
+ return result;
12117
+ }
12118
+
11887
12119
  // plugin-src/client/channels/weixin/api.js
11888
12120
  var WEIXIN_RPC_CHANNEL = "/weixin";
11889
12121
  var WEIXIN_ENDPOINTS = Object.freeze({
@@ -11929,13 +12161,35 @@ function normalizeTestMessage5(value) {
11929
12161
  const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
11930
12162
  return { sent: false, code };
11931
12163
  }
12164
+ function normalizeConnectionError(value, fallbackCode = "WEIXIN_ERROR", fallbackMessage = "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5") {
12165
+ const details = normalizeWeixinDiagnosticDetails(value?.details);
12166
+ return {
12167
+ code: string(value?.code, fallbackCode).slice(0, 100),
12168
+ message: string(value?.message, fallbackMessage).slice(0, 500),
12169
+ ...Object.keys(details).length ? { details } : {}
12170
+ };
12171
+ }
12172
+ function managementRequestError(cause, operation) {
12173
+ if (cause?.name === "AbortError") return cause;
12174
+ const error = new Error("\u65E0\u6CD5\u5B8C\u6210\u5FAE\u4FE1\u7BA1\u7406\u8BF7\u6C42\uFF0C\u8BF7\u68C0\u67E5 DSH \u8FDE\u63A5\u540E\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u3002");
12175
+ error.code = "weixin-management-unreachable";
12176
+ error.details = normalizeWeixinDiagnosticDetails({ operation, stage: "management.request", occurredAt: (/* @__PURE__ */ new Date()).toISOString() });
12177
+ return error;
12178
+ }
12179
+ function invalidResponse() {
12180
+ const error = new Error("DSH \u5FAE\u4FE1\u7BA1\u7406\u63A5\u53E3\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u72B6\u6001\u3002");
12181
+ error.code = "weixin-management-invalid-response";
12182
+ error.details = { stage: "management.request", occurredAt: (/* @__PURE__ */ new Date()).toISOString() };
12183
+ return error;
12184
+ }
11932
12185
  function unwrapRpcResult9(result) {
11933
12186
  if (!isRecord7(result) || typeof result.ok !== "boolean") {
11934
- throw new Error("\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
12187
+ throw invalidResponse();
11935
12188
  }
11936
12189
  if (!result.ok) {
11937
- const error = new Error(string(result.error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25"));
11938
- error.code = string(result.error?.code, "WEIXIN_RPC_ERROR");
12190
+ if (!isRecord7(result.error)) throw invalidResponse();
12191
+ const visible = normalizeConnectionError(result.error, "WEIXIN_RPC_ERROR", "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25");
12192
+ const error = Object.assign(new Error(visible.message), visible);
11939
12193
  throw error;
11940
12194
  }
11941
12195
  return result.value;
@@ -11955,7 +12209,7 @@ function safeVerificationUrl(value) {
11955
12209
  }
11956
12210
  function normalizeProvisioning5(value) {
11957
12211
  if (!isRecord7(value) || !string(value.attemptId)) {
11958
- throw new Error("\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
12212
+ throw invalidResponse();
11959
12213
  }
11960
12214
  const status = PROVISION_STATES4.has(value.status) ? value.status : "failed";
11961
12215
  const result = {
@@ -11972,10 +12226,7 @@ function normalizeProvisioning5(value) {
11972
12226
  if (string(value.botId)) result.botId = string(value.botId);
11973
12227
  if (value.alreadyConnected === true) result.alreadyConnected = true;
11974
12228
  if (isRecord7(value.error)) {
11975
- result.error = {
11976
- code: string(value.error.code, "WEIXIN_PROVISION_FAILED"),
11977
- message: string(value.error.message, "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210")
11978
- };
12229
+ result.error = normalizeConnectionError(value.error, "WEIXIN_PROVISION_FAILED", "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210");
11979
12230
  }
11980
12231
  return result;
11981
12232
  }
@@ -12008,15 +12259,12 @@ function normalizeBot6(value) {
12008
12259
  messagesReplied: Math.max(0, Number(value.stats?.messagesReplied) || 0)
12009
12260
  },
12010
12261
  lastMessageError: normalizeLastMessageError(value.lastMessageError),
12011
- error: isRecord7(value.error) ? {
12012
- code: string(value.error.code, "WEIXIN_ACCOUNT_ERROR"),
12013
- message: string(value.error.message, "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA")
12014
- } : null
12262
+ error: isRecord7(value.error) ? normalizeConnectionError(value.error, "WEIXIN_ACCOUNT_ERROR", "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA") : null
12015
12263
  };
12016
12264
  }
12017
12265
  function normalizeSnapshot8(value) {
12018
12266
  if (!isRecord7(value) || !Array.isArray(value.bots)) {
12019
- throw new Error("\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u8D26\u53F7\u5217\u8868");
12267
+ throw invalidResponse();
12020
12268
  }
12021
12269
  const bots = value.bots.map(normalizeBot6).filter(Boolean);
12022
12270
  return {
@@ -12030,23 +12278,130 @@ function normalizeSnapshot8(value) {
12030
12278
  },
12031
12279
  provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
12032
12280
  testMessage: normalizeTestMessage5(value.testMessage),
12281
+ warnings: Array.isArray(value.warnings) ? value.warnings.filter(isRecord7).slice(0, 8).map((error) => normalizeConnectionError(error)) : [],
12033
12282
  agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
12034
12283
  modelCatalog: normalizeModelCatalog(value.modelCatalog)
12035
12284
  };
12036
12285
  }
12037
12286
  function presentError9(error) {
12038
- return {
12039
- code: string(error?.code, "WEIXIN_ERROR"),
12040
- message: string(error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
12041
- };
12287
+ return normalizeConnectionError(error);
12042
12288
  }
12043
12289
  function formatRemaining5(milliseconds) {
12044
12290
  const seconds = Math.max(0, Math.ceil(milliseconds / 1e3));
12045
12291
  return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
12046
12292
  }
12047
12293
 
12294
+ // plugin-src/client/channels/weixin/connection-error.js
12295
+ var STAGE_LABELS = {
12296
+ "startup.load": "\u52A0\u8F7D\u5FAE\u4FE1\u914D\u7F6E",
12297
+ "qr.begin": "\u7533\u8BF7\u4E8C\u7EF4\u7801",
12298
+ "qr.encode": "\u751F\u6210\u4E8C\u7EF4\u7801\u56FE\u7247",
12299
+ "qr.poll": "\u67E5\u8BE2\u626B\u7801\u72B6\u6001",
12300
+ "qr.verify": "\u63D0\u4EA4\u914D\u5BF9\u7801",
12301
+ "qr.cancel": "\u53D6\u6D88\u7ED1\u5B9A",
12302
+ "credential.read": "\u8BFB\u53D6\u767B\u5F55\u51ED\u636E",
12303
+ "credential.save": "\u4FDD\u5B58\u767B\u5F55\u51ED\u636E",
12304
+ "credential.remove": "\u79FB\u9664\u767B\u5F55\u51ED\u636E",
12305
+ "account.save": "\u4FDD\u5B58\u8D26\u53F7\u914D\u7F6E",
12306
+ "account.remove": "\u79FB\u9664\u8D26\u53F7\u914D\u7F6E",
12307
+ "state.load": "\u8BFB\u53D6\u8D26\u53F7\u72B6\u6001",
12308
+ "state.write": "\u4FDD\u5B58\u8D26\u53F7\u72B6\u6001",
12309
+ "state.cleanup": "\u6E05\u7406\u8D26\u53F7\u72B6\u6001",
12310
+ "workspace.write": "\u4FDD\u5B58\u5DE5\u4F5C\u533A\u8BBE\u7F6E",
12311
+ "workspace.cleanup": "\u6E05\u7406\u5DE5\u4F5C\u533A\u8BBE\u7F6E",
12312
+ "runtime.prepare": "\u51C6\u5907\u6D88\u606F\u8FDE\u63A5",
12313
+ activation: "\u6FC0\u6D3B\u5FAE\u4FE1\u8D26\u53F7",
12314
+ "harness.check": "\u68C0\u67E5 DSH \u5BBF\u4E3B",
12315
+ "connection.start": "\u542F\u52A8\u5FAE\u4FE1\u8FDE\u63A5",
12316
+ "connection.poll": "\u540C\u6B65\u5FAE\u4FE1\u6D88\u606F",
12317
+ "connection.stop": "\u505C\u6B62\u5FAE\u4FE1\u8FDE\u63A5",
12318
+ "status.read": "\u8BFB\u53D6\u8FDE\u63A5\u72B6\u6001",
12319
+ rollback: "\u6062\u590D\u539F\u8D26\u53F7\u72B6\u6001",
12320
+ "management.request": "\u8BBF\u95EE DSH \u7BA1\u7406\u63A5\u53E3"
12321
+ };
12322
+ function provisioningErrorTitle(error) {
12323
+ const stage = error?.details?.stage;
12324
+ if (stage === "qr.begin" || stage === "qr.encode") return "\u65E0\u6CD5\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801";
12325
+ if (stage === "qr.poll") return "\u67E5\u8BE2\u5FAE\u4FE1\u626B\u7801\u72B6\u6001\u5931\u8D25";
12326
+ if (stage === "management.request") return "\u65E0\u6CD5\u5B8C\u6210\u5FAE\u4FE1\u7BA1\u7406\u8BF7\u6C42";
12327
+ return "\u5FAE\u4FE1\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210";
12328
+ }
12329
+ function formatWeixinDiagnostic(value) {
12330
+ const error = normalizeConnectionError(value);
12331
+ const details = error.details ?? {};
12332
+ return [
12333
+ localizeText(error.message),
12334
+ details.hint ? localizeText(details.hint) : null,
12335
+ localizeText("\u9519\u8BEF\u7801") + ": " + error.code,
12336
+ ...["operation", "stage", "reason", "httpStatus", "providerCode", "resource", "referenceId", "occurredAt", "rollback", "pluginVersion"].filter((field) => details[field] !== void 0).map((field) => `${field}: ${details[field]}`)
12337
+ ].filter(Boolean).join("\n");
12338
+ }
12339
+ function WeixinConnectionError({ error: value, warning = false }) {
12340
+ const error = normalizeConnectionError(value);
12341
+ const details = error.details ?? {};
12342
+ const [copyState, setCopyState] = React22.useState(null);
12343
+ React22.useEffect(() => setCopyState(null), [details.referenceId, error.code, error.message]);
12344
+ const copy = async () => {
12345
+ try {
12346
+ if (typeof globalThis.navigator?.clipboard?.writeText !== "function") throw new Error("clipboard unavailable");
12347
+ await globalThis.navigator.clipboard.writeText(formatWeixinDiagnostic(error));
12348
+ setCopyState("copied");
12349
+ } catch {
12350
+ setCopyState("manual");
12351
+ }
12352
+ };
12353
+ const fields = [
12354
+ ["\u9519\u8BEF\u7801", error.code],
12355
+ ["\u5931\u8D25\u9636\u6BB5", localizeText(STAGE_LABELS[details.stage] ?? details.stage ?? "")],
12356
+ ["\u5E95\u5C42\u539F\u56E0", details.reason],
12357
+ ["HTTP \u72B6\u6001", details.httpStatus],
12358
+ ["\u5FAE\u4FE1\u8FD4\u56DE\u7801", details.providerCode],
12359
+ ["\u53C2\u8003\u53F7", details.referenceId],
12360
+ ["\u53D1\u751F\u65F6\u95F4", details.occurredAt],
12361
+ ["\u63D2\u4EF6\u7248\u672C", details.pluginVersion]
12362
+ ].filter(([, text7]) => text7 !== void 0 && text7 !== "");
12363
+ return h2(
12364
+ "div",
12365
+ { className: "dxw-summary dim-cardSummary", "data-weixin-diagnostic": true, role: warning ? "status" : void 0 },
12366
+ h2("p", null, error.message),
12367
+ details.hint ? h2("p", null, details.hint) : null,
12368
+ h2(
12369
+ "details",
12370
+ { style: { marginTop: 8 } },
12371
+ h2("summary", { style: { cursor: "pointer" } }, "\u8BCA\u65AD\u8BE6\u60C5"),
12372
+ h2(
12373
+ "dl",
12374
+ { style: { display: "grid", gridTemplateColumns: "auto minmax(0, 1fr)", gap: "4px 12px", margin: "10px 0" } },
12375
+ ...fields.flatMap(([label, text7]) => [
12376
+ h2("dt", { key: `${label}-label` }, label),
12377
+ React22.createElement("dd", { key: label, style: { margin: 0, overflowWrap: "anywhere" } }, String(text7))
12378
+ ])
12379
+ ),
12380
+ !details.referenceId ? h2("p", null, "\u672A\u53D6\u5F97 Host \u8BCA\u65AD\u53C2\u8003\u53F7\u3002") : null,
12381
+ h2(
12382
+ "div",
12383
+ { className: "dim-viewActions" },
12384
+ h2("button", { type: "button", className: "dxw-button", onClick: copy }, copyState === "copied" ? "\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236" : "\u590D\u5236\u8BCA\u65AD\u4FE1\u606F")
12385
+ ),
12386
+ copyState === "manual" ? h2(
12387
+ "div",
12388
+ null,
12389
+ h2("p", null, "\u65E0\u6CD5\u8BBF\u95EE\u526A\u8D34\u677F\uFF0C\u8BF7\u9009\u62E9\u5E76\u590D\u5236\u4EE5\u4E0B\u8BCA\u65AD\u4FE1\u606F\u3002"),
12390
+ React22.createElement("textarea", {
12391
+ readOnly: true,
12392
+ value: formatWeixinDiagnostic(error),
12393
+ rows: 7,
12394
+ "aria-label": localizeText("\u8BCA\u65AD\u4FE1\u606F"),
12395
+ style: { width: "100%", boxSizing: "border-box", marginTop: 8 }
12396
+ })
12397
+ ) : null
12398
+ )
12399
+ );
12400
+ }
12401
+
12048
12402
  // plugin-src/client/channels/weixin/index.js
12049
- var Button14 = React22.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
12403
+ var React23 = __toESM(require("react"), 1);
12404
+ var Button14 = React23.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
12050
12405
  return h2("button", {
12051
12406
  ...props,
12052
12407
  ref,
@@ -12119,14 +12474,14 @@ function EmptyView6({ onStart, busy }) {
12119
12474
  );
12120
12475
  }
12121
12476
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
12122
- const [imageFailed, setImageFailed] = React22.useState(false);
12477
+ const [imageFailed, setImageFailed] = React23.useState(false);
12123
12478
  const source = safeQrSource5(provision.qrCodeDataUrl);
12124
12479
  const href = safeVerificationUrl(provision.verificationUrl);
12125
12480
  const remaining = Math.max(0, provision.expiresAt - now);
12126
12481
  const expired = remaining === 0 || provision.status === "expired";
12127
12482
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
12128
12483
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
12129
- React22.useEffect(() => setImageFailed(false), [source]);
12484
+ React23.useEffect(() => setImageFailed(false), [source]);
12130
12485
  return h2(
12131
12486
  "div",
12132
12487
  { className: "dxw-card dim-surfaceCard" },
@@ -12193,9 +12548,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
12193
12548
  );
12194
12549
  }
12195
12550
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
12196
- const [code, setCode] = React22.useState("");
12551
+ const [code, setCode] = React23.useState("");
12197
12552
  const valid = /^\d{4,8}$/.test(code);
12198
- React22.useEffect(() => setCode(""), [provision.attemptId]);
12553
+ React23.useEffect(() => setCode(""), [provision.attemptId]);
12199
12554
  return h2(
12200
12555
  "div",
12201
12556
  { className: "dxw-card dim-surfaceCard" },
@@ -12262,9 +12617,8 @@ function ProvisionError3({ provision, busy, onRetry, onClose }) {
12262
12617
  h2(
12263
12618
  "div",
12264
12619
  { className: "dxw-error dim-inlineError", role: "alert" },
12265
- h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : "\u5FAE\u4FE1\u6CA1\u6709\u7ED1\u5B9A\u5B8C\u6210"),
12266
- h2("p", null, error.message),
12267
- h2("span", { className: "dxw-errorCode" }, error.code),
12620
+ h2("h3", null, provision.status === "expired" ? "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F" : provisioningErrorTitle(error)),
12621
+ h2(WeixinConnectionError, { error }),
12268
12622
  h2(
12269
12623
  "div",
12270
12624
  { className: "dxw-actions dim-viewActions" },
@@ -12390,7 +12744,7 @@ function AccountCard5({
12390
12744
  ),
12391
12745
  h2(Button14, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
12392
12746
  ),
12393
- summary2 ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary2) : null,
12747
+ account.error ? h2(WeixinConnectionError, { error: account.error }) : summary2 ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary2) : null,
12394
12748
  account.lastMessageError ? h2(LastMessageErrorSummary, {
12395
12749
  className: "dxw-summary",
12396
12750
  error: account.lastMessageError
@@ -12399,7 +12753,7 @@ function AccountCard5({
12399
12753
  className: "dxw-summary dim-cardFeedback",
12400
12754
  role: "status",
12401
12755
  "aria-live": "polite"
12402
- }, feedback) : null
12756
+ }, typeof feedback === "string" ? feedback : h2(WeixinConnectionError, { error: feedback })) : null
12403
12757
  )
12404
12758
  )
12405
12759
  )
@@ -12463,7 +12817,7 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
12463
12817
  };
12464
12818
  }
12465
12819
  function WeixinSettingsTab({ rpcCall }) {
12466
- const [model, setModel] = React22.useState({
12820
+ const [model, setModel] = React23.useState({
12467
12821
  phase: "loading",
12468
12822
  bots: [],
12469
12823
  totals: EMPTY_TOTALS3,
@@ -12472,33 +12826,41 @@ function WeixinSettingsTab({ rpcCall }) {
12472
12826
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
12473
12827
  modelCatalog: EMPTY_MODEL_CATALOG
12474
12828
  });
12475
- const [provision, setProvision] = React22.useState(null);
12476
- const [busy, setBusy] = React22.useState(false);
12477
- const [busyByBot, setBusyByBot] = React22.useState({});
12478
- const [feedbackByBot, setFeedbackByBot] = React22.useState({});
12479
- const [removeTarget, setRemoveTarget] = React22.useState(null);
12480
- const [notice, setNotice] = React22.useState("");
12481
- const [now, setNow] = React22.useState(() => Date.now());
12482
- const addButtonRef = React22.useRef(null);
12483
- const mountedRef = React22.useRef(true);
12829
+ const [provision, setProvision] = React23.useState(null);
12830
+ const [busy, setBusy] = React23.useState(false);
12831
+ const [busyByBot, setBusyByBot] = React23.useState({});
12832
+ const [feedbackByBot, setFeedbackByBot] = React23.useState({});
12833
+ const [removeTarget, setRemoveTarget] = React23.useState(null);
12834
+ const [notice, setNotice] = React23.useState("");
12835
+ const [operationWarnings, setOperationWarnings] = React23.useState([]);
12836
+ const [now, setNow] = React23.useState(() => Date.now());
12837
+ const addButtonRef = React23.useRef(null);
12838
+ const mountedRef = React23.useRef(true);
12484
12839
  const workspaceFence = useWorkspaceSnapshotFence();
12485
12840
  const scheduleAnimationFrame = useAnimationFrameScheduler();
12486
- React22.useEffect(() => {
12841
+ React23.useEffect(() => {
12487
12842
  mountedRef.current = true;
12488
12843
  return () => {
12489
12844
  mountedRef.current = false;
12490
12845
  };
12491
12846
  }, []);
12492
- const announce = React22.useCallback((value) => {
12847
+ const announce = React23.useCallback((value) => {
12493
12848
  setNotice("");
12494
12849
  scheduleAnimationFrame(() => {
12495
12850
  if (value) setNotice(value);
12496
12851
  }, "announcement");
12497
12852
  }, [scheduleAnimationFrame]);
12498
- const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
12499
- return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
12853
+ const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
12854
+ let result;
12855
+ try {
12856
+ result = await rpcCall(endpoint, payload, signal);
12857
+ } catch (error) {
12858
+ if (signal?.aborted) throw error;
12859
+ throw managementRequestError(error, endpoint);
12860
+ }
12861
+ return unwrapRpcResult9(result);
12500
12862
  }, [rpcCall]);
12501
- const loadStatus = React22.useCallback(async ({
12863
+ const loadStatus = React23.useCallback(async ({
12502
12864
  signal,
12503
12865
  silent = false,
12504
12866
  restoreProvisioning = false
@@ -12536,12 +12898,12 @@ function WeixinSettingsTab({ rpcCall }) {
12536
12898
  return void 0;
12537
12899
  }
12538
12900
  }, [invoke, workspaceFence]);
12539
- React22.useEffect(() => {
12901
+ React23.useEffect(() => {
12540
12902
  const controller = new AbortController();
12541
12903
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
12542
12904
  return () => controller.abort();
12543
12905
  }, [loadStatus]);
12544
- React22.useEffect(() => {
12906
+ React23.useEffect(() => {
12545
12907
  if (model.phase !== "ready") return void 0;
12546
12908
  const controller = new AbortController();
12547
12909
  let running = false;
@@ -12560,12 +12922,12 @@ function WeixinSettingsTab({ rpcCall }) {
12560
12922
  window.clearInterval(timer);
12561
12923
  };
12562
12924
  }, [loadStatus, model.phase]);
12563
- React22.useEffect(() => {
12925
+ React23.useEffect(() => {
12564
12926
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
12565
12927
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
12566
12928
  return () => window.clearInterval(timer);
12567
12929
  }, [provision?.attemptId, provision?.status]);
12568
- const startProvisioning = React22.useCallback(async ({ replace = false } = {}) => {
12930
+ const startProvisioning = React23.useCallback(async ({ replace = false } = {}) => {
12569
12931
  setBusy(true);
12570
12932
  try {
12571
12933
  if (replace && provision?.attemptId) {
@@ -12586,7 +12948,7 @@ function WeixinSettingsTab({ rpcCall }) {
12586
12948
  setBusy(false);
12587
12949
  }
12588
12950
  }, [announce, invoke, provision?.attemptId]);
12589
- const cancelProvisioning = React22.useCallback(async () => {
12951
+ const cancelProvisioning = React23.useCallback(async () => {
12590
12952
  setBusy(true);
12591
12953
  try {
12592
12954
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -12601,7 +12963,7 @@ function WeixinSettingsTab({ rpcCall }) {
12601
12963
  setBusy(false);
12602
12964
  }
12603
12965
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
12604
- const submitVerification = React22.useCallback(async (verifyCode) => {
12966
+ const submitVerification = React23.useCallback(async (verifyCode) => {
12605
12967
  if (!provision?.attemptId) return;
12606
12968
  setBusy(true);
12607
12969
  try {
@@ -12617,7 +12979,7 @@ function WeixinSettingsTab({ rpcCall }) {
12617
12979
  setBusy(false);
12618
12980
  }
12619
12981
  }, [announce, invoke, provision?.attemptId]);
12620
- React22.useEffect(() => {
12982
+ React23.useEffect(() => {
12621
12983
  const attemptId = provision?.attemptId;
12622
12984
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
12623
12985
  const controller = new AbortController();
@@ -12665,7 +13027,7 @@ function WeixinSettingsTab({ rpcCall }) {
12665
13027
  controller.abort();
12666
13028
  };
12667
13029
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
12668
- const setBotBusy = React22.useCallback((botId, value) => {
13030
+ const setBotBusy = React23.useCallback((botId, value) => {
12669
13031
  setBusyByBot((current) => {
12670
13032
  const next = { ...current };
12671
13033
  if (value) next[botId] = value;
@@ -12673,7 +13035,7 @@ function WeixinSettingsTab({ rpcCall }) {
12673
13035
  return next;
12674
13036
  });
12675
13037
  }, []);
12676
- const reconnect = React22.useCallback(async (account) => {
13038
+ const reconnect = React23.useCallback(async (account) => {
12677
13039
  const snapshotVersion = workspaceFence.beginMutation();
12678
13040
  setBotBusy(account.botId, "reconnect");
12679
13041
  setFeedbackByBot((current) => {
@@ -12713,21 +13075,22 @@ function WeixinSettingsTab({ rpcCall }) {
12713
13075
  setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
12714
13076
  }
12715
13077
  announce(feedback);
12716
- } catch {
12717
- const feedback = "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
13078
+ } catch (error) {
13079
+ const feedback = presentError9(error);
12718
13080
  if (mountedRef.current) {
12719
13081
  setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
12720
13082
  }
12721
- announce(feedback);
13083
+ announce(feedback.message);
12722
13084
  } finally {
12723
13085
  const shouldRefresh = workspaceFence.endMutation();
12724
13086
  if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
12725
13087
  setBotBusy(account.botId, null);
12726
13088
  }
12727
13089
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
12728
- const saveWorkspace = React22.useCallback(async (account, workspace) => {
13090
+ const saveWorkspace = React23.useCallback(async (account, workspace) => {
12729
13091
  const workspaceVersion = workspaceFence.beginMutation();
12730
13092
  setBotBusy(account.botId, "workspace");
13093
+ setFeedbackByBot((current) => ({ ...current, [account.botId]: null }));
12731
13094
  try {
12732
13095
  const snapshot = normalizeSnapshot8(await invoke(
12733
13096
  WEIXIN_ENDPOINTS.setWorkspace,
@@ -12744,15 +13107,19 @@ function WeixinSettingsTab({ rpcCall }) {
12744
13107
  modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
12745
13108
  });
12746
13109
  }
13110
+ } catch (error) {
13111
+ if (mountedRef.current) setFeedbackByBot((current) => ({ ...current, [account.botId]: presentError9(error) }));
13112
+ throw error;
12747
13113
  } finally {
12748
13114
  const shouldRefresh = workspaceFence.endMutation();
12749
13115
  if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
12750
13116
  if (mountedRef.current) setBotBusy(account.botId, null);
12751
13117
  }
12752
13118
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
12753
- const saveBotSetting = React22.useCallback(async (account, operation, endpoint, payload) => {
13119
+ const saveBotSetting = React23.useCallback(async (account, operation, endpoint, payload) => {
12754
13120
  const snapshotVersion = workspaceFence.beginMutation();
12755
13121
  setBotBusy(account.botId, operation);
13122
+ setFeedbackByBot((current) => ({ ...current, [account.botId]: null }));
12756
13123
  try {
12757
13124
  const snapshot = normalizeSnapshot8(await invoke(
12758
13125
  endpoint,
@@ -12769,15 +13136,20 @@ function WeixinSettingsTab({ rpcCall }) {
12769
13136
  modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
12770
13137
  });
12771
13138
  }
13139
+ } catch (error) {
13140
+ if (mountedRef.current) setFeedbackByBot((current) => ({ ...current, [account.botId]: presentError9(error) }));
13141
+ throw error;
12772
13142
  } finally {
12773
13143
  const shouldRefresh = workspaceFence.endMutation();
12774
13144
  if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
12775
13145
  if (mountedRef.current) setBotBusy(account.botId, null);
12776
13146
  }
12777
13147
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
12778
- const remove = React22.useCallback(async (account) => {
13148
+ const remove = React23.useCallback(async (account) => {
12779
13149
  const snapshotVersion = workspaceFence.beginMutation();
12780
13150
  setBotBusy(account.botId, "delete");
13151
+ setOperationWarnings([]);
13152
+ setFeedbackByBot((current) => ({ ...current, [account.botId]: null }));
12781
13153
  try {
12782
13154
  const snapshot = normalizeSnapshot8(await invoke(WEIXIN_ENDPOINTS.deleteBot, {
12783
13155
  botId: account.botId,
@@ -12793,10 +13165,13 @@ function WeixinSettingsTab({ rpcCall }) {
12793
13165
  modelCatalog: snapshot.modelCatalog ?? current.modelCatalog
12794
13166
  }));
12795
13167
  }
13168
+ setOperationWarnings(snapshot.warnings);
12796
13169
  setRemoveTarget(null);
12797
13170
  announce("\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002");
12798
13171
  } catch (error) {
12799
- announce(`\u79FB\u9664\u5931\u8D25\uFF1A${presentError9(error).message}`);
13172
+ const failure = presentError9(error);
13173
+ if (mountedRef.current) setFeedbackByBot((current) => ({ ...current, [account.botId]: failure }));
13174
+ announce(failure.message);
12800
13175
  } finally {
12801
13176
  const shouldRefresh = workspaceFence.endMutation();
12802
13177
  if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
@@ -12850,7 +13225,13 @@ function WeixinSettingsTab({ rpcCall }) {
12850
13225
  addButtonRef
12851
13226
  }),
12852
13227
  h2("div", { className: "dxw-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
12853
- model.error && model.phase === "ready" ? h2("div", { className: "dxw-statusNotice dim-statusNotice" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null,
13228
+ ...operationWarnings.map((error, index) => h2(WeixinConnectionError, { key: error.details?.referenceId ?? index, error, warning: true })),
13229
+ model.error && model.phase === "ready" ? h2(
13230
+ "div",
13231
+ { className: "dxw-statusNotice dim-statusNotice" },
13232
+ h2("p", null, "\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u4EE5\u4E0B\u662F\u4E0A\u6B21\u8BFB\u53D6\u7684\u72B6\u6001\u3002"),
13233
+ h2(WeixinConnectionError, { error: model.error })
13234
+ ) : null,
12854
13235
  model.phase === "loading" ? h2(LoadingView6) : model.phase === "error" ? h2(
12855
13236
  "div",
12856
13237
  { className: "dxw-card dim-surfaceCard" },
@@ -12858,11 +13239,11 @@ function WeixinSettingsTab({ rpcCall }) {
12858
13239
  "div",
12859
13240
  { className: "dxw-error dim-inlineError" },
12860
13241
  h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001"),
12861
- h2("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"),
13242
+ h2(WeixinConnectionError, { error: model.error }),
12862
13243
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
12863
13244
  )
12864
13245
  ) : h2(
12865
- React22.Fragment,
13246
+ React23.Fragment,
12866
13247
  null,
12867
13248
  provisionView,
12868
13249
  model.bots.length === 0 && !provision ? h2(EmptyView6, { onStart: () => void startProvisioning(), busy }) : null,
@@ -13148,7 +13529,7 @@ function formatRemaining6(milliseconds) {
13148
13529
  }
13149
13530
 
13150
13531
  // plugin-src/client/channels/whatsapp/index.js
13151
- var React23 = __toESM(require("react"), 1);
13532
+ var React24 = __toESM(require("react"), 1);
13152
13533
 
13153
13534
  // plugin-src/client/channels/whatsapp/styles.js
13154
13535
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -13173,7 +13554,7 @@ function installWhatsappStyles() {
13173
13554
 
13174
13555
  // plugin-src/client/channels/whatsapp/index.js
13175
13556
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
13176
- var Button16 = React23.forwardRef(function Button17({ children, kind = "secondary", className = "", ...props }, ref) {
13557
+ var Button16 = React24.forwardRef(function Button17({ children, kind = "secondary", className = "", ...props }, ref) {
13177
13558
  return h2("button", {
13178
13559
  ...props,
13179
13560
  ref,
@@ -13527,7 +13908,7 @@ function WhatsappAccountCard({
13527
13908
  );
13528
13909
  }
13529
13910
  function WhatsappSettingsTab({ rpcCall }) {
13530
- const [model, setModel] = React23.useState({
13911
+ const [model, setModel] = React24.useState({
13531
13912
  phase: "loading",
13532
13913
  bots: [],
13533
13914
  totals: { configured: 0, connected: 0 },
@@ -13535,16 +13916,16 @@ function WhatsappSettingsTab({ rpcCall }) {
13535
13916
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
13536
13917
  modelCatalog: EMPTY_MODEL_CATALOG
13537
13918
  });
13538
- const [provision, setProvision] = React23.useState(null);
13539
- const [busy, setBusy] = React23.useState(false);
13540
- const [busyByBot, setBusyByBot] = React23.useState({});
13541
- const [testNoticeByBot, setTestNoticeByBot] = React23.useState({});
13542
- const [removeTarget, setRemoveTarget] = React23.useState(null);
13543
- const [now, setNow] = React23.useState(Date.now());
13544
- const mounted = React23.useRef(true);
13919
+ const [provision, setProvision] = React24.useState(null);
13920
+ const [busy, setBusy] = React24.useState(false);
13921
+ const [busyByBot, setBusyByBot] = React24.useState({});
13922
+ const [testNoticeByBot, setTestNoticeByBot] = React24.useState({});
13923
+ const [removeTarget, setRemoveTarget] = React24.useState(null);
13924
+ const [now, setNow] = React24.useState(Date.now());
13925
+ const mounted = React24.useRef(true);
13545
13926
  const workspaceFence = useWorkspaceSnapshotFence();
13546
- const addButtonRef = React23.useRef(null);
13547
- React23.useEffect(() => {
13927
+ const addButtonRef = React24.useRef(null);
13928
+ React24.useEffect(() => {
13548
13929
  const disposeDingtalk = installDingtalkStyles();
13549
13930
  const disposeWhatsapp = installWhatsappStyles();
13550
13931
  mounted.current = true;
@@ -13554,11 +13935,11 @@ function WhatsappSettingsTab({ rpcCall }) {
13554
13935
  disposeDingtalk();
13555
13936
  };
13556
13937
  }, []);
13557
- const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
13938
+ const invoke = React24.useCallback(async (endpoint, payload = {}, signal) => {
13558
13939
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
13559
13940
  return unwrapRpcResult10(await rpcCall(endpoint, payload, signal));
13560
13941
  }, [rpcCall]);
13561
- const loadStatus = React23.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
13942
+ const loadStatus = React24.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
13562
13943
  const workspaceVersion = workspaceFence.beginStatus();
13563
13944
  if (workspaceVersion === null) return void 0;
13564
13945
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -13589,12 +13970,12 @@ function WhatsappSettingsTab({ rpcCall }) {
13589
13970
  return void 0;
13590
13971
  }
13591
13972
  }, [invoke, workspaceFence]);
13592
- React23.useEffect(() => {
13973
+ React24.useEffect(() => {
13593
13974
  const controller = new AbortController();
13594
13975
  void loadStatus({ signal: controller.signal, restore: true });
13595
13976
  return () => controller.abort();
13596
13977
  }, [loadStatus]);
13597
- React23.useEffect(() => {
13978
+ React24.useEffect(() => {
13598
13979
  if (model.phase !== "ready") return void 0;
13599
13980
  const controller = new AbortController();
13600
13981
  const timer = window.setInterval(
@@ -13606,12 +13987,12 @@ function WhatsappSettingsTab({ rpcCall }) {
13606
13987
  window.clearInterval(timer);
13607
13988
  };
13608
13989
  }, [loadStatus, model.phase]);
13609
- React23.useEffect(() => {
13990
+ React24.useEffect(() => {
13610
13991
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
13611
13992
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
13612
13993
  return () => window.clearInterval(timer);
13613
13994
  }, [provision?.attemptId, provision?.status]);
13614
- const startProvisioning = React23.useCallback(async (replace = false) => {
13995
+ const startProvisioning = React24.useCallback(async (replace = false) => {
13615
13996
  setBusy(true);
13616
13997
  try {
13617
13998
  if (replace && provision?.attemptId) {
@@ -13629,7 +14010,7 @@ function WhatsappSettingsTab({ rpcCall }) {
13629
14010
  if (mounted.current) setBusy(false);
13630
14011
  }
13631
14012
  }, [invoke, provision?.attemptId]);
13632
- const closeProvision = React23.useCallback(async () => {
14013
+ const closeProvision = React24.useCallback(async () => {
13633
14014
  setBusy(true);
13634
14015
  try {
13635
14016
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -13640,7 +14021,7 @@ function WhatsappSettingsTab({ rpcCall }) {
13640
14021
  if (mounted.current) setBusy(false);
13641
14022
  }
13642
14023
  }, [invoke, provision?.attemptId, provision?.status]);
13643
- React23.useEffect(() => {
14024
+ React24.useEffect(() => {
13644
14025
  const attemptId = provision?.attemptId;
13645
14026
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
13646
14027
  const controller = new AbortController();
@@ -13681,7 +14062,7 @@ function WhatsappSettingsTab({ rpcCall }) {
13681
14062
  if (timer) window.clearTimeout(timer);
13682
14063
  };
13683
14064
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
13684
- const botAction = React23.useCallback(async (account, operation, endpoint, payload) => {
14065
+ const botAction = React24.useCallback(async (account, operation, endpoint, payload) => {
13685
14066
  const snapshotVersion = workspaceFence.beginMutation();
13686
14067
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
13687
14068
  if (operation === "reconnect") {
@@ -13815,7 +14196,7 @@ function WhatsappSettingsTab({ rpcCall }) {
13815
14196
  h2(Button16, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
13816
14197
  )
13817
14198
  ) : h2(
13818
- React23.Fragment,
14199
+ React24.Fragment,
13819
14200
  null,
13820
14201
  provision?.status === "pending" ? h2(QrPanel5, {
13821
14202
  provision,
@@ -13846,7 +14227,7 @@ var normalizeSnapshot10 = api4.normalizeSnapshot;
13846
14227
  var presentError11 = api4.presentError;
13847
14228
 
13848
14229
  // plugin-src/client/channels/imessage/index.js
13849
- var React24 = __toESM(require("react"), 1);
14230
+ var React25 = __toESM(require("react"), 1);
13850
14231
 
13851
14232
  // plugin-src/client/channels/imessage/styles.js
13852
14233
  var IMESSAGE_STYLE_ID = "xmanrui-dsh-im-imessage-settings";
@@ -13938,11 +14319,73 @@ var channel4 = createTokenChannelSettings({
13938
14319
  var IMessageSettingsTab = channel4.SettingsTab;
13939
14320
  var IMessageAccountCard = channel4.AccountCard;
13940
14321
 
14322
+ // src/channels/shared/interface-language.mjs
14323
+ var LANGUAGE_TAG = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
14324
+ var HOST_LANGUAGE_SOURCES = Object.freeze(["config", "settings", "mirror"]);
14325
+ function normalizeInterfaceLanguageTag(value) {
14326
+ if (typeof value !== "string") return null;
14327
+ const trimmed = value.trim();
14328
+ return LANGUAGE_TAG.test(trimmed) ? trimmed : null;
14329
+ }
14330
+
14331
+ // plugin-src/client/interface-language.js
14332
+ var HOST_LANGUAGE_RPC_CHANNEL = "/dsh-im-language";
14333
+ var HOST_LANGUAGE_ENDPOINTS = Object.freeze({
14334
+ get: "settings.language.get",
14335
+ mirror: "settings.language.mirror"
14336
+ });
14337
+ var RETRY_DELAYS_MS = Object.freeze([1e3, 4e3, 15e3]);
14338
+ function installInterfaceLanguageMirror(ctx, {
14339
+ rpcCall,
14340
+ setTimeoutFn = (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
14341
+ clearTimeoutFn = (timer) => globalThis.clearTimeout(timer),
14342
+ retryDelaysMs = RETRY_DELAYS_MS
14343
+ } = {}) {
14344
+ if (typeof rpcCall !== "function" || typeof ctx?.locale?.getLocale !== "function") {
14345
+ return () => {
14346
+ };
14347
+ }
14348
+ const delays = retryDelaysMs.length > 0 ? retryDelaysMs : RETRY_DELAYS_MS;
14349
+ const scheduler = createPollScheduler({ setTimeoutFn, clearTimeoutFn });
14350
+ let mirrored = null;
14351
+ let attempt = 0;
14352
+ const retry = (active) => {
14353
+ if (mirrored === active) mirrored = null;
14354
+ scheduler.schedule(report, delays[Math.min(attempt, delays.length - 1)]);
14355
+ attempt += 1;
14356
+ };
14357
+ const report = () => {
14358
+ if (scheduler.disposed) return;
14359
+ const active = normalizeInterfaceLanguageTag(ctx.locale.getLocale()?.active);
14360
+ if (active === null || active === mirrored) return;
14361
+ mirrored = active;
14362
+ Promise.resolve(rpcCall(HOST_LANGUAGE_ENDPOINTS.mirror, { locale: active })).then(
14363
+ (result) => {
14364
+ if (result?.ok === true) {
14365
+ attempt = 0;
14366
+ return;
14367
+ }
14368
+ retry(active);
14369
+ },
14370
+ () => retry(active)
14371
+ );
14372
+ };
14373
+ report();
14374
+ const off = typeof ctx.on === "function" ? ctx.on("locale/change", () => {
14375
+ attempt = 0;
14376
+ report();
14377
+ }) : null;
14378
+ return () => {
14379
+ scheduler.dispose();
14380
+ if (typeof off === "function") off();
14381
+ };
14382
+ }
14383
+
13941
14384
  // plugin-src/client/delivery-settings.js
13942
- var React27 = __toESM(require("react"), 1);
14385
+ var React28 = __toESM(require("react"), 1);
13943
14386
 
13944
14387
  // plugin-src/client/access-policy-settings.js
13945
- var React25 = __toESM(require("react"), 1);
14388
+ var React26 = __toESM(require("react"), 1);
13946
14389
  var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
13947
14390
  var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
13948
14391
  weixin: Object.freeze({
@@ -14055,8 +14498,8 @@ function ScenePolicyEditor({
14055
14498
  unsupported = false,
14056
14499
  onChange
14057
14500
  }) {
14058
- const ownerHelpId = React25.useId();
14059
- const emptyAllowlistHelpId = React25.useId();
14501
+ const ownerHelpId = React26.useId();
14502
+ const emptyAllowlistHelpId = React26.useId();
14060
14503
  const allowlist = policy.mode === "allowlist";
14061
14504
  const collectionKey = allowlist ? "users" : "commandPermissionOverrides";
14062
14505
  const branchKey = allowlist ? "allowlist" : "open";
@@ -14108,7 +14551,7 @@ function ScenePolicyEditor({
14108
14551
  h2("strong", null, "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A"),
14109
14552
  h2("p", null, "\u6B64\u533A\u57DF\u65E0\u9700\u914D\u7F6E\uFF0C\u4FDD\u5B58\u79C1\u804A\u8BBE\u7F6E\u65F6\u4F1A\u4FDD\u7559\u73B0\u6709\u7FA4\u804A\u7B56\u7565\u3002")
14110
14553
  ) : h2(
14111
- React25.Fragment,
14554
+ React26.Fragment,
14112
14555
  null,
14113
14556
  h2(
14114
14557
  "div",
@@ -14249,16 +14692,16 @@ function AccessPolicySettingsPage({ channel: channel5, account, rpcCall, onSaved
14249
14692
  const definition = ACCESS_CHANNEL_DEFINITIONS[channel5];
14250
14693
  const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
14251
14694
  const initialKey = JSON.stringify(initialPolicy);
14252
- const [draft, setDraft] = React25.useState(() => clonePolicy(
14695
+ const [draft, setDraft] = React26.useState(() => clonePolicy(
14253
14696
  initialPolicy ?? DEFAULT_ACCESS_POLICY
14254
14697
  ));
14255
- const [saving, setSaving] = React25.useState(false);
14256
- const [feedback, setFeedback] = React25.useState(null);
14257
- React25.useEffect(() => {
14698
+ const [saving, setSaving] = React26.useState(false);
14699
+ const [feedback, setFeedback] = React26.useState(null);
14700
+ React26.useEffect(() => {
14258
14701
  const next = normalizeAccessPolicy(account?.accessPolicy);
14259
14702
  setDraft(clonePolicy(next ?? DEFAULT_ACCESS_POLICY));
14260
14703
  }, [account?.botId, initialKey]);
14261
- React25.useEffect(() => {
14704
+ React26.useEffect(() => {
14262
14705
  setFeedback(null);
14263
14706
  }, [account?.botId]);
14264
14707
  if (!definition) {
@@ -14349,7 +14792,7 @@ function AccessPolicySettingsPage({ channel: channel5, account, rpcCall, onSaved
14349
14792
  }
14350
14793
 
14351
14794
  // plugin-src/client/channels/feishu/group-settings.js
14352
- var React26 = __toESM(require("react"), 1);
14795
+ var React27 = __toESM(require("react"), 1);
14353
14796
  var GROUP_MESSAGE_PERMISSION_OPERATION2 = FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
14354
14797
  function SettingsButton({ children, kind = "secondary", className = "", ...props }) {
14355
14798
  return h2("button", {
@@ -14410,9 +14853,9 @@ function GroupResponseModeEditor({
14410
14853
  onAuthorize
14411
14854
  }) {
14412
14855
  const current = normalizeGroupResponseMode(value);
14413
- const [saving, setSaving] = React26.useState(false);
14414
- const [authorizing, setAuthorizing] = React26.useState(false);
14415
- const [error, setError] = React26.useState(null);
14856
+ const [saving, setSaving] = React27.useState(false);
14857
+ const [authorizing, setAuthorizing] = React27.useState(false);
14858
+ const [error, setError] = React27.useState(null);
14416
14859
  const change = async (event) => {
14417
14860
  const next = normalizeGroupResponseMode(event.target.value);
14418
14861
  if (next === current || saving || disabled) return;
@@ -14493,8 +14936,8 @@ function GroupResponseModeEditor({
14493
14936
  }
14494
14937
  function GroupTopicReplyEditor({ value = false, disabled = false, onSave }) {
14495
14938
  const current = value === true ? "on" : "off";
14496
- const [saving, setSaving] = React26.useState(false);
14497
- const [error, setError] = React26.useState(null);
14939
+ const [saving, setSaving] = React27.useState(false);
14940
+ const [error, setError] = React27.useState(null);
14498
14941
  const change = async (event) => {
14499
14942
  const next = event.target.value === "on";
14500
14943
  if ((next ? "on" : "off") === current || saving || disabled) return;
@@ -14681,14 +15124,14 @@ function PermissionFlow({ provision, now, busy, botName, onRetry, onCancel, onCl
14681
15124
  );
14682
15125
  }
14683
15126
  function FeishuGroupSettingsPage({ account, rpcCall }) {
14684
- const [settings, setSettings] = React26.useState(() => groupSettingsFrom(account));
14685
- const [refreshing, setRefreshing] = React26.useState(false);
14686
- const [refreshError, setRefreshError] = React26.useState(null);
14687
- const [provision, setProvision] = React26.useState(null);
14688
- const [provisionBusy, setProvisionBusy] = React26.useState(false);
14689
- const [now, setNow] = React26.useState(() => Date.now());
14690
- const mounted = React26.useRef(true);
14691
- React26.useEffect(() => {
15127
+ const [settings, setSettings] = React27.useState(() => groupSettingsFrom(account));
15128
+ const [refreshing, setRefreshing] = React27.useState(false);
15129
+ const [refreshError, setRefreshError] = React27.useState(null);
15130
+ const [provision, setProvision] = React27.useState(null);
15131
+ const [provisionBusy, setProvisionBusy] = React27.useState(false);
15132
+ const [now, setNow] = React27.useState(() => Date.now());
15133
+ const mounted = React27.useRef(true);
15134
+ React27.useEffect(() => {
14692
15135
  setSettings(groupSettingsFrom(account));
14693
15136
  }, [
14694
15137
  account.botId,
@@ -14696,22 +15139,22 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
14696
15139
  account.groupTopicReply,
14697
15140
  account.groupMessagePermissionGranted
14698
15141
  ]);
14699
- React26.useEffect(() => {
15142
+ React27.useEffect(() => {
14700
15143
  mounted.current = true;
14701
15144
  return () => {
14702
15145
  mounted.current = false;
14703
15146
  };
14704
15147
  }, []);
14705
- const invoke = React26.useCallback(async (endpoint, payload = {}, signal) => {
15148
+ const invoke = React27.useCallback(async (endpoint, payload = {}, signal) => {
14706
15149
  if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u7FA4\u804A\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
14707
15150
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
14708
15151
  }, [rpcCall]);
14709
- const applySnapshot = React26.useCallback((value) => {
15152
+ const applySnapshot = React27.useCallback((value) => {
14710
15153
  const result = targetBotFromSnapshot(value, account.botId);
14711
15154
  if (mounted.current) setSettings(groupSettingsFrom(result.bot));
14712
15155
  return result;
14713
15156
  }, [account.botId]);
14714
- const loadSettings = React26.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
15157
+ const loadSettings = React27.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
14715
15158
  setRefreshing(true);
14716
15159
  setRefreshError(null);
14717
15160
  try {
@@ -14740,16 +15183,16 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
14740
15183
  if (!signal?.aborted && mounted.current) setRefreshing(false);
14741
15184
  }
14742
15185
  }, [account.botId, applySnapshot, invoke]);
14743
- React26.useEffect(() => {
15186
+ React27.useEffect(() => {
14744
15187
  const controller = new AbortController();
14745
15188
  void loadSettings({ signal: controller.signal, restoreProvisioning: true });
14746
15189
  return () => controller.abort();
14747
15190
  }, [loadSettings]);
14748
- const saveSetting = React26.useCallback(async (endpoint, payload) => {
15191
+ const saveSetting = React27.useCallback(async (endpoint, payload) => {
14749
15192
  const value = await invoke(endpoint, { botId: account.botId, ...payload });
14750
15193
  applySnapshot(value);
14751
15194
  }, [account.botId, applySnapshot, invoke]);
14752
- const startAuthorization = React26.useCallback(async ({ replace = false } = {}) => {
15195
+ const startAuthorization = React27.useCallback(async ({ replace = false } = {}) => {
14753
15196
  if (provisionBusy) return;
14754
15197
  const previousAttemptId = provision?.attemptId;
14755
15198
  setProvisionBusy(true);
@@ -14790,13 +15233,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
14790
15233
  if (mounted.current) setProvisionBusy(false);
14791
15234
  }
14792
15235
  }, [account.botId, invoke, provision?.attemptId, provisionBusy]);
14793
- const finishAuthorization = React26.useCallback(async (signal) => {
15236
+ const finishAuthorization = React27.useCallback(async (signal) => {
14794
15237
  const bot = await loadSettings({ signal, restoreProvisioning: false });
14795
15238
  if (signal?.aborted || !mounted.current) return;
14796
15239
  if (!bot) throw new Error("\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001");
14797
15240
  setProvision(null);
14798
15241
  }, [loadSettings]);
14799
- React26.useEffect(() => {
15242
+ React27.useEffect(() => {
14800
15243
  if (!provision?.attemptId || !["qr", "connecting"].includes(provision.phase) || provision.expired) return void 0;
14801
15244
  const timerHost = globalThis.window ?? globalThis;
14802
15245
  const controller = new AbortController();
@@ -14840,7 +15283,7 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
14840
15283
  timerHost.clearTimeout(timer);
14841
15284
  };
14842
15285
  }, [account.botId, finishAuthorization, invoke, provision]);
14843
- React26.useEffect(() => {
15286
+ React27.useEffect(() => {
14844
15287
  if (!provision?.attemptId || provision.phase !== "qr" || provision.expired) return void 0;
14845
15288
  const timerHost = globalThis.window ?? globalThis;
14846
15289
  const tick = () => {
@@ -14854,7 +15297,7 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
14854
15297
  const timer = timerHost.setInterval(tick, 1e3);
14855
15298
  return () => timerHost.clearInterval(timer);
14856
15299
  }, [provision?.attemptId, provision?.expired, provision?.expiresAt, provision?.phase]);
14857
- const cancelAuthorization = React26.useCallback(async () => {
15300
+ const cancelAuthorization = React27.useCallback(async () => {
14858
15301
  if (!provision?.attemptId || provisionBusy) {
14859
15302
  setProvision(null);
14860
15303
  return;
@@ -15175,13 +15618,13 @@ function TargetForm({
15175
15618
  }) {
15176
15619
  const editing = mode === "edit";
15177
15620
  const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
15178
- const [targetId, setTargetId] = React27.useState(initialValue?.targetId ?? "");
15179
- const [name2, setName] = React27.useState(initialValue?.name ?? "");
15180
- const [kind, setKind] = React27.useState(initialKind);
15181
- const [route, setRoute] = React27.useState(initialValue?.route ?? {});
15182
- const [error, setError] = React27.useState(null);
15183
- const [testing, setTesting] = React27.useState(false);
15184
- const [testState, setTestState] = React27.useState(null);
15621
+ const [targetId, setTargetId] = React28.useState(initialValue?.targetId ?? "");
15622
+ const [name2, setName] = React28.useState(initialValue?.name ?? "");
15623
+ const [kind, setKind] = React28.useState(initialKind);
15624
+ const [route, setRoute] = React28.useState(initialValue?.route ?? {});
15625
+ const [error, setError] = React28.useState(null);
15626
+ const [testing, setTesting] = React28.useState(false);
15627
+ const [testState, setTestState] = React28.useState(null);
15185
15628
  const currentTarget = () => {
15186
15629
  const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field) => {
15187
15630
  const raw = String(route[field.key] ?? "").trim();
@@ -15398,7 +15841,7 @@ function TargetSuggestionPicker({
15398
15841
  suggestions.map((suggestion, index) => {
15399
15842
  const identity = routeIdentity(definition, suggestion);
15400
15843
  const added = configured.has(identity);
15401
- return React27.createElement("option", {
15844
+ return React28.createElement("option", {
15402
15845
  key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
15403
15846
  value: String(index),
15404
15847
  disabled: added
@@ -15415,11 +15858,11 @@ function TargetSuggestionPicker({
15415
15858
  );
15416
15859
  }
15417
15860
  function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
15418
- const [action, setAction] = React27.useState(null);
15419
- const [testState, setTestState] = React27.useState(null);
15420
- const [syncFeedback, setSyncFeedback] = React27.useState(null);
15421
- const [copyState, setCopyState] = React27.useState(null);
15422
- const [confirmDelete, setConfirmDelete] = React27.useState(false);
15861
+ const [action, setAction] = React28.useState(null);
15862
+ const [testState, setTestState] = React28.useState(null);
15863
+ const [syncFeedback, setSyncFeedback] = React28.useState(null);
15864
+ const [copyState, setCopyState] = React28.useState(null);
15865
+ const [confirmDelete, setConfirmDelete] = React28.useState(false);
15423
15866
  const sessionSync = target.sessionSync ?? { enabled: false, state: "unavailable" };
15424
15867
  const syncDescription = sessionSync.state === "active" ? "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD" : sessionSync.state === "waiting" ? "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD" : sessionSync.state === "unavailable" ? "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807" : "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F";
15425
15868
  const testTarget = async () => {
@@ -15482,7 +15925,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
15482
15925
  h2(
15483
15926
  "div",
15484
15927
  { className: "dim-targetTitle" },
15485
- React27.createElement("strong", null, target.name || target.targetId),
15928
+ React28.createElement("strong", null, target.name || target.targetId),
15486
15929
  h2("span", null, kindLabel(definition, target.kind))
15487
15930
  ),
15488
15931
  h2("code", null, `targetId: ${target.targetId}`)
@@ -15568,26 +16011,26 @@ function DeliveryTargetSettingsPage({
15568
16011
  onBack
15569
16012
  }) {
15570
16013
  const definition = CHANNEL_DEFINITIONS[channel5];
15571
- const [activeTabId, setActiveTabId] = React27.useState(BOT_SETTINGS_TABS[0].id);
15572
- const [phase, setPhase] = React27.useState("loading");
15573
- const [targets, setTargets] = React27.useState([]);
15574
- const [suggestionPhase, setSuggestionPhase] = React27.useState("idle");
15575
- const [suggestions, setSuggestions] = React27.useState([]);
15576
- const [suggestionError, setSuggestionError] = React27.useState(null);
15577
- const [error, setError] = React27.useState(null);
15578
- const [editor, setEditor] = React27.useState(null);
15579
- const [saving, setSaving] = React27.useState(false);
15580
- const [botCopyState, setBotCopyState] = React27.useState(null);
15581
- const [accessPolicy, setAccessPolicy] = React27.useState(account.accessPolicy);
15582
- const mounted = React27.useRef(true);
15583
- React27.useEffect(() => {
16014
+ const [activeTabId, setActiveTabId] = React28.useState(BOT_SETTINGS_TABS[0].id);
16015
+ const [phase, setPhase] = React28.useState("loading");
16016
+ const [targets, setTargets] = React28.useState([]);
16017
+ const [suggestionPhase, setSuggestionPhase] = React28.useState("idle");
16018
+ const [suggestions, setSuggestions] = React28.useState([]);
16019
+ const [suggestionError, setSuggestionError] = React28.useState(null);
16020
+ const [error, setError] = React28.useState(null);
16021
+ const [editor, setEditor] = React28.useState(null);
16022
+ const [saving, setSaving] = React28.useState(false);
16023
+ const [botCopyState, setBotCopyState] = React28.useState(null);
16024
+ const [accessPolicy, setAccessPolicy] = React28.useState(account.accessPolicy);
16025
+ const mounted = React28.useRef(true);
16026
+ React28.useEffect(() => {
15584
16027
  setAccessPolicy(account.accessPolicy);
15585
16028
  }, [account.botId, account.accessPolicy]);
15586
- const invoke = React27.useCallback(async (endpoint, payload = {}, signal) => {
16029
+ const invoke = React28.useCallback(async (endpoint, payload = {}, signal) => {
15587
16030
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
15588
16031
  return unwrapRpcResult13(await rpcCall(endpoint, payload, signal));
15589
16032
  }, [rpcCall]);
15590
- const loadTargets = React27.useCallback(async ({ signal, silent = false } = {}) => {
16033
+ const loadTargets = React28.useCallback(async ({ signal, silent = false } = {}) => {
15591
16034
  if (!silent) setPhase("loading");
15592
16035
  setError(null);
15593
16036
  try {
@@ -15601,7 +16044,7 @@ function DeliveryTargetSettingsPage({
15601
16044
  setPhase("error");
15602
16045
  }
15603
16046
  }, [account.botId, invoke]);
15604
- const loadSuggestions = React27.useCallback(async () => {
16047
+ const loadSuggestions = React28.useCallback(async () => {
15605
16048
  setSuggestionPhase("loading");
15606
16049
  setSuggestionError(null);
15607
16050
  try {
@@ -15615,7 +16058,7 @@ function DeliveryTargetSettingsPage({
15615
16058
  setSuggestionPhase("error");
15616
16059
  }
15617
16060
  }, [account.botId, definition, invoke]);
15618
- React27.useEffect(() => {
16061
+ React28.useEffect(() => {
15619
16062
  mounted.current = true;
15620
16063
  const controller = new AbortController();
15621
16064
  void loadTargets({ signal: controller.signal });
@@ -15737,7 +16180,7 @@ function DeliveryTargetSettingsPage({
15737
16180
  account,
15738
16181
  rpcCall: accessRpcCall
15739
16182
  }) : h2(
15740
- React27.Fragment,
16183
+ React28.Fragment,
15741
16184
  null,
15742
16185
  h2(
15743
16186
  "section",
@@ -15848,7 +16291,7 @@ function DeliveryTargetSettingsPage({
15848
16291
  }
15849
16292
 
15850
16293
  // plugin-src/client/global-settings.js
15851
- var React28 = __toESM(require("react"), 1);
16294
+ var React29 = __toESM(require("react"), 1);
15852
16295
 
15853
16296
  // src/channels/shared/inbound-ttl.mjs
15854
16297
  var DEFAULT_INBOUND_TTL_HOURS = 168;
@@ -15910,29 +16353,29 @@ function GlobalButton({ children, kind = "secondary", className = "", ...props }
15910
16353
  }, children);
15911
16354
  }
15912
16355
  function GlobalSettingsPanel({ rpcCall }) {
15913
- const [phase, setPhase] = React28.useState("loading");
15914
- const [loadError, setLoadError] = React28.useState(null);
15915
- const [ttlInput, setTtlInput] = React28.useState("");
15916
- const [savedTtl, setSavedTtl] = React28.useState(null);
15917
- const [ttlError, setTtlError] = React28.useState(false);
15918
- const [saveError, setSaveError] = React28.useState(null);
15919
- const [saveSucceeded, setSaveSucceeded] = React28.useState(false);
15920
- const [isSaving, setIsSaving] = React28.useState(false);
15921
- const [sweepConfirming, setSweepConfirming] = React28.useState(false);
15922
- const [sweeping, setSweeping] = React28.useState(false);
15923
- const ttlErrorId = React28.useId();
15924
- const ttlHintsId = React28.useId();
15925
- const sweepTriggerId = React28.useId();
15926
- const sweepConfirmId = React28.useId();
15927
- const sweepConfirmTextId = React28.useId();
15928
- const sweepConfirmButtonId = React28.useId();
15929
- const mounted = React28.useRef(true);
15930
- const saving = React28.useRef(false);
15931
- const invoke = React28.useCallback(async (endpoint, payload = {}, signal) => {
16356
+ const [phase, setPhase] = React29.useState("loading");
16357
+ const [loadError, setLoadError] = React29.useState(null);
16358
+ const [ttlInput, setTtlInput] = React29.useState("");
16359
+ const [savedTtl, setSavedTtl] = React29.useState(null);
16360
+ const [ttlError, setTtlError] = React29.useState(false);
16361
+ const [saveError, setSaveError] = React29.useState(null);
16362
+ const [saveSucceeded, setSaveSucceeded] = React29.useState(false);
16363
+ const [isSaving, setIsSaving] = React29.useState(false);
16364
+ const [sweepConfirming, setSweepConfirming] = React29.useState(false);
16365
+ const [sweeping, setSweeping] = React29.useState(false);
16366
+ const ttlErrorId = React29.useId();
16367
+ const ttlHintsId = React29.useId();
16368
+ const sweepTriggerId = React29.useId();
16369
+ const sweepConfirmId = React29.useId();
16370
+ const sweepConfirmTextId = React29.useId();
16371
+ const sweepConfirmButtonId = React29.useId();
16372
+ const mounted = React29.useRef(true);
16373
+ const saving = React29.useRef(false);
16374
+ const invoke = React29.useCallback(async (endpoint, payload = {}, signal) => {
15932
16375
  if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
15933
16376
  return unwrapRpcResult14(await rpcCall(endpoint, payload, signal));
15934
16377
  }, [rpcCall]);
15935
- const loadSettings = React28.useCallback(async ({ signal } = {}) => {
16378
+ const loadSettings = React29.useCallback(async ({ signal } = {}) => {
15936
16379
  setPhase("loading");
15937
16380
  setLoadError(null);
15938
16381
  try {
@@ -15956,7 +16399,7 @@ function GlobalSettingsPanel({ rpcCall }) {
15956
16399
  setPhase("error");
15957
16400
  }
15958
16401
  }, [invoke]);
15959
- React28.useEffect(() => {
16402
+ React29.useEffect(() => {
15960
16403
  mounted.current = true;
15961
16404
  const controller = new AbortController();
15962
16405
  void loadSettings({ signal: controller.signal });
@@ -15965,7 +16408,7 @@ function GlobalSettingsPanel({ rpcCall }) {
15965
16408
  controller.abort();
15966
16409
  };
15967
16410
  }, [loadSettings]);
15968
- React28.useEffect(() => {
16411
+ React29.useEffect(() => {
15969
16412
  if (!sweepConfirming) return;
15970
16413
  globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
15971
16414
  }, [sweepConfirmButtonId, sweepConfirming]);
@@ -17237,7 +17680,7 @@ function installSessionChannelLogos(document2 = globalThis.document) {
17237
17680
  }
17238
17681
 
17239
17682
  // plugin-src/client/update-panel.js
17240
- var React29 = __toESM(require("react"), 1);
17683
+ var React30 = __toESM(require("react"), 1);
17241
17684
  var import_react_dom4 = require("react-dom");
17242
17685
  var import_valid = __toESM(require_valid(), 1);
17243
17686
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -17327,11 +17770,11 @@ function manualUpdateCommand(snapshot) {
17327
17770
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
17328
17771
  }
17329
17772
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
17330
- const [copyState, setCopyState] = React29.useState("idle");
17331
- const commandRef = React29.useRef(null);
17332
- const mounted = React29.useRef(false);
17333
- const copying = React29.useRef(false);
17334
- React29.useEffect(() => {
17773
+ const [copyState, setCopyState] = React30.useState("idle");
17774
+ const commandRef = React30.useRef(null);
17775
+ const mounted = React30.useRef(false);
17776
+ const copying = React30.useRef(false);
17777
+ React30.useEffect(() => {
17335
17778
  mounted.current = true;
17336
17779
  return () => {
17337
17780
  mounted.current = false;
@@ -17363,7 +17806,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
17363
17806
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
17364
17807
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
17365
17808
  command ? h2(
17366
- React29.Fragment,
17809
+ React30.Fragment,
17367
17810
  null,
17368
17811
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
17369
17812
  h2(
@@ -17399,7 +17842,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
17399
17842
  "aria-hidden": "true",
17400
17843
  focusable: "false"
17401
17844
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
17402
- React29.Fragment,
17845
+ React30.Fragment,
17403
17846
  null,
17404
17847
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
17405
17848
  h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@@ -17424,10 +17867,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
17424
17867
  );
17425
17868
  }
17426
17869
  function UpdateDialog({ children, onClose }) {
17427
- const dialogRef = React29.useRef(null);
17428
- const titleId = React29.useId();
17429
- const descriptionId = React29.useId();
17430
- React29.useEffect(() => {
17870
+ const dialogRef = React30.useRef(null);
17871
+ const titleId = React30.useId();
17872
+ const descriptionId = React30.useId();
17873
+ React30.useEffect(() => {
17431
17874
  const previous = globalThis.document?.activeElement;
17432
17875
  dialogRef.current?.focus?.();
17433
17876
  return () => {
@@ -17485,19 +17928,19 @@ function UpdateDialog({ children, onClose }) {
17485
17928
  return typeof document !== "undefined" && document.body ? (0, import_react_dom4.createPortal)(content, document.body) : content;
17486
17929
  }
17487
17930
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17488
- const [snapshot, setSnapshot] = React29.useState(null);
17489
- const [action, setAction] = React29.useState("status");
17490
- const [error, setError] = React29.useState(null);
17491
- const [open, setOpen] = React29.useState(false);
17492
- const [uncertainInstall, setUncertainInstall] = React29.useState(false);
17493
- const mounted = React29.useRef(false);
17494
- const busy = React29.useRef(false);
17495
- const readController = React29.useRef(null);
17496
- const pollReadController = React29.useRef(null);
17497
- const installRequest = React29.useRef(null);
17498
- const onStatusRef = React29.useRef(onStatus);
17931
+ const [snapshot, setSnapshot] = React30.useState(null);
17932
+ const [action, setAction] = React30.useState("status");
17933
+ const [error, setError] = React30.useState(null);
17934
+ const [open, setOpen] = React30.useState(false);
17935
+ const [uncertainInstall, setUncertainInstall] = React30.useState(false);
17936
+ const mounted = React30.useRef(false);
17937
+ const busy = React30.useRef(false);
17938
+ const readController = React30.useRef(null);
17939
+ const pollReadController = React30.useRef(null);
17940
+ const installRequest = React30.useRef(null);
17941
+ const onStatusRef = React30.useRef(onStatus);
17499
17942
  onStatusRef.current = onStatus;
17500
- const accept = React29.useCallback((next) => {
17943
+ const accept = React30.useCallback((next) => {
17501
17944
  setSnapshot(next);
17502
17945
  onStatusRef.current?.(next);
17503
17946
  }, []);
@@ -17506,7 +17949,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17506
17949
  setUncertainInstall(false);
17507
17950
  accept(next);
17508
17951
  };
17509
- const invoke = React29.useCallback(async (endpoint, payload = {}, signal) => {
17952
+ const invoke = React30.useCallback(async (endpoint, payload = {}, signal) => {
17510
17953
  if (typeof rpcCall !== "function") {
17511
17954
  const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
17512
17955
  unavailable.code = "update-unavailable";
@@ -17514,7 +17957,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17514
17957
  }
17515
17958
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
17516
17959
  }, [rpcCall]);
17517
- React29.useEffect(() => {
17960
+ React30.useEffect(() => {
17518
17961
  mounted.current = true;
17519
17962
  busy.current = true;
17520
17963
  const controller = new AbortController();
@@ -17537,7 +17980,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17537
17980
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
17538
17981
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
17539
17982
  const shouldPoll = activeJob || uncertainInstall;
17540
- React29.useEffect(() => {
17983
+ React30.useEffect(() => {
17541
17984
  if (!shouldPoll) return void 0;
17542
17985
  let controller;
17543
17986
  const scheduler = createPollScheduler({
@@ -17635,7 +18078,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17635
18078
  const manualCommand = manualUpdateCommand(snapshot);
17636
18079
  const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
17637
18080
  return h2(
17638
- React29.Fragment,
18081
+ React30.Fragment,
17639
18082
  null,
17640
18083
  h2("button", {
17641
18084
  type: "button",
@@ -17660,13 +18103,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
17660
18103
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
17661
18104
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
17662
18105
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
17663
- React29.Fragment,
18106
+ React30.Fragment,
17664
18107
  null,
17665
18108
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
17666
18109
  h2("dd", null, `v${snapshot.installedVersion}`)
17667
18110
  ) : null,
17668
18111
  targetVersion ? h2(
17669
- React29.Fragment,
18112
+ React30.Fragment,
17670
18113
  null,
17671
18114
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
17672
18115
  h2("dd", null, `v${targetVersion}`)
@@ -17887,23 +18330,23 @@ function IMSettingsTab({
17887
18330
  browserLocation = globalThis.location,
17888
18331
  navigateToRecoveryUrl = replacePageLocation
17889
18332
  }) {
17890
- const [selected, setSelected] = React30.useState("weixin");
17891
- const [loopbackRecovery, setLoopbackRecovery] = React30.useState(null);
17892
- const [runningVersion, setRunningVersion] = React30.useState(IM_PLUGIN_VERSION);
17893
- const [deliverySettings, setDeliverySettings] = React30.useState(null);
17894
- const githubTooltipId = React30.useId();
17895
- const generalSettingsTooltipId = React30.useId();
18333
+ const [selected, setSelected] = React31.useState("weixin");
18334
+ const [loopbackRecovery, setLoopbackRecovery] = React31.useState(null);
18335
+ const [runningVersion, setRunningVersion] = React31.useState(IM_PLUGIN_VERSION);
18336
+ const [deliverySettings, setDeliverySettings] = React31.useState(null);
18337
+ const githubTooltipId = React31.useId();
18338
+ const generalSettingsTooltipId = React31.useId();
17896
18339
  const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
17897
18340
  const active = CHANNELS.find((channel5) => channel5.id === selected) ?? CHANNELS[0];
17898
18341
  const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
17899
18342
  const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
17900
- const reportLoopbackRecovery = React30.useCallback((recovery) => {
18343
+ const reportLoopbackRecovery = React31.useCallback((recovery) => {
17901
18344
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
17902
18345
  }, []);
17903
- const reportUpdateStatus = React30.useCallback((snapshot) => {
18346
+ const reportUpdateStatus = React31.useCallback((snapshot) => {
17904
18347
  setRunningVersion(snapshot.runningVersion);
17905
18348
  }, []);
17906
- const rpcCalls = React30.useMemo(() => createLoopbackAwareRpcCalls({
18349
+ const rpcCalls = React31.useMemo(() => createLoopbackAwareRpcCalls({
17907
18350
  dingtalkRpcCall,
17908
18351
  discordRpcCall,
17909
18352
  feishuRpcCall,
@@ -17941,7 +18384,7 @@ function IMSettingsTab({
17941
18384
  weixinRpcCall,
17942
18385
  whatsappRpcCall
17943
18386
  ]);
17944
- const botSettingsContext = React30.useMemo(() => Object.freeze({
18387
+ const botSettingsContext = React31.useMemo(() => Object.freeze({
17945
18388
  openBotSettings: setDeliverySettings
17946
18389
  }), []);
17947
18390
  return h2(
@@ -18084,6 +18527,12 @@ function apply(ctx) {
18084
18527
  );
18085
18528
  const t = ctx.locale.bind(IM_LOCALE_NAMESPACE);
18086
18529
  setImTranslator(t);
18530
+ ctx.effect(
18531
+ () => installInterfaceLanguageMirror(ctx, {
18532
+ rpcCall: (endpoint, payload, signal) => callManagementRpc(ctx.connection, HOST_LANGUAGE_RPC_CHANNEL, endpoint, payload, signal)
18533
+ }),
18534
+ "im-settings: mirror the DSH interface language"
18535
+ );
18087
18536
  ctx.effect(() => installSessionChannelLogos(), "im-settings: Session channel logos");
18088
18537
  ctx.effect(() => {
18089
18538
  const disposers = [