@xmanrui/dsh-im 4.2.1 → 4.4.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 (74) hide show
  1. package/README.en.md +4 -2
  2. package/README.md +4 -2
  3. package/lib/client.js +1267 -787
  4. package/lib/index.js +237 -235
  5. package/package.json +1 -1
  6. package/plugin-src/client/access-policy-settings.js +351 -0
  7. package/plugin-src/client/channel-card-meta.js +2 -1
  8. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  9. package/plugin-src/client/channels/dingtalk/index.js +1 -0
  10. package/plugin-src/client/channels/feishu/api.js +5 -0
  11. package/plugin-src/client/channels/feishu/index.js +1 -0
  12. package/plugin-src/client/channels/qq/api.js +5 -0
  13. package/plugin-src/client/channels/qq/index.js +1 -0
  14. package/plugin-src/client/channels/shared/token-api.js +5 -0
  15. package/plugin-src/client/channels/shared/token-channel.js +1 -0
  16. package/plugin-src/client/channels/telegram/api.js +2 -17
  17. package/plugin-src/client/channels/telegram/index.js +0 -112
  18. package/plugin-src/client/channels/telegram/styles.js +0 -28
  19. package/plugin-src/client/channels/wecom/api.js +5 -0
  20. package/plugin-src/client/channels/wecom/index.js +1 -0
  21. package/plugin-src/client/channels/weixin/api.js +5 -0
  22. package/plugin-src/client/channels/weixin/index.js +1 -0
  23. package/plugin-src/client/channels/whatsapp/api.js +4 -10
  24. package/plugin-src/client/channels/whatsapp/index.js +1 -125
  25. package/plugin-src/client/channels/whatsapp/styles.js +0 -25
  26. package/plugin-src/client/context-enhancement.js +207 -102
  27. package/plugin-src/client/delivery-settings.js +61 -9
  28. package/plugin-src/client/i18n.js +71 -3
  29. package/plugin-src/client/index.js +1 -0
  30. package/plugin-src/client/styles.js +59 -8
  31. package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
  32. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  33. package/plugin-src/host/channels/feishu/production.mjs +39 -1
  34. package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
  35. package/plugin-src/host/channels/qq/production.mjs +12 -1
  36. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  37. package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
  38. package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
  39. package/plugin-src/host/channels/shared/production.mjs +15 -1
  40. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  41. package/plugin-src/host/channels/slack/production.mjs +12 -1
  42. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  43. package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
  44. package/plugin-src/host/channels/wecom/production.mjs +12 -1
  45. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  46. package/plugin-src/host/channels/weixin/production.mjs +12 -1
  47. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  48. package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
  49. package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
  50. package/scripts/verify-package.mjs +4 -3
  51. package/src/channels/dingtalk/dingtalk-bridge.mjs +61 -9
  52. package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
  53. package/src/channels/discord/discord-runtime.mjs +29 -1
  54. package/src/channels/feishu/bridge.mjs +286 -120
  55. package/src/channels/feishu/feishu-runtime.mjs +4 -0
  56. package/src/channels/feishu/message-utils.mjs +5 -0
  57. package/src/channels/qq/qq-bridge.mjs +59 -17
  58. package/src/channels/qq/qq-runtime.mjs +25 -3
  59. package/src/channels/shared/access-policy.mjs +210 -0
  60. package/src/channels/shared/bot-workspace-store.mjs +176 -33
  61. package/src/channels/shared/command-permission.mjs +32 -0
  62. package/src/channels/shared/compact-command.mjs +5 -2
  63. package/src/channels/shared/context-enhancement.mjs +73 -20
  64. package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
  65. package/src/channels/shared/inbound-access.mjs +43 -0
  66. package/src/channels/shared/text-harness-bridge.mjs +47 -4
  67. package/src/channels/shared/workspace-command.mjs +10 -1
  68. package/src/channels/slack/slack-runtime.mjs +4 -0
  69. package/src/channels/telegram/telegram-runtime.mjs +5 -13
  70. package/src/channels/wecom/wecom-bridge.mjs +51 -4
  71. package/src/channels/wecom/wecom-runtime.mjs +4 -0
  72. package/src/channels/weixin/weixin-bridge.mjs +59 -16
  73. package/src/channels/weixin/weixin-runtime.mjs +4 -0
  74. package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -20
package/lib/client.js CHANGED
@@ -571,7 +571,7 @@ var React23 = __toESM(require("react"), 1);
571
571
  // package.json
572
572
  var package_default = {
573
573
  name: "@xmanrui/dsh-im",
574
- version: "4.2.1",
574
+ version: "4.4.0",
575
575
  description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
576
576
  keywords: [
577
577
  "deepseek-harness",
@@ -841,6 +841,62 @@ var EN = Object.freeze({
841
841
  "IM\u673A\u5668\u4EBA": "IM bots",
842
842
  "IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings",
843
843
  "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E": "More bot settings",
844
+ "\u673A\u5668\u4EBA\u8BBE\u7F6E": "Bot settings",
845
+ "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E": "Bot settings tabs",
846
+ "\u6295\u9012\u8BBE\u7F6E": "Delivery settings",
847
+ "\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
848
+ "\u67E5\u770B\u8BBF\u95EE\u6743\u9650\u8BF4\u660E": "View access permission details",
849
+ "\u5141\u8BB8\u6240\u6709\u7528\u6237": "Allow all users",
850
+ "\u4EC5\u767D\u540D\u5355\u7528\u6237": "Allowlisted users only",
851
+ "\u9ED8\u8BA4\u547D\u4EE4\u6743\u9650": "Default command permission",
852
+ "\u547D\u4EE4\u6743\u9650": "Command permission",
853
+ "\u53EF\u4EE5\u6267\u884C\u547D\u4EE4": "Can run commands",
854
+ "\u4E0D\u53EF\u4EE5\u6267\u884C\u547D\u4EE4": "Cannot run commands",
855
+ "\u767D\u540D\u5355\u7528\u6237": "Allowlisted users",
856
+ "\u67E5\u770B\u767D\u540D\u5355\u8BF4\u660E": "View allowlist details",
857
+ "\u547D\u4EE4\u6743\u9650\u4F8B\u5916": "Command permission exceptions",
858
+ "\u5F53\u524D\u6CA1\u6709\u767D\u540D\u5355\u7528\u6237\uFF0C\u4FDD\u5B58\u540E\u666E\u901A\u7528\u6237\u5C06\u65E0\u6CD5\u4F7F\u7528\u673A\u5668\u4EBA\u3002": "There are currently no allowlisted users. After saving, regular users will not be able to use the bot.",
859
+ "\u65B0\u589E\u7528\u6237": "Add user",
860
+ "\u5C1A\u672A\u6DFB\u52A0\u7528\u6237": "No users added",
861
+ "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A": "Group chat is not supported by this channel",
862
+ "\u539F\u6240\u6709\u8005\u6216\u626B\u7801\u63A5\u5165\u8005\u59CB\u7EC8\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF1B\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u7EA6\u675F\u5176\u4ED6\u7528\u6237\u3002": "The original owner or QR-code operator can always access the bot and run commands; the settings below apply only to other users.",
863
+ "\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": "No setup is needed here. Saving direct-message settings keeps the existing group policy.",
864
+ "\u8BBF\u95EE\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002": "Access settings saved.",
865
+ "\u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Access settings are currently unavailable.",
866
+ "\u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not save access settings. Try again later.",
867
+ "\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5DF2\u4FDD\u5B58\u7684\u8BBF\u95EE\u7B56\u7565\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002": "The service did not return the saved access policy. Refresh and try again.",
868
+ "\u8BBF\u95EE\u7B56\u7565\u5C1A\u672A\u5C31\u7EEA\uFF0C\u8BF7\u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868\u5237\u65B0\u540E\u91CD\u8BD5\u3002": "The access policy is not ready. Return to the bot list, refresh, and try again.",
869
+ "\u5F53\u524D\u6E20\u9053\u6682\u4E0D\u652F\u6301\u8BBF\u95EE\u8BBE\u7F6E\u3002": "Access settings are not supported by this channel yet.",
870
+ "\u7528\u6237\u6807\u8BC6\u65E0\u6548\u3002": "The user ID is invalid.",
871
+ "\u7528\u6237\u6807\u8BC6\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002": "The user ID must be a string.",
872
+ "\u7528\u6237\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A\u3001\u5305\u542B\u63A7\u5236\u5B57\u7B26\u6216\u8D85\u8FC7 256 \u4E2A\u5B57\u7B26\u3002": "The user ID cannot be empty, contain control characters, or exceed 256 characters.",
873
+ "\u7528\u6237\u6761\u76EE\u5FC5\u987B\u5305\u542B\u7528\u6237\u6807\u8BC6\u548C\u547D\u4EE4\u6743\u9650\u3002": "Each user entry must include a user ID and command permission.",
874
+ "\u547D\u4EE4\u6743\u9650\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002": "Command permission must be a boolean.",
875
+ "\u8BBF\u95EE\u6A21\u5F0F\u53EA\u80FD\u662F open \u6216 allowlist\u3002": "Access mode must be open or allowlist.",
876
+ "\u5F00\u653E\u6A21\u5F0F\u8BBE\u7F6E\u5FC5\u987B\u5B8C\u6574\u3002": "Open-mode settings must be complete.",
877
+ "\u5F00\u653E\u6A21\u5F0F\u9ED8\u8BA4\u547D\u4EE4\u6743\u9650\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002": "The open-mode default command permission must be a boolean.",
878
+ "\u5F00\u653E\u6A21\u5F0F\u547D\u4EE4\u6743\u9650\u8986\u76D6\u7528\u6237\u5FC5\u987B\u662F\u6570\u7EC4\u3002": "Open-mode command permission overrides must be an array.",
879
+ "\u5F00\u653E\u6A21\u5F0F\u547D\u4EE4\u6743\u9650\u8986\u76D6\u7528\u6237\u4E0D\u80FD\u5305\u542B\u91CD\u590D\u7684\u7528\u6237\u6807\u8BC6\u3002": "Open-mode command permission overrides cannot contain duplicate user IDs.",
880
+ "\u767D\u540D\u5355\u6A21\u5F0F\u8BBE\u7F6E\u5FC5\u987B\u5B8C\u6574\u3002": "Allowlist-mode settings must be complete.",
881
+ "\u767D\u540D\u5355\u6A21\u5F0F\u7528\u6237\u5FC5\u987B\u662F\u6570\u7EC4\u3002": "Allowlist-mode users must be an array.",
882
+ "\u767D\u540D\u5355\u6A21\u5F0F\u7528\u6237\u4E0D\u80FD\u5305\u542B\u91CD\u590D\u7684\u7528\u6237\u6807\u8BC6\u3002": "Allowlist-mode users cannot contain duplicate user IDs.",
883
+ "\u8BBF\u95EE\u573A\u666F\u8BBE\u7F6E\u5FC5\u987B\u540C\u65F6\u5305\u542B\u6A21\u5F0F\u3001\u5F00\u653E\u6A21\u5F0F\u8BBE\u7F6E\u548C\u767D\u540D\u5355\u6A21\u5F0F\u8BBE\u7F6E\u3002": "Each access context must include its mode, open-mode settings, and allowlist-mode settings.",
884
+ "\u8BF7\u540C\u65F6\u63D0\u4EA4\u5B8C\u6574\u7684\u79C1\u804A\u548C\u7FA4\u804A\u8BBF\u95EE\u8BBE\u7F6E\u3002": "Submit complete direct-message and group access settings together.",
885
+ "\u586B\u5199\u5FAE\u4FE1\u7528\u6237 ID": "Enter a WeChat user ID",
886
+ "\u98DE\u4E66 Open ID": "Feishu Open ID",
887
+ "\u7FA4\u6210\u5458 Open ID": "Group member Open ID",
888
+ "\u9489\u9489\u7528\u6237 ID": "DingTalk user ID",
889
+ "\u586B\u5199 senderStaffId \u6216 senderId": "Enter senderStaffId or senderId",
890
+ "\u7FA4\u6210\u5458\u7528\u6237 ID": "Group member user ID",
891
+ "\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID": "WeCom user ID",
892
+ "\u586B\u5199 userid": "Enter userid",
893
+ "\u586B\u5199 member_openid": "Enter member_openid",
894
+ "QQ User Open ID": "QQ User Open ID",
895
+ "\u7FA4\u6210\u5458 User ID": "Group member User ID",
896
+ "\u586B\u5199\u6570\u5B57 User ID": "Enter a numeric user ID",
897
+ "WhatsApp \u7535\u8BDD\u53F7\u7801\u6216 JID": "WhatsApp phone number or JID",
898
+ "\u7FA4\u6210\u5458\u7535\u8BDD\u53F7\u7801\u6216 JID": "Group member phone number or JID",
899
+ "8613800000000 \u6216\u5B8C\u6574 JID": "8613800000000 or a full JID",
844
900
  "IM \u6E20\u9053": "IM channels",
845
901
  "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA": "Connecting DeepSeek Harness",
846
902
  "\u5F53\u524D\u7248\u672C": "Current version",
@@ -1050,31 +1106,44 @@ var EN = Object.freeze({
1050
1106
  "\u7FA4\u804A\u548C\u79C1\u804A": "Groups and direct",
1051
1107
  "\u5173\u95ED\u5F39\u7A97": "Close dialog",
1052
1108
  "\u67E5\u770B\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BF4\u660E": "View context enhancement details",
1109
+ "\u4E0A\u4E0B\u6587\u589E\u5F3A\u8303\u56F4": "Context enhancement scope",
1053
1110
  "\u542F\u7528\u8303\u56F4": "Enable in",
1054
- "\u7FA4\u804A\u4E2D\u542F\u7528": "Enable in group chats",
1055
- "\u79C1\u804A\u4E2D\u542F\u7528": "Enable in direct chats",
1111
+ "\u542F\u7528": "Enabled",
1056
1112
  "\uFF08\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A\uFF09": "(This channel does not support group chats)",
1057
1113
  "\u6765\u6E90\u5B57\u6BB5": "Source fields",
1058
1114
  "\u67E5\u770B\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E": "View source field details",
1115
+ "\u67E5\u770B\u7FA4\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E": "View group source field details",
1116
+ "\u67E5\u770B\u79C1\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E": "View direct source field details",
1059
1117
  "\u6E20\u9053": "Channel",
1060
1118
  "\u4F1A\u8BDD\u7C7B\u578B": "Conversation type",
1061
1119
  "\u53D1\u9001\u8005\u6807\u8BC6": "Sender ID",
1062
1120
  "\u53D1\u9001\u8005\u6635\u79F0": "Sender name",
1063
1121
  "\u67E5\u770B\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E": "View sender name field details",
1122
+ "\u67E5\u770B\u7FA4\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E": "View group sender name field details",
1123
+ "\u67E5\u770B\u79C1\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E": "View direct sender name field details",
1064
1124
  "\u8BE5\u5B57\u6BB5\u4E0D\u662F\u6BCF\u4E2A\u6E20\u9053\u90FD\u80FD\u63D0\u4F9B\u3002\u5F53\u524D\u6D88\u606F\u6CA1\u6709\u53D1\u9001\u8005\u6635\u79F0\u65F6\uFF0C\u5373\u4F7F\u5DF2\u9009\u62E9\u8BE5\u5B57\u6BB5\uFF0C<dsh_im_source> \u4E2D\u4E5F\u4F1A\u7701\u7565 senderName\u3002": "This field is not available on every channel. If the current message has no sender name, <dsh_im_source> omits senderName even when the field is selected.",
1065
1125
  "\u673A\u5668\u4EBA\u6807\u8BC6": "Bot ID",
1066
1126
  "\u589E\u5F3A\u63D0\u793A\u8BCD": "Guidance",
1067
1127
  "\u67E5\u770B\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E": "View guidance instructions",
1128
+ "\u67E5\u770B\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E": "View group guidance instructions",
1129
+ "\u67E5\u770B\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E": "View direct guidance instructions",
1068
1130
  "\u4F7F\u7528\u8BF4\u660E": "How to use",
1069
1131
  "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528 <dsh_im_source> \u4E2D\u5DF2\u9009\u62E9\u7684\u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002": "Tell the model how to use the selected fields in <dsh_im_source>. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.",
1070
1132
  "\u751F\u6548\u89C4\u5219": "Behavior",
1071
1133
  "\u9690\u79C1\u63D0\u793A": "Privacy",
1072
1134
  "\u4F7F\u7528\u793A\u4F8B": "Example",
1073
1135
  "\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\nconversationType\u662F\u7FA4\u804A\u65F6\u56DE\u590D\u4E25\u8083\u4E00\u70B9\uFF0CconversationType\u662F\u79C1\u804A\u65F6\u56DE\u590D\u4E00\u5B9A\u8981\u5E7D\u9ED8\u641E\u7B11\uFF0C\u50CF\u5468\u661F\u9A70\u7684\u7535\u5F71\u4E00\u6837\u641E\u7B11": "Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nWhen conversationType is group, reply more seriously. When conversationType is direct, make the reply humorous and funny.",
1136
+ "\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\n\u5F53\u524D\u6D88\u606F\u6765\u81EA\u7FA4\u804A\uFF0C\u8BF7\u4F7F\u7528\u4E25\u8083\u3001\u514B\u5236\u3001\u7B80\u6D01\u7684\u8868\u8FBE\u65B9\u5F0F\u3002": "Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a group chat. Use a serious, restrained, and concise style.",
1137
+ "\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002\n\u5F53\u524D\u6D88\u606F\u6765\u81EA\u79C1\u804A\uFF0C\u53EF\u4EE5\u4F7F\u7528\u66F4\u8F7B\u677E\u3001\u5E7D\u9ED8\u3001\u8BE6\u7EC6\u7684\u8868\u8FBE\u65B9\u5F0F\u3002": "Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a direct chat. You may use a lighter, more humorous, and detailed style.",
1074
1138
  "\u586B\u5165\u793A\u4F8B": "Use example",
1075
1139
  "\u6E05\u7A7A": "Clear",
1076
1140
  "\u9009\u62E9\u5728\u54EA\u4E9B\u4F1A\u8BDD\u4E2D\u542F\u7528\u3001\u63D0\u4F9B\u54EA\u4E9B\u6765\u6E90\u5B57\u6BB5\uFF0C\u4EE5\u53CA\u5982\u4F55\u4F7F\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u4EC5\u4F7F\u7528\u5DF2\u6709\u6D88\u606F\u5143\u6570\u636E\uFF0C\u4E0D\u67E5\u8BE2\u5E73\u53F0 API\u3002": "Choose which conversations to enhance, which source fields to include, and how to use them. Only existing message metadata is used; no platform APIs are queried.",
1077
1141
  "\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u52FE\u9009\u4E14\u5F53\u524D\u6D88\u606F\u4E2D\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002": "Reference these values by field name (such as senderId or conversationType) in the guidance. Only selected fields available in the current message are sent; no extra lookups are made.",
1142
+ "\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u5F53\u524D\u4F1A\u8BDD\u4E2D\u52FE\u9009\u4E14\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002": "Reference these values by field name (such as senderId or conversationType) in the guidance. Only fields selected and available in the current conversation are sent; no extra lookups are made.",
1143
+ "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u7FA4\u804A\u6D88\u606F\u7684 <dsh_im_source> \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002": "Tell the model how to use <dsh_im_source> fields for the current group message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.",
1144
+ "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u79C1\u804A\u6D88\u606F\u7684 <dsh_im_source> \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002": "Tell the model how to use <dsh_im_source> fields for the current direct message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.",
1145
+ "\u4EC5\u5728\u7FA4\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002": "Used only when group enhancement is enabled. Clear and save to omit group guidance; selected source fields still follow the group settings.",
1146
+ "\u4EC5\u5728\u79C1\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002": "Used only when direct enhancement is enabled. Clear and save to omit direct guidance; selected source fields still follow the direct settings.",
1078
1147
  "\u53EA\u9700\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\uFF0C\u4E0D\u518D\u9644\u52A0\u589E\u5F3A\u63D0\u793A\u8BCD\uFF0C\u5DF2\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F00\u5173\u8BBE\u7F6E\u53D1\u9001\u3002": "Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags. Clear and save to omit guidance. Selected source fields still follow the conversation switches.",
1079
1148
  "\u53D1\u9001\u8005\u6807\u8BC6\u53EF\u80FD\u5305\u542B\u5E73\u53F0\u7528\u6237 ID \u6216\u7535\u8BDD\u53F7\u7801\u5F62\u5F0F\u7684\u6807\u8BC6\u3002\u5173\u95ED\u5F00\u5173\u4E0D\u4F1A\u5220\u9664\u5DF2\u7ECF\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\u7684\u4FE1\u606F\u3002": "Sender IDs may contain platform user IDs or phone-number-like identifiers. Turning this off does not remove information already saved in conversation history.",
1080
1149
  "\u4E0A\u4E0B\u6587\u589E\u5F3A\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not save context enhancement. Try again.",
@@ -1342,7 +1411,6 @@ var EN = Object.freeze({
1342
1411
  "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Get a Bot Token from @BotFather, then connect it here.",
1343
1412
  "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token": "Enter the Bot Token from @BotFather",
1344
1413
  "\u8BBF\u95EE\u6A21\u5F0F": "Access mode",
1345
- "\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
1346
1414
  "Telegram \u8BBF\u95EE\u6A21\u5F0F": "Telegram access mode",
1347
1415
  "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E": "View Telegram access mode details",
1348
1416
  "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002": "All group messages are ignored; only allowlisted users may send DMs.",
@@ -1872,6 +1940,135 @@ function normalizeLastMessageError(value) {
1872
1940
  return code && reason && message && referenceId && at !== null ? { code, reason, message, referenceId, at } : null;
1873
1941
  }
1874
1942
 
1943
+ // src/channels/shared/access-policy.mjs
1944
+ var ACCESS_POLICY_MODES = Object.freeze(["open", "allowlist"]);
1945
+ var ACCESS_POLICY_CONVERSATION_TYPES = Object.freeze(["direct", "group"]);
1946
+ var ACCESS_POLICY_USER_ID_MAX_LENGTH = 256;
1947
+ var POLICY_KEYS = ["direct", "group"];
1948
+ var SCOPE_KEYS = ["mode", "open", "allowlist"];
1949
+ var OPEN_KEYS = ["defaultCanExecuteCommands", "commandPermissionOverrides"];
1950
+ var ALLOWLIST_KEYS = ["users"];
1951
+ var USER_KEYS = ["id", "canExecuteCommands"];
1952
+ var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/;
1953
+ function invalidAccessPolicy(message) {
1954
+ const error = new TypeError(message);
1955
+ error.code = "access-policy-invalid";
1956
+ return error;
1957
+ }
1958
+ function hasExactKeys(input, keys) {
1959
+ return input && typeof input === "object" && !Array.isArray(input) && [Object.prototype, null].includes(Object.getPrototypeOf(input)) && Reflect.ownKeys(input).length === keys.length && keys.every((key) => Object.hasOwn(input, key));
1960
+ }
1961
+ function normalizeAccessPolicyUserId(value) {
1962
+ if (typeof value === "number") {
1963
+ if (!Number.isFinite(value)) throw invalidAccessPolicy("\u7528\u6237\u6807\u8BC6\u65E0\u6548\u3002");
1964
+ value = String(value);
1965
+ } else if (typeof value === "bigint") {
1966
+ value = String(value);
1967
+ }
1968
+ if (typeof value !== "string") throw invalidAccessPolicy("\u7528\u6237\u6807\u8BC6\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002");
1969
+ const id5 = value.trim();
1970
+ if (!id5 || id5.length > ACCESS_POLICY_USER_ID_MAX_LENGTH || CONTROL_CHARACTERS.test(id5)) {
1971
+ throw invalidAccessPolicy(`\u7528\u6237\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A\u3001\u5305\u542B\u63A7\u5236\u5B57\u7B26\u6216\u8D85\u8FC7 ${ACCESS_POLICY_USER_ID_MAX_LENGTH} \u4E2A\u5B57\u7B26\u3002`);
1972
+ }
1973
+ return id5;
1974
+ }
1975
+ function validateAccessPolicyUser(input) {
1976
+ if (!hasExactKeys(input, USER_KEYS)) {
1977
+ throw invalidAccessPolicy("\u7528\u6237\u6761\u76EE\u5FC5\u987B\u5305\u542B\u7528\u6237\u6807\u8BC6\u548C\u547D\u4EE4\u6743\u9650\u3002");
1978
+ }
1979
+ if (typeof input.canExecuteCommands !== "boolean") {
1980
+ throw invalidAccessPolicy("\u547D\u4EE4\u6743\u9650\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002");
1981
+ }
1982
+ return Object.freeze({
1983
+ id: normalizeAccessPolicyUserId(input.id),
1984
+ canExecuteCommands: input.canExecuteCommands
1985
+ });
1986
+ }
1987
+ function validateUsers(input, { listMessage, duplicateMessage }) {
1988
+ if (!Array.isArray(input)) throw invalidAccessPolicy(listMessage);
1989
+ const users = input.map(validateAccessPolicyUser);
1990
+ if (new Set(users.map(({ id: id5 }) => id5)).size !== users.length) {
1991
+ throw invalidAccessPolicy(duplicateMessage);
1992
+ }
1993
+ return Object.freeze(users);
1994
+ }
1995
+ function validateOpenSettings(input) {
1996
+ if (!hasExactKeys(input, OPEN_KEYS)) {
1997
+ throw invalidAccessPolicy("\u5F00\u653E\u6A21\u5F0F\u8BBE\u7F6E\u5FC5\u987B\u5B8C\u6574\u3002");
1998
+ }
1999
+ if (typeof input.defaultCanExecuteCommands !== "boolean") {
2000
+ throw invalidAccessPolicy("\u5F00\u653E\u6A21\u5F0F\u9ED8\u8BA4\u547D\u4EE4\u6743\u9650\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002");
2001
+ }
2002
+ return Object.freeze({
2003
+ defaultCanExecuteCommands: input.defaultCanExecuteCommands,
2004
+ commandPermissionOverrides: validateUsers(input.commandPermissionOverrides, {
2005
+ listMessage: "\u5F00\u653E\u6A21\u5F0F\u547D\u4EE4\u6743\u9650\u8986\u76D6\u7528\u6237\u5FC5\u987B\u662F\u6570\u7EC4\u3002",
2006
+ duplicateMessage: "\u5F00\u653E\u6A21\u5F0F\u547D\u4EE4\u6743\u9650\u8986\u76D6\u7528\u6237\u4E0D\u80FD\u5305\u542B\u91CD\u590D\u7684\u7528\u6237\u6807\u8BC6\u3002"
2007
+ })
2008
+ });
2009
+ }
2010
+ function validateAllowlistSettings(input) {
2011
+ if (!hasExactKeys(input, ALLOWLIST_KEYS)) {
2012
+ throw invalidAccessPolicy("\u767D\u540D\u5355\u6A21\u5F0F\u8BBE\u7F6E\u5FC5\u987B\u5B8C\u6574\u3002");
2013
+ }
2014
+ return Object.freeze({
2015
+ users: validateUsers(input.users, {
2016
+ listMessage: "\u767D\u540D\u5355\u6A21\u5F0F\u7528\u6237\u5FC5\u987B\u662F\u6570\u7EC4\u3002",
2017
+ duplicateMessage: "\u767D\u540D\u5355\u6A21\u5F0F\u7528\u6237\u4E0D\u80FD\u5305\u542B\u91CD\u590D\u7684\u7528\u6237\u6807\u8BC6\u3002"
2018
+ })
2019
+ });
2020
+ }
2021
+ function validateAccessPolicyScope(input) {
2022
+ if (!hasExactKeys(input, SCOPE_KEYS)) {
2023
+ throw invalidAccessPolicy("\u8BBF\u95EE\u573A\u666F\u8BBE\u7F6E\u5FC5\u987B\u540C\u65F6\u5305\u542B\u6A21\u5F0F\u3001\u5F00\u653E\u6A21\u5F0F\u8BBE\u7F6E\u548C\u767D\u540D\u5355\u6A21\u5F0F\u8BBE\u7F6E\u3002");
2024
+ }
2025
+ if (!ACCESS_POLICY_MODES.includes(input.mode)) {
2026
+ throw invalidAccessPolicy("\u8BBF\u95EE\u6A21\u5F0F\u53EA\u80FD\u662F open \u6216 allowlist\u3002");
2027
+ }
2028
+ return Object.freeze({
2029
+ mode: input.mode,
2030
+ open: validateOpenSettings(input.open),
2031
+ allowlist: validateAllowlistSettings(input.allowlist)
2032
+ });
2033
+ }
2034
+ function validateAccessPolicy(input) {
2035
+ if (!hasExactKeys(input, POLICY_KEYS)) {
2036
+ throw invalidAccessPolicy("\u8BF7\u540C\u65F6\u63D0\u4EA4\u5B8C\u6574\u7684\u79C1\u804A\u548C\u7FA4\u804A\u8BBF\u95EE\u8BBE\u7F6E\u3002");
2037
+ }
2038
+ return Object.freeze({
2039
+ direct: validateAccessPolicyScope(input.direct),
2040
+ group: validateAccessPolicyScope(input.group)
2041
+ });
2042
+ }
2043
+ function normalizeAccessPolicy(input) {
2044
+ try {
2045
+ return validateAccessPolicy(input);
2046
+ } catch {
2047
+ return null;
2048
+ }
2049
+ }
2050
+ function createAccessPolicyScope(options) {
2051
+ return validateAccessPolicyScope(options === void 0 ? {
2052
+ mode: "allowlist",
2053
+ open: {
2054
+ defaultCanExecuteCommands: false,
2055
+ commandPermissionOverrides: []
2056
+ },
2057
+ allowlist: { users: [] }
2058
+ } : options);
2059
+ }
2060
+ var DENY_SCOPE = createAccessPolicyScope();
2061
+ var DEFAULT_ACCESS_POLICY = Object.freeze({
2062
+ direct: DENY_SCOPE,
2063
+ group: DENY_SCOPE
2064
+ });
2065
+ var ALLOWED = Object.freeze({ allowed: true, reason: "allowed" });
2066
+ var POLICY_UNAVAILABLE = Object.freeze({ allowed: false, reason: "policy-unavailable" });
2067
+ var INVALID_CONTEXT = Object.freeze({ allowed: false, reason: "invalid-context" });
2068
+ var SENDER_UNAVAILABLE = Object.freeze({ allowed: false, reason: "sender-unavailable" });
2069
+ var SENDER_NOT_ALLOWED = Object.freeze({ allowed: false, reason: "sender-not-allowed" });
2070
+ var COMMAND_NOT_ALLOWED = Object.freeze({ allowed: false, reason: "command-not-allowed" });
2071
+
1875
2072
  // src/channels/shared/context-enhancement.mjs
1876
2073
  var CONTEXT_ENHANCEMENT_FIELDS = Object.freeze([
1877
2074
  "channel",
@@ -1881,26 +2078,39 @@ var CONTEXT_ENHANCEMENT_FIELDS = Object.freeze([
1881
2078
  "botId"
1882
2079
  ]);
1883
2080
  var CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH = 8e3;
1884
- var CONTEXT_GUIDANCE_EXAMPLE = `\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002
1885
- conversationType\u662F\u7FA4\u804A\u65F6\u56DE\u590D\u4E25\u8083\u4E00\u70B9\uFF0CconversationType\u662F\u79C1\u804A\u65F6\u56DE\u590D\u4E00\u5B9A\u8981\u5E7D\u9ED8\u641E\u7B11\uFF0C\u50CF\u5468\u661F\u9A70\u7684\u7535\u5F71\u4E00\u6837\u641E\u7B11`;
2081
+ var CONTEXT_GROUP_GUIDANCE_EXAMPLE = `\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002
2082
+ \u5F53\u524D\u6D88\u606F\u6765\u81EA\u7FA4\u804A\uFF0C\u8BF7\u4F7F\u7528\u4E25\u8083\u3001\u514B\u5236\u3001\u7B80\u6D01\u7684\u8868\u8FBE\u65B9\u5F0F\u3002`;
2083
+ var CONTEXT_DIRECT_GUIDANCE_EXAMPLE = `\u4EC5\u4F9D\u636E\u5F53\u524D\u6D88\u606F\u7684 <dsh_im_source> \u4E2D\u5B9E\u9645\u63D0\u4F9B\u7684\u5B57\u6BB5\u7406\u89E3\u6765\u6E90\uFF1B\u6CA1\u6709\u63D0\u4F9B\u7684\u5B57\u6BB5\u4E0D\u8981\u731C\u6D4B\u6216\u8865\u5168\u3002
2084
+ \u5F53\u524D\u6D88\u606F\u6765\u81EA\u79C1\u804A\uFF0C\u53EF\u4EE5\u4F7F\u7528\u66F4\u8F7B\u677E\u3001\u5E7D\u9ED8\u3001\u8BE6\u7EC6\u7684\u8868\u8FBE\u65B9\u5F0F\u3002`;
1886
2085
  var DEFAULT_CONTEXT_ENHANCEMENT_CONFIG = Object.freeze({
1887
- groupEnabled: false,
1888
- directEnabled: false,
1889
- fields: Object.freeze(["senderId"]),
1890
- guidance: ""
2086
+ group: Object.freeze({
2087
+ enabled: false,
2088
+ fields: Object.freeze(["senderId"]),
2089
+ guidance: ""
2090
+ }),
2091
+ direct: Object.freeze({
2092
+ enabled: false,
2093
+ fields: Object.freeze(["senderId"]),
2094
+ guidance: ""
2095
+ })
1891
2096
  });
1892
- var CONFIG_KEYS = ["groupEnabled", "directEnabled", "fields", "guidance"];
2097
+ var CONFIG_KEYS = ["group", "direct"];
2098
+ var SCOPE_KEYS2 = ["enabled", "fields", "guidance"];
2099
+ var LEGACY_CONFIG_KEYS = ["groupEnabled", "directEnabled", "fields", "guidance"];
1893
2100
  function invalidConfig(message) {
1894
2101
  const error = new TypeError(message);
1895
2102
  error.code = "context-enhancement-invalid";
1896
2103
  return error;
1897
2104
  }
1898
- function validateContextEnhancementConfig(input) {
1899
- if (!input || typeof input !== "object" || Array.isArray(input) || ![Object.prototype, null].includes(Object.getPrototypeOf(input)) || Reflect.ownKeys(input).length !== CONFIG_KEYS.length || !CONFIG_KEYS.every((key) => Object.hasOwn(input, key))) {
2105
+ function hasExactKeys2(input, keys) {
2106
+ return input && typeof input === "object" && !Array.isArray(input) && [Object.prototype, null].includes(Object.getPrototypeOf(input)) && Reflect.ownKeys(input).length === keys.length && keys.every((key) => Object.hasOwn(input, key));
2107
+ }
2108
+ function validateContextEnhancementScope(input) {
2109
+ if (!hasExactKeys2(input, SCOPE_KEYS2)) {
1900
2110
  throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
1901
2111
  }
1902
- const { groupEnabled, directEnabled, fields, guidance } = input;
1903
- if (typeof groupEnabled !== "boolean" || typeof directEnabled !== "boolean") {
2112
+ const { enabled, fields, guidance } = input;
2113
+ if (typeof enabled !== "boolean") {
1904
2114
  throw invalidConfig("\u7FA4\u804A\u548C\u79C1\u804A\u5F00\u5173\u5FC5\u987B\u662F\u5E03\u5C14\u503C\u3002");
1905
2115
  }
1906
2116
  if (!Array.isArray(fields) || ![...fields].every((field) => CONTEXT_ENHANCEMENT_FIELDS.includes(field))) {
@@ -1910,17 +2120,46 @@ function validateContextEnhancementConfig(input) {
1910
2120
  throw invalidConfig(`\u589E\u5F3A\u63D0\u793A\u8BCD\u4E0D\u5F97\u8D85\u8FC7 ${CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH} \u4E2A\u5B57\u7B26\u3002`);
1911
2121
  }
1912
2122
  return Object.freeze({
1913
- groupEnabled,
1914
- directEnabled,
2123
+ enabled,
1915
2124
  fields: Object.freeze(CONTEXT_ENHANCEMENT_FIELDS.filter((field) => fields.includes(field))),
1916
2125
  guidance: guidance.trim() ? guidance : ""
1917
2126
  });
1918
2127
  }
2128
+ function validateContextEnhancementConfig(input) {
2129
+ if (!hasExactKeys2(input, CONFIG_KEYS)) {
2130
+ throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
2131
+ }
2132
+ return Object.freeze({
2133
+ group: validateContextEnhancementScope(input.group),
2134
+ direct: validateContextEnhancementScope(input.direct)
2135
+ });
2136
+ }
2137
+ function migrateLegacyContextEnhancementConfig(input) {
2138
+ if (!hasExactKeys2(input, LEGACY_CONFIG_KEYS)) {
2139
+ throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
2140
+ }
2141
+ return validateContextEnhancementConfig({
2142
+ group: {
2143
+ enabled: input.groupEnabled,
2144
+ fields: input.fields,
2145
+ guidance: input.guidance
2146
+ },
2147
+ direct: {
2148
+ enabled: input.directEnabled,
2149
+ fields: input.fields,
2150
+ guidance: input.guidance
2151
+ }
2152
+ });
2153
+ }
1919
2154
  function normalizeContextEnhancementConfig(input) {
1920
2155
  try {
1921
2156
  return validateContextEnhancementConfig(input);
1922
2157
  } catch {
1923
- return DEFAULT_CONTEXT_ENHANCEMENT_CONFIG;
2158
+ try {
2159
+ return migrateLegacyContextEnhancementConfig(input);
2160
+ } catch {
2161
+ return DEFAULT_CONTEXT_ENHANCEMENT_CONFIG;
2162
+ }
1924
2163
  }
1925
2164
  }
1926
2165
 
@@ -1936,7 +2175,8 @@ var DINGTALK_ENDPOINTS = Object.freeze({
1936
2175
  deleteBot: "bot.delete",
1937
2176
  setWorkspace: "bot.workspace.set",
1938
2177
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
1939
- setContextEnhancement: "bot.context-enhancement.set"
2178
+ setContextEnhancement: "bot.context-enhancement.set",
2179
+ setAccessPolicy: "bot.access-policy.set"
1940
2180
  });
1941
2181
  var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
1942
2182
  var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
@@ -2066,6 +2306,7 @@ function normalizeBot(value) {
2066
2306
  workspace: optionalString(value.workspace, 4096) ?? "",
2067
2307
  agentPreset: normalizeAgentPresetId(value.agentPreset),
2068
2308
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
2309
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
2069
2310
  bot: {
2070
2311
  name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
2071
2312
  clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
@@ -2656,11 +2897,33 @@ var FIELD_LABELS = Object.freeze({
2656
2897
  senderName: "\u53D1\u9001\u8005\u6635\u79F0",
2657
2898
  botId: "\u673A\u5668\u4EBA\u6807\u8BC6"
2658
2899
  });
2900
+ var SCOPE_COPY = Object.freeze({
2901
+ group: Object.freeze({
2902
+ title: "\u7FA4\u804A",
2903
+ enable: "\u542F\u7528",
2904
+ fieldsHelpLabel: "\u67E5\u770B\u7FA4\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E",
2905
+ senderNameHelpLabel: "\u67E5\u770B\u7FA4\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E",
2906
+ guidanceLabel: "\u589E\u5F3A\u63D0\u793A\u8BCD",
2907
+ guidanceHelpLabel: "\u67E5\u770B\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E",
2908
+ guidanceUsage: "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u7FA4\u804A\u6D88\u606F\u7684 <dsh_im_source> \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002",
2909
+ guidanceBehavior: "\u4EC5\u5728\u7FA4\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u7FA4\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002"
2910
+ }),
2911
+ direct: Object.freeze({
2912
+ title: "\u79C1\u804A",
2913
+ enable: "\u542F\u7528",
2914
+ fieldsHelpLabel: "\u67E5\u770B\u79C1\u804A\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E",
2915
+ senderNameHelpLabel: "\u67E5\u770B\u79C1\u804A\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E",
2916
+ guidanceLabel: "\u589E\u5F3A\u63D0\u793A\u8BCD",
2917
+ guidanceHelpLabel: "\u67E5\u770B\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E",
2918
+ guidanceUsage: "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528\u5F53\u524D\u79C1\u804A\u6D88\u606F\u7684 <dsh_im_source> \u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002",
2919
+ guidanceBehavior: "\u4EC5\u5728\u79C1\u804A\u5F00\u5173\u5F00\u542F\u65F6\u4F7F\u7528\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\u4E0D\u518D\u9644\u52A0\u79C1\u804A\u589E\u5F3A\u63D0\u793A\u8BCD\uFF1B\u6240\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F53\u524D\u573A\u666F\u8BBE\u7F6E\u53D1\u9001\u3002"
2920
+ })
2921
+ });
2659
2922
  function contextEnhancementLabel(config) {
2660
- const { groupEnabled, directEnabled } = normalizeContextEnhancementConfig(config);
2661
- if (groupEnabled && directEnabled) return "\u7FA4\u804A\u548C\u79C1\u804A";
2662
- if (groupEnabled) return "\u4EC5\u7FA4\u804A";
2663
- if (directEnabled) return "\u4EC5\u79C1\u804A";
2923
+ const { group, direct } = normalizeContextEnhancementConfig(config);
2924
+ if (group.enabled && direct.enabled) return "\u7FA4\u804A\u548C\u79C1\u804A";
2925
+ if (group.enabled) return "\u4EC5\u7FA4\u804A";
2926
+ if (direct.enabled) return "\u4EC5\u79C1\u804A";
2664
2927
  return "\u672A\u5F00\u542F";
2665
2928
  }
2666
2929
  function ContextIcon({ kind = "sliders" }) {
@@ -2678,26 +2941,197 @@ function ContextIcon({ kind = "sliders" }) {
2678
2941
  focusable: "false"
2679
2942
  }, h2("path", { d: path }));
2680
2943
  }
2944
+ function ContextEnhancementScopeEditor({
2945
+ kind,
2946
+ scope,
2947
+ example,
2948
+ supported,
2949
+ busy,
2950
+ idPrefix,
2951
+ onChange
2952
+ }) {
2953
+ const copy = SCOPE_COPY[kind];
2954
+ const unavailableId = `${idPrefix}-${kind}-unavailable`;
2955
+ const fieldsHelpId = `${idPrefix}-${kind}-fields-help`;
2956
+ const senderNameHelpId = `${idPrefix}-${kind}-sender-name-help`;
2957
+ const guidanceId = `${idPrefix}-${kind}-guidance`;
2958
+ const guidanceHelpId = `${idPrefix}-${kind}-guidance-help`;
2959
+ const disabled = busy || !supported;
2960
+ return h2(
2961
+ "fieldset",
2962
+ {
2963
+ className: "dim-contextSection dim-contextScope",
2964
+ "data-context-kind": kind,
2965
+ "aria-label": copy.title,
2966
+ disabled
2967
+ },
2968
+ h2(
2969
+ "label",
2970
+ { className: "dim-contextSwitchRow" },
2971
+ h2(
2972
+ "span",
2973
+ { className: "dim-contextSwitchLabel" },
2974
+ h2("span", null, copy.enable),
2975
+ !supported ? h2("span", {
2976
+ id: unavailableId,
2977
+ className: "dim-contextUnavailable"
2978
+ }, "\uFF08\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A\uFF09") : null
2979
+ ),
2980
+ h2("input", {
2981
+ type: "checkbox",
2982
+ role: "switch",
2983
+ className: "dim-contextSwitch",
2984
+ checked: supported && scope.enabled,
2985
+ disabled,
2986
+ "aria-describedby": !supported ? unavailableId : void 0,
2987
+ onChange: (event) => {
2988
+ if (supported) onChange("enabled", event.target.checked);
2989
+ }
2990
+ })
2991
+ ),
2992
+ h2(
2993
+ "div",
2994
+ { className: "dim-contextScopeBlock" },
2995
+ h2(
2996
+ "div",
2997
+ { className: "dim-contextLegend" },
2998
+ h2("span", null, "\u6765\u6E90\u5B57\u6BB5"),
2999
+ h2(
3000
+ "span",
3001
+ { className: "dim-contextHelp dim-contextLegendHelp" },
3002
+ h2("button", {
3003
+ type: "button",
3004
+ className: "dim-contextHelpButton",
3005
+ disabled,
3006
+ "aria-label": copy.fieldsHelpLabel,
3007
+ "aria-describedby": fieldsHelpId
3008
+ }, h2("span", { "aria-hidden": "true" }, "?")),
3009
+ h2(
3010
+ "span",
3011
+ { id: fieldsHelpId, className: "dim-contextTooltip dim-contextLegendTooltip", role: "tooltip" },
3012
+ "\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u5F53\u524D\u4F1A\u8BDD\u4E2D\u52FE\u9009\u4E14\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002"
3013
+ )
3014
+ )
3015
+ ),
3016
+ h2("div", { className: "dim-contextFields" }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
3017
+ const fieldId = `${idPrefix}-${kind}-field-${field}`;
3018
+ return h2(
3019
+ "div",
3020
+ { key: field, className: "dim-contextField" },
3021
+ h2("input", {
3022
+ id: fieldId,
3023
+ type: "checkbox",
3024
+ name: `${kind}-${field}`,
3025
+ checked: scope.fields.includes(field),
3026
+ disabled,
3027
+ onChange: (event) => onChange("fields", event.target.checked ? [...scope.fields, field] : scope.fields.filter((value) => value !== field))
3028
+ }),
3029
+ h2(
3030
+ "span",
3031
+ { className: "dim-contextFieldText" },
3032
+ h2("label", { className: "dim-contextFieldName", htmlFor: fieldId }, FIELD_LABELS[field]),
3033
+ field === "senderName" ? h2(
3034
+ "span",
3035
+ { className: "dim-contextHelp dim-contextFieldHelp" },
3036
+ h2("button", {
3037
+ type: "button",
3038
+ className: "dim-contextHelpButton dim-contextFieldHelpButton",
3039
+ disabled,
3040
+ "aria-label": copy.senderNameHelpLabel,
3041
+ "aria-describedby": senderNameHelpId
3042
+ }, h2("span", { "aria-hidden": "true" }, "?")),
3043
+ h2("span", {
3044
+ id: senderNameHelpId,
3045
+ className: "dim-contextTooltip dim-contextFieldTooltip",
3046
+ role: "tooltip"
3047
+ }, "\u8BE5\u5B57\u6BB5\u4E0D\u662F\u6BCF\u4E2A\u6E20\u9053\u90FD\u80FD\u63D0\u4F9B\u3002\u5F53\u524D\u6D88\u606F\u6CA1\u6709\u53D1\u9001\u8005\u6635\u79F0\u65F6\uFF0C\u5373\u4F7F\u5DF2\u9009\u62E9\u8BE5\u5B57\u6BB5\uFF0C<dsh_im_source> \u4E2D\u4E5F\u4F1A\u7701\u7565 senderName\u3002")
3048
+ ) : null,
3049
+ h2("label", { className: "dim-contextFieldKey", htmlFor: fieldId }, field)
3050
+ )
3051
+ );
3052
+ }))
3053
+ ),
3054
+ h2(
3055
+ "div",
3056
+ { className: "dim-contextGuidance dim-contextScopeBlock" },
3057
+ h2(
3058
+ "div",
3059
+ { className: "dim-contextEditorHeader" },
3060
+ h2(
3061
+ "span",
3062
+ { className: "dim-contextEditorTitle" },
3063
+ h2("label", { htmlFor: guidanceId }, copy.guidanceLabel),
3064
+ h2(
3065
+ "span",
3066
+ { className: "dim-contextHelp" },
3067
+ h2("button", {
3068
+ type: "button",
3069
+ className: "dim-contextHelpButton",
3070
+ disabled,
3071
+ "aria-label": copy.guidanceHelpLabel,
3072
+ "aria-describedby": guidanceHelpId
3073
+ }, h2("span", { "aria-hidden": "true" }, "?")),
3074
+ h2(
3075
+ "span",
3076
+ { id: guidanceHelpId, className: "dim-contextTooltip dim-contextGuidanceTooltip", role: "tooltip" },
3077
+ h2("strong", null, "\u4F7F\u7528\u8BF4\u660E"),
3078
+ h2("span", null, copy.guidanceUsage),
3079
+ h2("strong", null, "\u751F\u6548\u89C4\u5219"),
3080
+ h2("span", null, copy.guidanceBehavior),
3081
+ h2("strong", null, "\u9690\u79C1\u63D0\u793A"),
3082
+ h2("span", null, "\u53D1\u9001\u8005\u6807\u8BC6\u53EF\u80FD\u5305\u542B\u5E73\u53F0\u7528\u6237 ID \u6216\u7535\u8BDD\u53F7\u7801\u5F62\u5F0F\u7684\u6807\u8BC6\u3002\u5173\u95ED\u5F00\u5173\u4E0D\u4F1A\u5220\u9664\u5DF2\u7ECF\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\u7684\u4FE1\u606F\u3002"),
3083
+ h2("strong", null, "\u4F7F\u7528\u793A\u4F8B"),
3084
+ h2("span", { className: "dim-contextTooltipExample" }, example)
3085
+ )
3086
+ )
3087
+ ),
3088
+ h2(
3089
+ "div",
3090
+ { className: "dim-contextTextActions" },
3091
+ h2("button", { type: "button", disabled, onClick: () => onChange("guidance", example) }, "\u586B\u5165\u793A\u4F8B"),
3092
+ h2("button", { type: "button", disabled, onClick: () => onChange("guidance", "") }, "\u6E05\u7A7A")
3093
+ )
3094
+ ),
3095
+ h2("textarea", {
3096
+ id: guidanceId,
3097
+ value: scope.guidance,
3098
+ placeholder: example,
3099
+ rows: 4,
3100
+ disabled,
3101
+ "data-context-kind": kind,
3102
+ maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
3103
+ "aria-describedby": guidanceHelpId,
3104
+ onChange: (event) => onChange("guidance", event.target.value)
3105
+ })
3106
+ )
3107
+ );
3108
+ }
2681
3109
  function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id: id5 }) {
2682
- const [draft, setDraft] = React7.useState(() => ({
2683
- ...normalizeContextEnhancementConfig(config),
2684
- ...!groupSupported ? { groupEnabled: false } : {}
2685
- }));
3110
+ const [draft, setDraft] = React7.useState(() => {
3111
+ const normalized = normalizeContextEnhancementConfig(config);
3112
+ return {
3113
+ ...normalized,
3114
+ ...!groupSupported ? {
3115
+ group: { ...normalized.group, enabled: false }
3116
+ } : {}
3117
+ };
3118
+ });
2686
3119
  const [saving, setSaving] = React7.useState(false);
2687
3120
  const [error, setError] = React7.useState(null);
3121
+ const [activeScope, setActiveScope] = React7.useState("direct");
2688
3122
  const savingRef = React7.useRef(false);
2689
3123
  const dialogRef = React7.useRef(null);
2690
3124
  const mountedRef = React7.useRef(true);
3125
+ const groupTabRef = React7.useRef(null);
3126
+ const directTabRef = React7.useRef(null);
2691
3127
  const titleId = React7.useId();
2692
3128
  const descriptionId = React7.useId();
2693
- const groupNoticeId = React7.useId();
2694
- const guidanceId = React7.useId();
2695
- const guidanceHelpId = React7.useId();
2696
- const fieldIdPrefix = React7.useId();
2697
- const fieldsHelpId = React7.useId();
2698
- const senderNameHelpId = React7.useId();
2699
- const guidanceExample = localizeText(CONTEXT_GUIDANCE_EXAMPLE);
3129
+ const scopeIdPrefix = React7.useId();
3130
+ const groupGuidanceExample = localizeText(CONTEXT_GROUP_GUIDANCE_EXAMPLE);
3131
+ const directGuidanceExample = localizeText(CONTEXT_DIRECT_GUIDANCE_EXAMPLE);
2700
3132
  const busy = disabled || saving;
3133
+ const scopeKinds = ["direct", "group"];
3134
+ const tabRefs = { group: groupTabRef, direct: directTabRef };
2701
3135
  React7.useEffect(() => {
2702
3136
  mountedRef.current = true;
2703
3137
  dialogRef.current?.focus?.();
@@ -2711,14 +3145,34 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
2711
3145
  queueMicrotask(() => returnFocusRef.current?.focus?.());
2712
3146
  };
2713
3147
  }, [returnFocusRef]);
2714
- const change = (key, value) => {
3148
+ const changeScope = (kind, key, value) => {
2715
3149
  if (busy || savingRef.current) return;
2716
- setDraft((current) => ({ ...current, [key]: value }));
3150
+ setDraft((current) => ({
3151
+ ...current,
3152
+ [kind]: { ...current[kind], [key]: value }
3153
+ }));
2717
3154
  setError(null);
2718
3155
  };
2719
3156
  const cancel = () => {
2720
3157
  if (!savingRef.current) onClose();
2721
3158
  };
3159
+ const activateScope = (kind, focus = false) => {
3160
+ if (busy || !scopeKinds.includes(kind)) return;
3161
+ setActiveScope(kind);
3162
+ if (focus) tabRefs[kind].current?.focus?.();
3163
+ };
3164
+ const handleTabKeyDown = (event, kind) => {
3165
+ let next;
3166
+ if (event.key === "Home") next = scopeKinds[0];
3167
+ else if (event.key === "End") next = scopeKinds.at(-1);
3168
+ else if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
3169
+ const offset = event.key === "ArrowRight" ? 1 : -1;
3170
+ next = scopeKinds[(scopeKinds.indexOf(kind) + offset + scopeKinds.length) % scopeKinds.length];
3171
+ }
3172
+ if (!next) return;
3173
+ event.preventDefault();
3174
+ activateScope(next, true);
3175
+ };
2722
3176
  const save = async () => {
2723
3177
  if (busy || savingRef.current) return;
2724
3178
  savingRef.current = true;
@@ -2813,166 +3267,45 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
2813
3267
  }, h2(ContextIcon, { kind: "close" }))
2814
3268
  ),
2815
3269
  h2(
2816
- "fieldset",
2817
- { className: "dim-contextSection", disabled: busy },
2818
- h2("legend", null, "\u542F\u7528\u8303\u56F4"),
2819
- h2(
2820
- "div",
2821
- { className: "dim-contextSwitches" },
2822
- h2(
2823
- "label",
2824
- { className: "dim-contextSwitchRow" },
2825
- h2(
2826
- "span",
2827
- { className: "dim-contextSwitchLabel" },
2828
- h2("span", null, "\u7FA4\u804A\u4E2D\u542F\u7528"),
2829
- !groupSupported ? h2("span", {
2830
- id: groupNoticeId,
2831
- className: "dim-contextUnavailable"
2832
- }, "\uFF08\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A\uFF09") : null
2833
- ),
2834
- h2("input", {
2835
- type: "checkbox",
2836
- role: "switch",
2837
- className: "dim-contextSwitch",
2838
- checked: groupSupported && draft.groupEnabled,
2839
- disabled: busy || !groupSupported,
2840
- "aria-describedby": !groupSupported ? groupNoticeId : void 0,
2841
- onChange: (event) => {
2842
- if (groupSupported) change("groupEnabled", event.target.checked);
2843
- }
2844
- })
2845
- ),
2846
- h2(
2847
- "label",
2848
- { className: "dim-contextSwitchRow" },
2849
- h2("span", null, "\u79C1\u804A\u4E2D\u542F\u7528"),
2850
- h2("input", {
2851
- type: "checkbox",
2852
- role: "switch",
2853
- className: "dim-contextSwitch",
2854
- checked: draft.directEnabled,
2855
- disabled: busy,
2856
- onChange: (event) => change("directEnabled", event.target.checked)
2857
- })
2858
- )
2859
- )
3270
+ "div",
3271
+ { className: "dim-contextTabs", role: "tablist", "aria-label": "\u4E0A\u4E0B\u6587\u589E\u5F3A\u8303\u56F4" },
3272
+ scopeKinds.map((kind) => {
3273
+ const selected = activeScope === kind;
3274
+ return h2("button", {
3275
+ key: kind,
3276
+ id: `${scopeIdPrefix}-${kind}-tab`,
3277
+ ref: tabRefs[kind],
3278
+ type: "button",
3279
+ role: "tab",
3280
+ className: "dim-contextTab",
3281
+ "data-context-kind": kind,
3282
+ "aria-selected": selected,
3283
+ "aria-controls": `${scopeIdPrefix}-${kind}-panel`,
3284
+ tabIndex: selected ? 0 : -1,
3285
+ disabled: busy,
3286
+ onClick: () => activateScope(kind),
3287
+ onKeyDown: (event) => handleTabKeyDown(event, kind)
3288
+ }, SCOPE_COPY[kind].title);
3289
+ })
2860
3290
  ),
2861
- h2(
2862
- "fieldset",
2863
- { className: "dim-contextSection", disabled: busy },
2864
- h2(
2865
- "legend",
2866
- { className: "dim-contextLegend" },
2867
- h2("span", null, "\u6765\u6E90\u5B57\u6BB5"),
2868
- h2(
2869
- "span",
2870
- { className: "dim-contextHelp dim-contextLegendHelp" },
2871
- h2("button", {
2872
- type: "button",
2873
- className: "dim-contextHelpButton",
2874
- disabled: busy,
2875
- "aria-label": "\u67E5\u770B\u6765\u6E90\u5B57\u6BB5\u8BF4\u660E",
2876
- "aria-describedby": fieldsHelpId
2877
- }, h2("span", { "aria-hidden": "true" }, "?")),
2878
- h2(
2879
- "span",
2880
- { id: fieldsHelpId, className: "dim-contextTooltip dim-contextLegendTooltip", role: "tooltip" },
2881
- "\u589E\u5F3A\u63D0\u793A\u8BCD\u4E2D\u8BF7\u4F7F\u7528\u5B57\u6BB5\u540D\uFF08\u5982 senderId\u3001conversationType\uFF09\u5F15\u7528\u8FD9\u4E9B\u4FE1\u606F\u3002\u53EA\u53D1\u9001\u52FE\u9009\u4E14\u5F53\u524D\u6D88\u606F\u4E2D\u53EF\u7528\u7684\u5B57\u6BB5\uFF0C\u4E0D\u4F1A\u989D\u5916\u67E5\u8BE2\u6216\u8865\u5168\u3002"
2882
- )
2883
- )
2884
- ),
2885
- h2("div", { className: "dim-contextFields" }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
2886
- const fieldId = `${fieldIdPrefix}-${field}`;
2887
- return h2(
2888
- "div",
2889
- { key: field, className: "dim-contextField" },
2890
- h2("input", {
2891
- id: fieldId,
2892
- type: "checkbox",
2893
- name: field,
2894
- checked: draft.fields.includes(field),
2895
- disabled: busy,
2896
- onChange: (event) => change("fields", event.target.checked ? [...draft.fields, field] : draft.fields.filter((value) => value !== field))
2897
- }),
2898
- h2(
2899
- "span",
2900
- { className: "dim-contextFieldText" },
2901
- h2("label", { className: "dim-contextFieldName", htmlFor: fieldId }, FIELD_LABELS[field]),
2902
- field === "senderName" ? h2(
2903
- "span",
2904
- { className: "dim-contextHelp dim-contextFieldHelp" },
2905
- h2("button", {
2906
- type: "button",
2907
- className: "dim-contextHelpButton dim-contextFieldHelpButton",
2908
- disabled: busy,
2909
- "aria-label": "\u67E5\u770B\u53D1\u9001\u8005\u6635\u79F0\u5B57\u6BB5\u8BF4\u660E",
2910
- "aria-describedby": senderNameHelpId
2911
- }, h2("span", { "aria-hidden": "true" }, "?")),
2912
- h2("span", {
2913
- id: senderNameHelpId,
2914
- className: "dim-contextTooltip dim-contextFieldTooltip",
2915
- role: "tooltip"
2916
- }, "\u8BE5\u5B57\u6BB5\u4E0D\u662F\u6BCF\u4E2A\u6E20\u9053\u90FD\u80FD\u63D0\u4F9B\u3002\u5F53\u524D\u6D88\u606F\u6CA1\u6709\u53D1\u9001\u8005\u6635\u79F0\u65F6\uFF0C\u5373\u4F7F\u5DF2\u9009\u62E9\u8BE5\u5B57\u6BB5\uFF0C<dsh_im_source> \u4E2D\u4E5F\u4F1A\u7701\u7565 senderName\u3002")
2917
- ) : null,
2918
- h2("label", { className: "dim-contextFieldKey", htmlFor: fieldId }, field)
2919
- )
2920
- );
2921
- }))
2922
- ),
2923
- h2(
2924
- "div",
2925
- { className: "dim-contextGuidance" },
2926
- h2(
2927
- "div",
2928
- { className: "dim-contextEditorHeader" },
2929
- h2(
2930
- "span",
2931
- { className: "dim-contextEditorTitle" },
2932
- h2("label", { htmlFor: guidanceId }, "\u589E\u5F3A\u63D0\u793A\u8BCD"),
2933
- h2(
2934
- "span",
2935
- { className: "dim-contextHelp" },
2936
- h2("button", {
2937
- type: "button",
2938
- className: "dim-contextHelpButton",
2939
- disabled: busy,
2940
- "aria-label": "\u67E5\u770B\u589E\u5F3A\u63D0\u793A\u8BCD\u4F7F\u7528\u8BF4\u660E",
2941
- "aria-describedby": guidanceHelpId
2942
- }, h2("span", { "aria-hidden": "true" }, "?")),
2943
- h2(
2944
- "span",
2945
- { id: guidanceHelpId, className: "dim-contextTooltip dim-contextGuidanceTooltip", role: "tooltip" },
2946
- h2("strong", null, "\u4F7F\u7528\u8BF4\u660E"),
2947
- h2("span", null, "\u7528\u4E8E\u544A\u8BC9\u6A21\u578B\u5982\u4F55\u4F7F\u7528 <dsh_im_source> \u4E2D\u5DF2\u9009\u62E9\u7684\u6765\u6E90\u5B57\u6BB5\u3002\u53EA\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002"),
2948
- h2("strong", null, "\u751F\u6548\u89C4\u5219"),
2949
- h2("span", null, "\u53EA\u9700\u586B\u5199\u6B63\u6587\uFF0C\u63D2\u4EF6\u81EA\u52A8\u6DFB\u52A0 <dsh_im_source_guidance> \u6210\u5BF9\u6807\u7B7E\u3002\u6E05\u7A7A\u5E76\u4FDD\u5B58\u540E\uFF0C\u4E0D\u518D\u9644\u52A0\u589E\u5F3A\u63D0\u793A\u8BCD\uFF0C\u5DF2\u9009\u6765\u6E90\u5B57\u6BB5\u4ECD\u6309\u5F00\u5173\u8BBE\u7F6E\u53D1\u9001\u3002"),
2950
- h2("strong", null, "\u9690\u79C1\u63D0\u793A"),
2951
- h2("span", null, "\u53D1\u9001\u8005\u6807\u8BC6\u53EF\u80FD\u5305\u542B\u5E73\u53F0\u7528\u6237 ID \u6216\u7535\u8BDD\u53F7\u7801\u5F62\u5F0F\u7684\u6807\u8BC6\u3002\u5173\u95ED\u5F00\u5173\u4E0D\u4F1A\u5220\u9664\u5DF2\u7ECF\u5199\u5165\u4F1A\u8BDD\u5386\u53F2\u7684\u4FE1\u606F\u3002"),
2952
- h2("strong", null, "\u4F7F\u7528\u793A\u4F8B"),
2953
- h2("span", { className: "dim-contextTooltipExample" }, guidanceExample)
2954
- )
2955
- )
2956
- ),
2957
- h2(
2958
- "div",
2959
- { className: "dim-contextTextActions" },
2960
- h2("button", { type: "button", disabled: busy, onClick: () => change("guidance", guidanceExample) }, "\u586B\u5165\u793A\u4F8B"),
2961
- h2("button", { type: "button", disabled: busy, onClick: () => change("guidance", "") }, "\u6E05\u7A7A")
2962
- )
2963
- ),
2964
- h2("textarea", {
2965
- id: guidanceId,
2966
- value: draft.guidance,
2967
- placeholder: guidanceExample,
2968
- rows: 4,
2969
- disabled: busy,
2970
- maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
2971
- "aria-describedby": guidanceHelpId,
2972
- onChange: (event) => change("guidance", event.target.value)
2973
- })
2974
- ),
2975
- error ? h2("p", { className: "dim-contextError", role: "alert" }, error) : null,
3291
+ scopeKinds.map((kind) => h2("div", {
3292
+ key: kind,
3293
+ id: `${scopeIdPrefix}-${kind}-panel`,
3294
+ className: "dim-contextTabPanel",
3295
+ role: "tabpanel",
3296
+ "data-context-kind": kind,
3297
+ "aria-labelledby": `${scopeIdPrefix}-${kind}-tab`,
3298
+ hidden: activeScope !== kind
3299
+ }, h2(ContextEnhancementScopeEditor, {
3300
+ kind,
3301
+ scope: draft[kind],
3302
+ example: kind === "group" ? groupGuidanceExample : directGuidanceExample,
3303
+ supported: kind === "group" ? groupSupported : true,
3304
+ busy,
3305
+ idPrefix: scopeIdPrefix,
3306
+ onChange: (key, value) => changeScope(kind, key, value)
3307
+ }))),
3308
+ error ? h2("p", { className: "dim-contextError", role: "alert" }, error) : null,
2976
3309
  h2(
2977
3310
  "footer",
2978
3311
  { className: "dim-contextFooter" },
@@ -2995,7 +3328,10 @@ function ContextEnhancementEditor({ config, groupSupported = true, disabled = fa
2995
3328
  const dialogId = React7.useId();
2996
3329
  const statusId = React7.useId();
2997
3330
  const saved = normalizeContextEnhancementConfig(config);
2998
- const label = contextEnhancementLabel(groupSupported ? saved : { ...saved, groupEnabled: false });
3331
+ const label = contextEnhancementLabel(groupSupported ? saved : {
3332
+ ...saved,
3333
+ group: { ...saved.group, enabled: false }
3334
+ });
2999
3335
  return h2(
3000
3336
  React7.Fragment,
3001
3337
  null,
@@ -3080,7 +3416,7 @@ function SettingsGlyph() {
3080
3416
  h2("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.03 1.55V21h-4v-.08A1.7 1.7 0 0 0 8.97 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.52-1.03H3v-4h.08A1.7 1.7 0 0 0 4.6 8.97a1.7 1.7 0 0 0-.34-1.88l-.06-.06L7.03 4.2l.06.06a1.7 1.7 0 0 0 1.88.34A1.7 1.7 0 0 0 10 3.08V3h4v.08a1.7 1.7 0 0 0 1.03 1.52 1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06a1.7 1.7 0 0 0-.34 1.88A1.7 1.7 0 0 0 20.92 10H21v4h-.08A1.7 1.7 0 0 0 19.4 15Z" })
3081
3417
  );
3082
3418
  }
3083
- function BotSettingsButton({ channel: channel4, botId, botName, connected }) {
3419
+ function BotSettingsButton({ channel: channel4, botId, botName, connected, accessPolicy }) {
3084
3420
  const { openBotSettings } = React9.useContext(BotSettingsContext);
3085
3421
  const tooltipId = React9.useId();
3086
3422
  return h2(
@@ -3096,7 +3432,8 @@ function BotSettingsButton({ channel: channel4, botId, botName, connected }) {
3096
3432
  channel: channel4,
3097
3433
  botId,
3098
3434
  botName,
3099
- connected: Boolean(connected)
3435
+ connected: Boolean(connected),
3436
+ accessPolicy
3100
3437
  })
3101
3438
  }, h2(SettingsGlyph)),
3102
3439
  h2("span", {
@@ -3659,7 +3996,8 @@ function AccountCard({
3659
3996
  channel: "dingtalk",
3660
3997
  botId: account.botId,
3661
3998
  botName: account.bot.name,
3662
- connected: account.connected
3999
+ connected: account.connected,
4000
+ accessPolicy: account.accessPolicy
3663
4001
  })
3664
4002
  )
3665
4003
  ),
@@ -4314,12 +4652,13 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4314
4652
  deleteBot: "bot.delete",
4315
4653
  setWorkspace: "bot.workspace.set",
4316
4654
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
4317
- setContextEnhancement: "bot.context-enhancement.set"
4655
+ setContextEnhancement: "bot.context-enhancement.set",
4656
+ setAccessPolicy: "bot.access-policy.set"
4318
4657
  });
4319
4658
  function createTokenChannelApi(channel4, connectionSummary, {
4320
4659
  normalizeBotExtension = () => ({})
4321
4660
  } = {}) {
4322
- const unwrapRpcResult11 = (result) => {
4661
+ const unwrapRpcResult12 = (result) => {
4323
4662
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
4324
4663
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
4325
4664
  }
@@ -4342,6 +4681,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
4342
4681
  workspace: text2(value.workspace, "", 4096),
4343
4682
  agentPreset: normalizeAgentPresetId(value.agentPreset),
4344
4683
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
4684
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
4345
4685
  bot: {
4346
4686
  name: text2(value.bot?.name, `${channel4}\u673A\u5668\u4EBA`, 100),
4347
4687
  username: text2(value.bot?.username, "", 100),
@@ -4379,7 +4719,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
4379
4719
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
4380
4720
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
4381
4721
  });
4382
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult11, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4722
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult12, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4383
4723
  }
4384
4724
 
4385
4725
  // plugin-src/client/channels/discord/api.js
@@ -4489,7 +4829,8 @@ function createTokenChannelSettings(definition) {
4489
4829
  channel: channel4.toLowerCase(),
4490
4830
  botId: account.botId,
4491
4831
  botName: account.bot.name,
4492
- connected: account.connected
4832
+ connected: account.connected,
4833
+ accessPolicy: account.accessPolicy
4493
4834
  })
4494
4835
  )
4495
4836
  ),
@@ -4925,6 +5266,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
4925
5266
  setWorkspace: "bot.workspace.set",
4926
5267
  setAgentPreset: "bot.preset.set",
4927
5268
  setContextEnhancement: "bot.context-enhancement.set",
5269
+ setAccessPolicy: "bot.access-policy.set",
4928
5270
  setGroupResponseMode: "bot.group-response-mode.set",
4929
5271
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
4930
5272
  testConnection: "connection.test",
@@ -5074,6 +5416,7 @@ function normalizeBotConnection(value, fallbackBotId) {
5074
5416
  workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
5075
5417
  agentPreset: normalizeAgentPresetId(value.agentPreset),
5076
5418
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
5419
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
5077
5420
  groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
5078
5421
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
5079
5422
  bot: normalizeBot2(value.bot),
@@ -5807,7 +6150,8 @@ function BotCard({
5807
6150
  channel: "feishu",
5808
6151
  botId: connection.botId,
5809
6152
  botName: bot.name,
5810
- connected
6153
+ connected,
6154
+ accessPolicy: connection.accessPolicy
5811
6155
  })
5812
6156
  )
5813
6157
  ),
@@ -7280,7 +7624,8 @@ var QQ_ENDPOINTS = Object.freeze({
7280
7624
  deleteBot: "bot.delete",
7281
7625
  setWorkspace: "bot.workspace.set",
7282
7626
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
7283
- setContextEnhancement: "bot.context-enhancement.set"
7627
+ setContextEnhancement: "bot.context-enhancement.set",
7628
+ setAccessPolicy: "bot.access-policy.set"
7284
7629
  });
7285
7630
  var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
7286
7631
  var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -7346,6 +7691,7 @@ function normalizeBot3(value) {
7346
7691
  workspace: text3(value.workspace, "", 4096),
7347
7692
  agentPreset: normalizeAgentPresetId(value.agentPreset),
7348
7693
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
7694
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
7349
7695
  bot: {
7350
7696
  name: text3(value.bot?.name, "QQ\u673A\u5668\u4EBA", 100),
7351
7697
  appIdMasked: text3(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -7686,7 +8032,8 @@ function AccountCard2({
7686
8032
  channel: "qq",
7687
8033
  botId: account.botId,
7688
8034
  botName: account.bot.name,
7689
- connected: account.connected
8035
+ connected: account.connected,
8036
+ accessPolicy: account.accessPolicy
7690
8037
  })
7691
8038
  )
7692
8039
  ),
@@ -8623,59 +8970,18 @@ var SlackAccountCard = channel2.AccountCard;
8623
8970
 
8624
8971
  // plugin-src/client/channels/telegram/api.js
8625
8972
  var TELEGRAM_RPC_CHANNEL = "/telegram";
8626
- var TELEGRAM_ENDPOINTS = Object.freeze({
8627
- ...TOKEN_BOT_ENDPOINTS,
8628
- setAccessPolicy: "bot.access-policy.set"
8629
- });
8630
- var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2", {
8631
- normalizeBotExtension: (value) => {
8632
- const source = value?.accessPolicy;
8633
- const accessMode = source?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible";
8634
- const allowedUsers = Array.isArray(source?.allowedUsers) ? [...new Set(source.allowedUsers.filter((entry) => typeof entry === "string" && /^[1-9]\d{0,15}$/.test(entry)))] : [];
8635
- return { accessPolicy: { accessMode, allowedUsers } };
8636
- }
8637
- });
8973
+ var TELEGRAM_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
8974
+ var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2");
8638
8975
  var unwrapRpcResult6 = api3.unwrapRpcResult;
8639
8976
  var normalizeSnapshot5 = api3.normalizeSnapshot;
8640
8977
  var presentError6 = api3.presentError;
8641
8978
 
8642
- // plugin-src/client/channels/telegram/index.js
8643
- var React17 = __toESM(require("react"), 1);
8644
-
8645
8979
  // plugin-src/client/channels/telegram/styles.js
8646
8980
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
8647
8981
  var CSS7 = String.raw`
8648
8982
  .dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
8649
8983
  .dtg-avatar { color: #fff; background: #229ed9; }
8650
8984
  .dtg-avatar svg { display: block; }
8651
- .dtg-access { min-width: 0; width: 100%; max-width: 100%; display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
8652
- .dtg-accessHeading { position: relative; min-width: 0; max-width: 100%; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 12px; }
8653
- .dtg-accessHeading > strong { min-width: 0; font-size: 13px; overflow-wrap: anywhere; }
8654
- .dtg-accessStatus { min-width: 0; max-width: 100%; flex: 0 1 auto; display: inline-flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px; }
8655
- .dtg-accessBadge { min-width: 0; max-width: 100%; flex: 0 1 auto; padding: 3px 8px; border-radius: 999px; color: #1687bd; background: #eaf7fd; font-size: 11px; font-weight: 700; overflow-wrap: anywhere; text-align: center; }
8656
- .dtg-accessBadge[data-mode="private-allowlist"] { color: #a15c00; background: #fff3d6; }
8657
- .dtg-accessHelp { position: static; display: inline-flex; flex: none; }
8658
- .dtg-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #229ed9 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1687bd; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
8659
- .dtg-accessHelpButton:hover { border-color: #229ed9; color: #1178a8; background: #eaf7fd; }
8660
- .dtg-accessHelpButton:focus-visible { outline: none; border-color: #229ed9; box-shadow: 0 0 0 3px color-mix(in srgb, #229ed9 18%, transparent); }
8661
- .dtg-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: min(300px, 100%); max-width: 100%; display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
8662
- .dtg-accessTooltipItem { display: grid; gap: 2px; }
8663
- .dtg-accessTooltipItem + .dtg-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
8664
- .dtg-accessTooltipItem strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 17px; font-weight: 700; }
8665
- .dtg-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; font-weight: 400; }
8666
- .dtg-accessHelp:hover .dtg-accessTooltip, .dtg-accessHelp:focus-within .dtg-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
8667
- .dtg-accessField { min-width: 0; max-width: 100%; display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
8668
- .dtg-accessField select, .dtg-accessField textarea { min-width: 0; width: 100%; max-width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
8669
- .dtg-accessField select { height: 34px; padding: 0 9px; }
8670
- .dtg-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
8671
- .dtg-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
8672
- .dtg-accessField > span, .dtg-accessField small { overflow-wrap: anywhere; }
8673
- .dtg-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
8674
- .dtg-accessWarning, .dtg-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
8675
- .dtg-accessWarning { color: #a15c00; }
8676
- .dtg-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
8677
- .dtg-accessActions { min-width: 0; max-width: 100%; display: flex; justify-content: flex-end; flex-wrap: wrap; }
8678
- .dtg-accessActions .ddt-button { max-width: 100%; white-space: normal; }
8679
8985
  `;
8680
8986
  function installTelegramStyles() {
8681
8987
  if (typeof document === "undefined") return () => {
@@ -8692,146 +8998,6 @@ function installTelegramStyles() {
8692
8998
  }
8693
8999
 
8694
9000
  // plugin-src/client/channels/telegram/index.js
8695
- function policyFor(account) {
8696
- return {
8697
- accessMode: account?.accessPolicy?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible",
8698
- allowedUsers: Array.isArray(account?.accessPolicy?.allowedUsers) ? account.accessPolicy.allowedUsers : []
8699
- };
8700
- }
8701
- function allowedUsersFromText(value) {
8702
- const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
8703
- if (entries.some((entry) => !/^[1-9]\d{0,15}$/.test(entry))) {
8704
- throw new TypeError("User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
8705
- }
8706
- return [...new Set(entries)];
8707
- }
8708
- function TelegramAccessSettings({ account, busy = false, onSave }) {
8709
- const policy = policyFor(account);
8710
- const sourceUsers = policy.allowedUsers.join("\n");
8711
- const accessHelpId = React17.useId();
8712
- const [accessMode, setAccessMode] = React17.useState(policy.accessMode);
8713
- const [allowedUsers, setAllowedUsers] = React17.useState(sourceUsers);
8714
- const [error, setError] = React17.useState(null);
8715
- React17.useEffect(() => {
8716
- setAccessMode(policy.accessMode);
8717
- setAllowedUsers(sourceUsers);
8718
- setError(null);
8719
- }, [policy.accessMode, sourceUsers]);
8720
- const save = async (event) => {
8721
- event.preventDefault();
8722
- setError(null);
8723
- try {
8724
- const normalized = allowedUsersFromText(allowedUsers);
8725
- if (typeof onSave !== "function") throw new Error("Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
8726
- await onSave({ accessMode, allowedUsers: normalized });
8727
- } catch (caught) {
8728
- setError(caught?.message ?? "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
8729
- }
8730
- };
8731
- const privateAllowlist = accessMode === "private-allowlist";
8732
- const savedPrivateAllowlist = policy.accessMode === "private-allowlist";
8733
- const emptyAllowlist = privateAllowlist && allowedUsers.trim() === "";
8734
- return h2(
8735
- "form",
8736
- { className: "dtg-access", onSubmit: save },
8737
- h2(
8738
- "div",
8739
- { className: "dtg-accessHeading" },
8740
- h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
8741
- h2(
8742
- "span",
8743
- { className: "dtg-accessStatus" },
8744
- h2(
8745
- "span",
8746
- { className: "dtg-accessBadge", "data-mode": policy.accessMode },
8747
- savedPrivateAllowlist ? "\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F" : "\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F"
8748
- ),
8749
- h2(
8750
- "span",
8751
- { className: "dtg-accessHelp" },
8752
- h2("button", {
8753
- type: "button",
8754
- className: "dtg-accessHelpButton",
8755
- "aria-label": "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
8756
- "aria-describedby": accessHelpId
8757
- }, h2("span", { "aria-hidden": "true" }, "?")),
8758
- h2(
8759
- "span",
8760
- {
8761
- id: accessHelpId,
8762
- className: "dtg-accessTooltip",
8763
- role: "tooltip"
8764
- },
8765
- h2(
8766
- "span",
8767
- { className: "dtg-accessTooltipItem" },
8768
- h2("strong", null, "\u517C\u5BB9\u6A21\u5F0F"),
8769
- h2("span", null, "\u4FDD\u6301\u539F\u6709\u884C\u4E3A\uFF1A\u79C1\u804A\u76F4\u63A5\u54CD\u5E94\uFF0C\u7FA4\u804A\u5728\u88AB\u63D0\u53CA\u6216\u56DE\u590D\u65F6\u54CD\u5E94\u3002")
8770
- ),
8771
- h2(
8772
- "span",
8773
- { className: "dtg-accessTooltipItem" },
8774
- h2("strong", null, "\u5B89\u5168\u6A21\u5F0F"),
8775
- h2("span", null, "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002")
8776
- )
8777
- )
8778
- )
8779
- )
8780
- ),
8781
- h2(
8782
- "label",
8783
- { className: "dtg-accessField" },
8784
- h2("span", null, "\u6A21\u5F0F"),
8785
- h2(
8786
- "select",
8787
- {
8788
- value: accessMode,
8789
- disabled: busy,
8790
- "aria-label": "Telegram \u8BBF\u95EE\u6A21\u5F0F",
8791
- onChange: (event) => {
8792
- setAccessMode(event.target.value);
8793
- setError(null);
8794
- }
8795
- },
8796
- h2("option", { value: "compatible" }, "\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
8797
- h2("option", { value: "private-allowlist" }, "\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09")
8798
- )
8799
- ),
8800
- h2(
8801
- "label",
8802
- { className: "dtg-accessField" },
8803
- h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID"),
8804
- h2("textarea", {
8805
- value: allowedUsers,
8806
- disabled: busy || !privateAllowlist,
8807
- rows: 3,
8808
- placeholder: "\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID",
8809
- "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID",
8810
- onChange: (event) => {
8811
- setAllowedUsers(event.target.value);
8812
- setError(null);
8813
- }
8814
- }),
8815
- h2("small", null, privateAllowlist ? "\u767D\u540D\u5355\u4EC5\u5C5E\u4E8E\u5F53\u524D\u673A\u5668\u4EBA\u3002" : "\u517C\u5BB9\u6A21\u5F0F\u4E0B\u6682\u4E0D\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002")
8816
- ),
8817
- emptyAllowlist ? h2(
8818
- "p",
8819
- { className: "dtg-accessWarning", role: "status" },
8820
- "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002"
8821
- ) : null,
8822
- error ? h2("p", { className: "dtg-accessError", role: "alert" }, error) : null,
8823
- h2(
8824
- "div",
8825
- { className: "dtg-accessActions" },
8826
- h2("button", {
8827
- type: "submit",
8828
- className: "ddt-button",
8829
- "data-kind": "secondary",
8830
- disabled: busy
8831
- }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
8832
- )
8833
- );
8834
- }
8835
9001
  var channel3 = createTokenChannelSettings({
8836
9002
  channel: "Telegram",
8837
9003
  endpoints: TELEGRAM_ENDPOINTS,
@@ -8844,9 +9010,7 @@ var channel3 = createTokenChannelSettings({
8844
9010
  tokenPlaceholder: "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token",
8845
9011
  emptyTitle: "\u63A5\u5165 Telegram \u673A\u5668\u4EBA",
8846
9012
  emptyDescription: "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002",
8847
- platformLabel: "Telegram",
8848
- AccountSettings: TelegramAccessSettings,
8849
- accountSettingsEndpoint: TELEGRAM_ENDPOINTS.setAccessPolicy
9013
+ platformLabel: "Telegram"
8850
9014
  });
8851
9015
  var TelegramSettingsTab = channel3.SettingsTab;
8852
9016
  var TelegramAccountCard = channel3.AccountCard;
@@ -8863,7 +9027,8 @@ var WECOM_ENDPOINTS = Object.freeze({
8863
9027
  deleteBot: "bot.delete",
8864
9028
  setWorkspace: "bot.workspace.set",
8865
9029
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
8866
- setContextEnhancement: "bot.context-enhancement.set"
9030
+ setContextEnhancement: "bot.context-enhancement.set",
9031
+ setAccessPolicy: "bot.access-policy.set"
8867
9032
  });
8868
9033
  var PROVISION_STATES3 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
8869
9034
  var ACCOUNT_STATES4 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -8935,6 +9100,7 @@ function normalizeBot4(value) {
8935
9100
  workspace: text4(value.workspace, "", 4096),
8936
9101
  agentPreset: normalizeAgentPresetId(value.agentPreset),
8937
9102
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
9103
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
8938
9104
  bot: {
8939
9105
  name: text4(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA", 100),
8940
9106
  appIdMasked: text4(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -8975,7 +9141,7 @@ function formatRemaining4(milliseconds) {
8975
9141
  }
8976
9142
 
8977
9143
  // plugin-src/client/channels/wecom/index.js
8978
- var React18 = __toESM(require("react"), 1);
9144
+ var React17 = __toESM(require("react"), 1);
8979
9145
 
8980
9146
  // plugin-src/client/channels/wecom/styles.js
8981
9147
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -9000,7 +9166,7 @@ function installWecomStyles() {
9000
9166
 
9001
9167
  // plugin-src/client/channels/wecom/index.js
9002
9168
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
9003
- var Button10 = React18.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
9169
+ var Button10 = React17.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
9004
9170
  return h2("button", {
9005
9171
  ...props,
9006
9172
  ref,
@@ -9258,7 +9424,8 @@ function AccountCard3({
9258
9424
  channel: "wecom",
9259
9425
  botId: account.botId,
9260
9426
  botName: account.bot.name,
9261
- connected: account.connected
9427
+ connected: account.connected,
9428
+ accessPolicy: account.accessPolicy
9262
9429
  })
9263
9430
  )
9264
9431
  ),
@@ -9311,27 +9478,27 @@ function AccountCard3({
9311
9478
  );
9312
9479
  }
9313
9480
  function WecomSettingsTab({ rpcCall }) {
9314
- const [model, setModel] = React18.useState({
9481
+ const [model, setModel] = React17.useState({
9315
9482
  phase: "loading",
9316
9483
  bots: [],
9317
9484
  totals: { configured: 0, connected: 0 },
9318
9485
  error: null,
9319
9486
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
9320
9487
  });
9321
- const [provision, setProvision] = React18.useState(null);
9322
- const [busy, setBusy] = React18.useState(false);
9323
- const [busyByBot, setBusyByBot] = React18.useState({});
9324
- const [feedbackByBot, setFeedbackByBot] = React18.useState({});
9325
- const [removeTarget, setRemoveTarget] = React18.useState(null);
9326
- const [credentialOpen, setCredentialOpen] = React18.useState(false);
9327
- const [credentialError, setCredentialError] = React18.useState(null);
9328
- const [notice, setNotice] = React18.useState("");
9329
- const [now, setNow] = React18.useState(Date.now());
9330
- const mounted = React18.useRef(true);
9488
+ const [provision, setProvision] = React17.useState(null);
9489
+ const [busy, setBusy] = React17.useState(false);
9490
+ const [busyByBot, setBusyByBot] = React17.useState({});
9491
+ const [feedbackByBot, setFeedbackByBot] = React17.useState({});
9492
+ const [removeTarget, setRemoveTarget] = React17.useState(null);
9493
+ const [credentialOpen, setCredentialOpen] = React17.useState(false);
9494
+ const [credentialError, setCredentialError] = React17.useState(null);
9495
+ const [notice, setNotice] = React17.useState("");
9496
+ const [now, setNow] = React17.useState(Date.now());
9497
+ const mounted = React17.useRef(true);
9331
9498
  const workspaceFence = useWorkspaceSnapshotFence();
9332
- const addButtonRef = React18.useRef(null);
9333
- const noticeFrameRef = React18.useRef(null);
9334
- const announce = React18.useCallback((message) => {
9499
+ const addButtonRef = React17.useRef(null);
9500
+ const noticeFrameRef = React17.useRef(null);
9501
+ const announce = React17.useCallback((message) => {
9335
9502
  if (!mounted.current) return;
9336
9503
  if (noticeFrameRef.current !== null) {
9337
9504
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -9345,7 +9512,7 @@ function WecomSettingsTab({ rpcCall }) {
9345
9512
  });
9346
9513
  }
9347
9514
  }, []);
9348
- React18.useEffect(() => {
9515
+ React17.useEffect(() => {
9349
9516
  const disposeDingtalk = installDingtalkStyles();
9350
9517
  const disposeWecom = installWecomStyles();
9351
9518
  mounted.current = true;
@@ -9359,11 +9526,11 @@ function WecomSettingsTab({ rpcCall }) {
9359
9526
  disposeDingtalk();
9360
9527
  };
9361
9528
  }, []);
9362
- const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
9529
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
9363
9530
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
9364
9531
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
9365
9532
  }, [rpcCall]);
9366
- const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9533
+ const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9367
9534
  const workspaceVersion = workspaceFence.beginStatus();
9368
9535
  if (workspaceVersion === null) return void 0;
9369
9536
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -9389,12 +9556,12 @@ function WecomSettingsTab({ rpcCall }) {
9389
9556
  return void 0;
9390
9557
  }
9391
9558
  }, [invoke, workspaceFence]);
9392
- React18.useEffect(() => {
9559
+ React17.useEffect(() => {
9393
9560
  const controller = new AbortController();
9394
9561
  void loadStatus({ signal: controller.signal, restore: true });
9395
9562
  return () => controller.abort();
9396
9563
  }, [loadStatus]);
9397
- React18.useEffect(() => {
9564
+ React17.useEffect(() => {
9398
9565
  if (model.phase !== "ready") return void 0;
9399
9566
  const controller = new AbortController();
9400
9567
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -9403,12 +9570,12 @@ function WecomSettingsTab({ rpcCall }) {
9403
9570
  window.clearInterval(timer);
9404
9571
  };
9405
9572
  }, [loadStatus, model.phase]);
9406
- React18.useEffect(() => {
9573
+ React17.useEffect(() => {
9407
9574
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
9408
9575
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
9409
9576
  return () => window.clearInterval(timer);
9410
9577
  }, [provision?.attemptId, provision?.status]);
9411
- const startProvisioning = React18.useCallback(async (replace = false) => {
9578
+ const startProvisioning = React17.useCallback(async (replace = false) => {
9412
9579
  setCredentialOpen(false);
9413
9580
  setCredentialError(null);
9414
9581
  setBusy(true);
@@ -9426,7 +9593,7 @@ function WecomSettingsTab({ rpcCall }) {
9426
9593
  if (mounted.current) setBusy(false);
9427
9594
  }
9428
9595
  }, [invoke, provision?.attemptId]);
9429
- const bindCredentials = React18.useCallback(async ({ identity, secret }) => {
9596
+ const bindCredentials = React17.useCallback(async ({ identity, secret }) => {
9430
9597
  const snapshotVersion = workspaceFence.beginMutation();
9431
9598
  setBusy(true);
9432
9599
  setCredentialError(null);
@@ -9454,7 +9621,7 @@ function WecomSettingsTab({ rpcCall }) {
9454
9621
  if (mounted.current) setBusy(false);
9455
9622
  }
9456
9623
  }, [invoke, loadStatus, workspaceFence]);
9457
- const closeProvision = React18.useCallback(async () => {
9624
+ const closeProvision = React17.useCallback(async () => {
9458
9625
  setBusy(true);
9459
9626
  try {
9460
9627
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -9465,7 +9632,7 @@ function WecomSettingsTab({ rpcCall }) {
9465
9632
  if (mounted.current) setBusy(false);
9466
9633
  }
9467
9634
  }, [invoke, provision?.attemptId, provision?.status]);
9468
- React18.useEffect(() => {
9635
+ React17.useEffect(() => {
9469
9636
  const attemptId = provision?.attemptId;
9470
9637
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
9471
9638
  const controller = new AbortController();
@@ -9495,7 +9662,7 @@ function WecomSettingsTab({ rpcCall }) {
9495
9662
  window.clearTimeout(timer);
9496
9663
  };
9497
9664
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
9498
- const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
9665
+ const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
9499
9666
  const snapshotVersion = workspaceFence.beginMutation();
9500
9667
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
9501
9668
  try {
@@ -9520,7 +9687,7 @@ function WecomSettingsTab({ rpcCall }) {
9520
9687
  });
9521
9688
  }
9522
9689
  }, [invoke, loadStatus, workspaceFence]);
9523
- const reconnect = React18.useCallback(async (account) => {
9690
+ const reconnect = React17.useCallback(async (account) => {
9524
9691
  setFeedbackByBot((current) => {
9525
9692
  const next = { ...current };
9526
9693
  delete next[account.botId];
@@ -9647,7 +9814,7 @@ function WecomSettingsTab({ rpcCall }) {
9647
9814
  }),
9648
9815
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
9649
9816
  model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
9650
- React18.Fragment,
9817
+ React17.Fragment,
9651
9818
  null,
9652
9819
  credentialView,
9653
9820
  provisionView,
@@ -9658,7 +9825,7 @@ function WecomSettingsTab({ rpcCall }) {
9658
9825
  }
9659
9826
 
9660
9827
  // plugin-src/client/channels/weixin/index.js
9661
- var React19 = __toESM(require("react"), 1);
9828
+ var React18 = __toESM(require("react"), 1);
9662
9829
 
9663
9830
  // plugin-src/client/channels/weixin/api.js
9664
9831
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -9672,7 +9839,8 @@ var WEIXIN_ENDPOINTS = Object.freeze({
9672
9839
  deleteBot: "bot.delete",
9673
9840
  setWorkspace: "bot.workspace.set",
9674
9841
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
9675
- setContextEnhancement: "bot.context-enhancement.set"
9842
+ setContextEnhancement: "bot.context-enhancement.set",
9843
+ setAccessPolicy: "bot.access-policy.set"
9676
9844
  });
9677
9845
  var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
9678
9846
  var PROVISION_STATES4 = /* @__PURE__ */ new Set([
@@ -9764,6 +9932,7 @@ function normalizeBot5(value) {
9764
9932
  workspace: string(value.workspace).slice(0, 4096),
9765
9933
  agentPreset: normalizeAgentPresetId(value.agentPreset),
9766
9934
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
9935
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
9767
9936
  bot: {
9768
9937
  name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
9769
9938
  accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
@@ -9815,7 +9984,7 @@ function formatRemaining5(milliseconds) {
9815
9984
  }
9816
9985
 
9817
9986
  // plugin-src/client/channels/weixin/index.js
9818
- var Button12 = React19.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
9987
+ var Button12 = React18.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
9819
9988
  return h2("button", {
9820
9989
  ...props,
9821
9990
  ref,
@@ -9888,14 +10057,14 @@ function EmptyView5({ onStart, busy }) {
9888
10057
  );
9889
10058
  }
9890
10059
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
9891
- const [imageFailed, setImageFailed] = React19.useState(false);
10060
+ const [imageFailed, setImageFailed] = React18.useState(false);
9892
10061
  const source = safeQrSource5(provision.qrCodeDataUrl);
9893
10062
  const href = safeVerificationUrl(provision.verificationUrl);
9894
10063
  const remaining = Math.max(0, provision.expiresAt - now);
9895
10064
  const expired = remaining === 0 || provision.status === "expired";
9896
10065
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
9897
10066
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
9898
- React19.useEffect(() => setImageFailed(false), [source]);
10067
+ React18.useEffect(() => setImageFailed(false), [source]);
9899
10068
  return h2(
9900
10069
  "div",
9901
10070
  { className: "dxw-card dim-surfaceCard" },
@@ -9962,9 +10131,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
9962
10131
  );
9963
10132
  }
9964
10133
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
9965
- const [code, setCode] = React19.useState("");
10134
+ const [code, setCode] = React18.useState("");
9966
10135
  const valid = /^\d{4,8}$/.test(code);
9967
- React19.useEffect(() => setCode(""), [provision.attemptId]);
10136
+ React18.useEffect(() => setCode(""), [provision.attemptId]);
9968
10137
  return h2(
9969
10138
  "div",
9970
10139
  { className: "dxw-card dim-surfaceCard" },
@@ -10101,7 +10270,8 @@ function AccountCard4({
10101
10270
  channel: "weixin",
10102
10271
  botId: account.botId,
10103
10272
  botName: account.bot.name,
10104
- connected: account.connected
10273
+ connected: account.connected,
10274
+ accessPolicy: account.accessPolicy
10105
10275
  })
10106
10276
  )
10107
10277
  ),
@@ -10207,7 +10377,7 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
10207
10377
  };
10208
10378
  }
10209
10379
  function WeixinSettingsTab({ rpcCall }) {
10210
- const [model, setModel] = React19.useState({
10380
+ const [model, setModel] = React18.useState({
10211
10381
  phase: "loading",
10212
10382
  bots: [],
10213
10383
  totals: EMPTY_TOTALS3,
@@ -10215,33 +10385,33 @@ function WeixinSettingsTab({ rpcCall }) {
10215
10385
  error: null,
10216
10386
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
10217
10387
  });
10218
- const [provision, setProvision] = React19.useState(null);
10219
- const [busy, setBusy] = React19.useState(false);
10220
- const [busyByBot, setBusyByBot] = React19.useState({});
10221
- const [feedbackByBot, setFeedbackByBot] = React19.useState({});
10222
- const [removeTarget, setRemoveTarget] = React19.useState(null);
10223
- const [notice, setNotice] = React19.useState("");
10224
- const [now, setNow] = React19.useState(() => Date.now());
10225
- const addButtonRef = React19.useRef(null);
10226
- const mountedRef = React19.useRef(true);
10388
+ const [provision, setProvision] = React18.useState(null);
10389
+ const [busy, setBusy] = React18.useState(false);
10390
+ const [busyByBot, setBusyByBot] = React18.useState({});
10391
+ const [feedbackByBot, setFeedbackByBot] = React18.useState({});
10392
+ const [removeTarget, setRemoveTarget] = React18.useState(null);
10393
+ const [notice, setNotice] = React18.useState("");
10394
+ const [now, setNow] = React18.useState(() => Date.now());
10395
+ const addButtonRef = React18.useRef(null);
10396
+ const mountedRef = React18.useRef(true);
10227
10397
  const workspaceFence = useWorkspaceSnapshotFence();
10228
10398
  const scheduleAnimationFrame = useAnimationFrameScheduler();
10229
- React19.useEffect(() => {
10399
+ React18.useEffect(() => {
10230
10400
  mountedRef.current = true;
10231
10401
  return () => {
10232
10402
  mountedRef.current = false;
10233
10403
  };
10234
10404
  }, []);
10235
- const announce = React19.useCallback((value) => {
10405
+ const announce = React18.useCallback((value) => {
10236
10406
  setNotice("");
10237
10407
  scheduleAnimationFrame(() => {
10238
10408
  if (value) setNotice(value);
10239
10409
  }, "announcement");
10240
10410
  }, [scheduleAnimationFrame]);
10241
- const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
10411
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
10242
10412
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
10243
10413
  }, [rpcCall]);
10244
- const loadStatus = React19.useCallback(async ({
10414
+ const loadStatus = React18.useCallback(async ({
10245
10415
  signal,
10246
10416
  silent = false,
10247
10417
  restoreProvisioning = false
@@ -10278,12 +10448,12 @@ function WeixinSettingsTab({ rpcCall }) {
10278
10448
  return void 0;
10279
10449
  }
10280
10450
  }, [invoke, workspaceFence]);
10281
- React19.useEffect(() => {
10451
+ React18.useEffect(() => {
10282
10452
  const controller = new AbortController();
10283
10453
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
10284
10454
  return () => controller.abort();
10285
10455
  }, [loadStatus]);
10286
- React19.useEffect(() => {
10456
+ React18.useEffect(() => {
10287
10457
  if (model.phase !== "ready") return void 0;
10288
10458
  const controller = new AbortController();
10289
10459
  let running = false;
@@ -10302,12 +10472,12 @@ function WeixinSettingsTab({ rpcCall }) {
10302
10472
  window.clearInterval(timer);
10303
10473
  };
10304
10474
  }, [loadStatus, model.phase]);
10305
- React19.useEffect(() => {
10475
+ React18.useEffect(() => {
10306
10476
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
10307
10477
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
10308
10478
  return () => window.clearInterval(timer);
10309
10479
  }, [provision?.attemptId, provision?.status]);
10310
- const startProvisioning = React19.useCallback(async ({ replace = false } = {}) => {
10480
+ const startProvisioning = React18.useCallback(async ({ replace = false } = {}) => {
10311
10481
  setBusy(true);
10312
10482
  try {
10313
10483
  if (replace && provision?.attemptId) {
@@ -10328,7 +10498,7 @@ function WeixinSettingsTab({ rpcCall }) {
10328
10498
  setBusy(false);
10329
10499
  }
10330
10500
  }, [announce, invoke, provision?.attemptId]);
10331
- const cancelProvisioning = React19.useCallback(async () => {
10501
+ const cancelProvisioning = React18.useCallback(async () => {
10332
10502
  setBusy(true);
10333
10503
  try {
10334
10504
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -10343,7 +10513,7 @@ function WeixinSettingsTab({ rpcCall }) {
10343
10513
  setBusy(false);
10344
10514
  }
10345
10515
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
10346
- const submitVerification = React19.useCallback(async (verifyCode) => {
10516
+ const submitVerification = React18.useCallback(async (verifyCode) => {
10347
10517
  if (!provision?.attemptId) return;
10348
10518
  setBusy(true);
10349
10519
  try {
@@ -10359,7 +10529,7 @@ function WeixinSettingsTab({ rpcCall }) {
10359
10529
  setBusy(false);
10360
10530
  }
10361
10531
  }, [announce, invoke, provision?.attemptId]);
10362
- React19.useEffect(() => {
10532
+ React18.useEffect(() => {
10363
10533
  const attemptId = provision?.attemptId;
10364
10534
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
10365
10535
  const controller = new AbortController();
@@ -10407,7 +10577,7 @@ function WeixinSettingsTab({ rpcCall }) {
10407
10577
  controller.abort();
10408
10578
  };
10409
10579
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
10410
- const setBotBusy = React19.useCallback((botId, value) => {
10580
+ const setBotBusy = React18.useCallback((botId, value) => {
10411
10581
  setBusyByBot((current) => {
10412
10582
  const next = { ...current };
10413
10583
  if (value) next[botId] = value;
@@ -10415,7 +10585,7 @@ function WeixinSettingsTab({ rpcCall }) {
10415
10585
  return next;
10416
10586
  });
10417
10587
  }, []);
10418
- const reconnect = React19.useCallback(async (account) => {
10588
+ const reconnect = React18.useCallback(async (account) => {
10419
10589
  const snapshotVersion = workspaceFence.beginMutation();
10420
10590
  setBotBusy(account.botId, "reconnect");
10421
10591
  setFeedbackByBot((current) => {
@@ -10466,7 +10636,7 @@ function WeixinSettingsTab({ rpcCall }) {
10466
10636
  setBotBusy(account.botId, null);
10467
10637
  }
10468
10638
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
10469
- const saveWorkspace = React19.useCallback(async (account, workspace) => {
10639
+ const saveWorkspace = React18.useCallback(async (account, workspace) => {
10470
10640
  const workspaceVersion = workspaceFence.beginMutation();
10471
10641
  setBotBusy(account.botId, "workspace");
10472
10642
  try {
@@ -10490,7 +10660,7 @@ function WeixinSettingsTab({ rpcCall }) {
10490
10660
  if (mountedRef.current) setBotBusy(account.botId, null);
10491
10661
  }
10492
10662
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10493
- const saveBotSetting = React19.useCallback(async (account, operation, endpoint, payload) => {
10663
+ const saveBotSetting = React18.useCallback(async (account, operation, endpoint, payload) => {
10494
10664
  const snapshotVersion = workspaceFence.beginMutation();
10495
10665
  setBotBusy(account.botId, operation);
10496
10666
  try {
@@ -10514,7 +10684,7 @@ function WeixinSettingsTab({ rpcCall }) {
10514
10684
  if (mountedRef.current) setBotBusy(account.botId, null);
10515
10685
  }
10516
10686
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10517
- const remove = React19.useCallback(async (account) => {
10687
+ const remove = React18.useCallback(async (account) => {
10518
10688
  const snapshotVersion = workspaceFence.beginMutation();
10519
10689
  setBotBusy(account.botId, "delete");
10520
10690
  try {
@@ -10598,7 +10768,7 @@ function WeixinSettingsTab({ rpcCall }) {
10598
10768
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
10599
10769
  )
10600
10770
  ) : h2(
10601
- React19.Fragment,
10771
+ React18.Fragment,
10602
10772
  null,
10603
10773
  provisionView,
10604
10774
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -10825,12 +10995,7 @@ function normalizeBot6(value) {
10825
10995
  workspace: text5(value.workspace, "", 4096),
10826
10996
  agentPreset: normalizeAgentPresetId(value.agentPreset),
10827
10997
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
10828
- accessPolicy: {
10829
- accessMode: ["self-only", "private-allowlist", "open"].includes(
10830
- value.accessPolicy?.accessMode
10831
- ) ? value.accessPolicy.accessMode : "self-only",
10832
- allowedNumbers: Array.isArray(value.accessPolicy?.allowedNumbers) ? [...new Set(value.accessPolicy.allowedNumbers.filter((entry) => typeof entry === "string" && /^[1-9]\d{4,14}$/.test(entry)))] : []
10833
- },
10998
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
10834
10999
  bot: {
10835
11000
  name: text5(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
10836
11001
  idMasked: text5(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
@@ -10872,7 +11037,7 @@ function formatRemaining6(milliseconds) {
10872
11037
  }
10873
11038
 
10874
11039
  // plugin-src/client/channels/whatsapp/index.js
10875
- var React20 = __toESM(require("react"), 1);
11040
+ var React19 = __toESM(require("react"), 1);
10876
11041
 
10877
11042
  // plugin-src/client/channels/whatsapp/styles.js
10878
11043
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -10880,31 +11045,6 @@ var CSS10 = String.raw`
10880
11045
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
10881
11046
  .dwa-avatar { color: #fff; background: #25d366; }
10882
11047
  .dwa-avatar svg { display: block; }
10883
- .dwa-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
10884
- .dwa-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
10885
- .dwa-accessHeading > strong { font-size: 13px; }
10886
- .dwa-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
10887
- .dwa-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #08785f; background: #eafbf0; font-size: 11px; font-weight: 700; }
10888
- .dwa-accessBadge[data-mode="private-allowlist"] { color: #0f6f8f; background: #eaf7fd; }
10889
- .dwa-accessBadge[data-mode="open"] { color: #a15c00; background: #fff3d6; }
10890
- .dwa-accessHelp { position: relative; display: inline-flex; flex: none; }
10891
- .dwa-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #25d366 34%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #128c7e; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; }
10892
- .dwa-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 270px; max-width: min(290px, calc(100vw - 48px)); display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
10893
- .dwa-accessTooltipItem { display: grid; gap: 2px; }
10894
- .dwa-accessTooltipItem + .dwa-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
10895
- .dwa-accessTooltipItem strong { font-size: 12px; line-height: 17px; }
10896
- .dwa-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
10897
- .dwa-accessHelp:hover .dwa-accessTooltip, .dwa-accessHelp:focus-within .dwa-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
10898
- .dwa-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
10899
- .dwa-accessField select, .dwa-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
10900
- .dwa-accessField select { height: 34px; padding: 0 9px; }
10901
- .dwa-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
10902
- .dwa-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
10903
- .dwa-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
10904
- .dwa-accessWarning, .dwa-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
10905
- .dwa-accessWarning { color: #a15c00; }
10906
- .dwa-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
10907
- .dwa-accessActions { display: flex; justify-content: flex-end; }
10908
11048
  `;
10909
11049
  function installWhatsappStyles() {
10910
11050
  if (typeof document === "undefined") return () => {
@@ -10922,157 +11062,7 @@ function installWhatsappStyles() {
10922
11062
 
10923
11063
  // plugin-src/client/channels/whatsapp/index.js
10924
11064
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
10925
- function accessPolicyFor(account) {
10926
- const accessMode = ["self-only", "private-allowlist", "open"].includes(
10927
- account?.accessPolicy?.accessMode
10928
- ) ? account.accessPolicy.accessMode : "self-only";
10929
- return {
10930
- accessMode,
10931
- allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers) ? account.accessPolicy.allowedNumbers : []
10932
- };
10933
- }
10934
- function allowedNumbersFromText(value) {
10935
- const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
10936
- const normalized = entries.map((entry) => entry.replace(/^\+/, ""));
10937
- if (normalized.some((entry) => !/^[1-9]\d{4,14}$/.test(entry))) {
10938
- throw new TypeError("\u7535\u8BDD\u53F7\u7801\u5FC5\u987B\u5305\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
10939
- }
10940
- return [...new Set(normalized)];
10941
- }
10942
- function WhatsappAccessSettings({ account, busy = false, onSave }) {
10943
- const policy = accessPolicyFor(account);
10944
- const sourceNumbers = policy.allowedNumbers.join("\n");
10945
- const helpId = React20.useId();
10946
- const [accessMode, setAccessMode] = React20.useState(policy.accessMode);
10947
- const [allowedNumbers, setAllowedNumbers] = React20.useState(sourceNumbers);
10948
- const [error, setError] = React20.useState(null);
10949
- React20.useEffect(() => {
10950
- setAccessMode(policy.accessMode);
10951
- setAllowedNumbers(sourceNumbers);
10952
- setError(null);
10953
- }, [policy.accessMode, sourceNumbers]);
10954
- const save = async (event) => {
10955
- event.preventDefault();
10956
- setError(null);
10957
- try {
10958
- const normalized = allowedNumbersFromText(allowedNumbers);
10959
- if (typeof onSave !== "function") throw new Error("WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
10960
- await onSave({ accessMode, allowedNumbers: normalized });
10961
- } catch (caught) {
10962
- setError(caught?.message ?? "WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
10963
- }
10964
- };
10965
- const allowlistEnabled = accessMode === "private-allowlist";
10966
- const labels = {
10967
- "self-only": "\u4EC5\u81EA\u5DF1\u6A21\u5F0F",
10968
- "private-allowlist": "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F",
10969
- open: "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"
10970
- };
10971
- return h2(
10972
- "form",
10973
- { className: "dwa-access", onSubmit: save },
10974
- h2(
10975
- "div",
10976
- { className: "dwa-accessHeading" },
10977
- h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
10978
- h2(
10979
- "span",
10980
- { className: "dwa-accessStatus" },
10981
- h2(
10982
- "span",
10983
- { className: "dwa-accessBadge", "data-mode": policy.accessMode },
10984
- ["\u5DF2\u751F\u6548\uFF1A", labels[policy.accessMode]]
10985
- ),
10986
- h2(
10987
- "span",
10988
- { className: "dwa-accessHelp" },
10989
- h2("button", {
10990
- type: "button",
10991
- className: "dwa-accessHelpButton",
10992
- "aria-label": "\u67E5\u770B WhatsApp \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
10993
- "aria-describedby": helpId
10994
- }, h2("span", { "aria-hidden": "true" }, "?")),
10995
- h2(
10996
- "span",
10997
- { id: helpId, className: "dwa-accessTooltip", role: "tooltip" },
10998
- h2(
10999
- "span",
11000
- { className: "dwa-accessTooltipItem" },
11001
- h2("strong", null, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F"),
11002
- h2("span", null, "\u53EA\u54CD\u5E94\u5DF2\u7ED1\u5B9A WhatsApp \u8D26\u53F7\u7684\u81EA\u804A\u6D88\u606F\u3002")
11003
- ),
11004
- h2(
11005
- "span",
11006
- { className: "dwa-accessTooltipItem" },
11007
- h2("strong", null, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
11008
- h2("span", null, "\u54CD\u5E94\u81EA\u804A\u548C\u767D\u540D\u5355\u8054\u7CFB\u4EBA\u7684\u79C1\u804A\uFF0C\u5FFD\u7565\u7FA4\u804A\u3002")
11009
- ),
11010
- h2(
11011
- "span",
11012
- { className: "dwa-accessTooltipItem" },
11013
- h2("strong", null, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"),
11014
- h2("span", null, "\u54CD\u5E94\u6240\u6709\u79C1\u804A\u3001\u5DF2\u7ED1\u5B9A\u8D26\u53F7\u81EA\u5DF1\u53D1\u51FA\u7684\u7FA4\u804A\u6D88\u606F\uFF0C\u4EE5\u53CA\u5176\u4ED6\u7FA4\u6210\u5458\u7684\u63D0\u53CA\u6216\u56DE\u590D\u3002")
11015
- )
11016
- )
11017
- )
11018
- )
11019
- ),
11020
- h2(
11021
- "label",
11022
- { className: "dwa-accessField" },
11023
- h2("span", null, "\u6A21\u5F0F"),
11024
- h2(
11025
- "select",
11026
- {
11027
- value: accessMode,
11028
- disabled: busy,
11029
- "aria-label": "WhatsApp \u8BBF\u95EE\u6A21\u5F0F",
11030
- onChange: (event) => {
11031
- setAccessMode(event.target.value);
11032
- setError(null);
11033
- }
11034
- },
11035
- h2("option", { value: "self-only" }, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
11036
- h2("option", { value: "private-allowlist" }, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
11037
- h2("option", { value: "open" }, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F")
11038
- )
11039
- ),
11040
- allowlistEnabled ? h2(
11041
- "label",
11042
- { className: "dwa-accessField" },
11043
- h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801"),
11044
- h2("textarea", {
11045
- value: allowedNumbers,
11046
- disabled: busy,
11047
- rows: 3,
11048
- placeholder: "\u6BCF\u884C\u4E00\u4E2A\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\u7684\u53F7\u7801",
11049
- "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801",
11050
- onChange: (event) => {
11051
- setAllowedNumbers(event.target.value);
11052
- setError(null);
11053
- }
11054
- }),
11055
- h2("small", null, "\u53EF\u4EE5\u5305\u542B\u5F00\u5934\u7684 +\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u81EA\u52A8\u79FB\u9664\u3002")
11056
- ) : null,
11057
- allowlistEnabled && allowedNumbers.trim() === "" ? h2(
11058
- "p",
11059
- { className: "dwa-accessWarning", role: "status" },
11060
- "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u5C06\u53EA\u63A5\u53D7\u81EA\u804A\u6D88\u606F\u3002"
11061
- ) : null,
11062
- error ? h2("p", { className: "dwa-accessError", role: "alert" }, error) : null,
11063
- h2(
11064
- "div",
11065
- { className: "dwa-accessActions" },
11066
- h2("button", {
11067
- type: "submit",
11068
- className: "ddt-button",
11069
- "data-kind": "secondary",
11070
- disabled: busy
11071
- }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
11072
- )
11073
- );
11074
- }
11075
- var Button14 = React20.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11065
+ var Button14 = React19.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11076
11066
  return h2("button", {
11077
11067
  ...props,
11078
11068
  ref,
@@ -11296,7 +11286,6 @@ function WhatsappAccountCard({
11296
11286
  onWorkspaceSave,
11297
11287
  onAgentPresetSave,
11298
11288
  onContextEnhancementSave,
11299
- onAccessPolicySave,
11300
11289
  onRequestRemove,
11301
11290
  onConfirmRemove,
11302
11291
  onCancelRemove
@@ -11343,7 +11332,8 @@ function WhatsappAccountCard({
11343
11332
  channel: "whatsapp",
11344
11333
  botId: account.botId,
11345
11334
  botName: account.bot.name,
11346
- connected: account.connected
11335
+ connected: account.connected,
11336
+ accessPolicy: account.accessPolicy
11347
11337
  })
11348
11338
  )
11349
11339
  ),
@@ -11362,11 +11352,6 @@ function WhatsappAccountCard({
11362
11352
  disabled: Boolean(busy),
11363
11353
  onSave: onContextEnhancementSave
11364
11354
  }),
11365
- h2(WhatsappAccessSettings, {
11366
- account,
11367
- busy: Boolean(busy),
11368
- onSave: onAccessPolicySave
11369
- }),
11370
11355
  h2(
11371
11356
  "div",
11372
11357
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -11409,23 +11394,23 @@ function WhatsappAccountCard({
11409
11394
  );
11410
11395
  }
11411
11396
  function WhatsappSettingsTab({ rpcCall }) {
11412
- const [model, setModel] = React20.useState({
11397
+ const [model, setModel] = React19.useState({
11413
11398
  phase: "loading",
11414
11399
  bots: [],
11415
11400
  totals: { configured: 0, connected: 0 },
11416
11401
  error: null,
11417
11402
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
11418
11403
  });
11419
- const [provision, setProvision] = React20.useState(null);
11420
- const [busy, setBusy] = React20.useState(false);
11421
- const [busyByBot, setBusyByBot] = React20.useState({});
11422
- const [testNoticeByBot, setTestNoticeByBot] = React20.useState({});
11423
- const [removeTarget, setRemoveTarget] = React20.useState(null);
11424
- const [now, setNow] = React20.useState(Date.now());
11425
- const mounted = React20.useRef(true);
11404
+ const [provision, setProvision] = React19.useState(null);
11405
+ const [busy, setBusy] = React19.useState(false);
11406
+ const [busyByBot, setBusyByBot] = React19.useState({});
11407
+ const [testNoticeByBot, setTestNoticeByBot] = React19.useState({});
11408
+ const [removeTarget, setRemoveTarget] = React19.useState(null);
11409
+ const [now, setNow] = React19.useState(Date.now());
11410
+ const mounted = React19.useRef(true);
11426
11411
  const workspaceFence = useWorkspaceSnapshotFence();
11427
- const addButtonRef = React20.useRef(null);
11428
- React20.useEffect(() => {
11412
+ const addButtonRef = React19.useRef(null);
11413
+ React19.useEffect(() => {
11429
11414
  const disposeDingtalk = installDingtalkStyles();
11430
11415
  const disposeWhatsapp = installWhatsappStyles();
11431
11416
  mounted.current = true;
@@ -11435,11 +11420,11 @@ function WhatsappSettingsTab({ rpcCall }) {
11435
11420
  disposeDingtalk();
11436
11421
  };
11437
11422
  }, []);
11438
- const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
11423
+ const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
11439
11424
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
11440
11425
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
11441
11426
  }, [rpcCall]);
11442
- const loadStatus = React20.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11427
+ const loadStatus = React19.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11443
11428
  const workspaceVersion = workspaceFence.beginStatus();
11444
11429
  if (workspaceVersion === null) return void 0;
11445
11430
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -11469,12 +11454,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11469
11454
  return void 0;
11470
11455
  }
11471
11456
  }, [invoke, workspaceFence]);
11472
- React20.useEffect(() => {
11457
+ React19.useEffect(() => {
11473
11458
  const controller = new AbortController();
11474
11459
  void loadStatus({ signal: controller.signal, restore: true });
11475
11460
  return () => controller.abort();
11476
11461
  }, [loadStatus]);
11477
- React20.useEffect(() => {
11462
+ React19.useEffect(() => {
11478
11463
  if (model.phase !== "ready") return void 0;
11479
11464
  const controller = new AbortController();
11480
11465
  const timer = window.setInterval(
@@ -11486,12 +11471,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11486
11471
  window.clearInterval(timer);
11487
11472
  };
11488
11473
  }, [loadStatus, model.phase]);
11489
- React20.useEffect(() => {
11474
+ React19.useEffect(() => {
11490
11475
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
11491
11476
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
11492
11477
  return () => window.clearInterval(timer);
11493
11478
  }, [provision?.attemptId, provision?.status]);
11494
- const startProvisioning = React20.useCallback(async (replace = false) => {
11479
+ const startProvisioning = React19.useCallback(async (replace = false) => {
11495
11480
  setBusy(true);
11496
11481
  try {
11497
11482
  if (replace && provision?.attemptId) {
@@ -11509,7 +11494,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11509
11494
  if (mounted.current) setBusy(false);
11510
11495
  }
11511
11496
  }, [invoke, provision?.attemptId]);
11512
- const closeProvision = React20.useCallback(async () => {
11497
+ const closeProvision = React19.useCallback(async () => {
11513
11498
  setBusy(true);
11514
11499
  try {
11515
11500
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -11520,7 +11505,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11520
11505
  if (mounted.current) setBusy(false);
11521
11506
  }
11522
11507
  }, [invoke, provision?.attemptId, provision?.status]);
11523
- React20.useEffect(() => {
11508
+ React19.useEffect(() => {
11524
11509
  const attemptId = provision?.attemptId;
11525
11510
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
11526
11511
  const controller = new AbortController();
@@ -11561,7 +11546,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11561
11546
  if (timer) window.clearTimeout(timer);
11562
11547
  };
11563
11548
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
11564
- const botAction = React20.useCallback(async (account, operation, endpoint, payload) => {
11549
+ const botAction = React19.useCallback(async (account, operation, endpoint, payload) => {
11565
11550
  const snapshotVersion = workspaceFence.beginMutation();
11566
11551
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
11567
11552
  if (operation === "reconnect") {
@@ -11644,12 +11629,6 @@ function WhatsappSettingsTab({ rpcCall }) {
11644
11629
  WHATSAPP_ENDPOINTS.setContextEnhancement,
11645
11630
  { botId: account.botId, config }
11646
11631
  ),
11647
- onAccessPolicySave: (accessPolicy) => botAction(
11648
- account,
11649
- "access",
11650
- WHATSAPP_ENDPOINTS.setAccessPolicy,
11651
- { botId: account.botId, ...accessPolicy }
11652
- ),
11653
11632
  onRequestRemove: () => setRemoveTarget(account.botId),
11654
11633
  onCancelRemove: () => setRemoveTarget(null),
11655
11634
  onConfirmRemove: async () => {
@@ -11686,7 +11665,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11686
11665
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
11687
11666
  )
11688
11667
  ) : h2(
11689
- React20.Fragment,
11668
+ React19.Fragment,
11690
11669
  null,
11691
11670
  provision?.status === "pending" ? h2(QrPanel5, {
11692
11671
  provision,
@@ -11707,6 +11686,404 @@ function WhatsappSettingsTab({ rpcCall }) {
11707
11686
 
11708
11687
  // plugin-src/client/delivery-settings.js
11709
11688
  var React21 = __toESM(require("react"), 1);
11689
+
11690
+ // plugin-src/client/access-policy-settings.js
11691
+ var React20 = __toESM(require("react"), 1);
11692
+ var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
11693
+ var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
11694
+ weixin: Object.freeze({
11695
+ directUserLabel: "\u5FAE\u4FE1\u7528\u6237 ID",
11696
+ directPlaceholder: "\u586B\u5199\u5FAE\u4FE1\u7528\u6237 ID",
11697
+ groupSupported: false
11698
+ }),
11699
+ feishu: Object.freeze({
11700
+ directUserLabel: "\u98DE\u4E66 Open ID",
11701
+ directPlaceholder: "ou_xxx",
11702
+ groupUserLabel: "\u7FA4\u6210\u5458 Open ID",
11703
+ groupPlaceholder: "ou_xxx"
11704
+ }),
11705
+ dingtalk: Object.freeze({
11706
+ directUserLabel: "\u9489\u9489\u7528\u6237 ID",
11707
+ directPlaceholder: "\u586B\u5199 senderStaffId \u6216 senderId",
11708
+ groupUserLabel: "\u7FA4\u6210\u5458\u7528\u6237 ID",
11709
+ groupPlaceholder: "\u586B\u5199 senderStaffId \u6216 senderId"
11710
+ }),
11711
+ wecom: Object.freeze({
11712
+ directUserLabel: "\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID",
11713
+ directPlaceholder: "\u586B\u5199 userid",
11714
+ groupUserLabel: "\u7FA4\u6210\u5458\u7528\u6237 ID",
11715
+ groupPlaceholder: "\u586B\u5199 userid"
11716
+ }),
11717
+ qq: Object.freeze({
11718
+ directUserLabel: "QQ User Open ID",
11719
+ directPlaceholder: "\u586B\u5199 user_openid",
11720
+ groupUserLabel: "\u7FA4\u6210\u5458 Open ID",
11721
+ groupPlaceholder: "\u586B\u5199 member_openid"
11722
+ }),
11723
+ slack: Object.freeze({
11724
+ directUserLabel: "Slack User ID",
11725
+ directPlaceholder: "U0123456789",
11726
+ groupUserLabel: "\u7FA4\u6210\u5458 User ID",
11727
+ groupPlaceholder: "U0123456789"
11728
+ }),
11729
+ telegram: Object.freeze({
11730
+ directUserLabel: "Telegram User ID",
11731
+ directPlaceholder: "\u586B\u5199\u6570\u5B57 User ID",
11732
+ groupUserLabel: "\u7FA4\u6210\u5458 User ID",
11733
+ groupPlaceholder: "\u586B\u5199\u6570\u5B57 User ID"
11734
+ }),
11735
+ discord: Object.freeze({
11736
+ directUserLabel: "Discord User ID",
11737
+ directPlaceholder: "\u586B\u5199\u6570\u5B57 User ID",
11738
+ groupUserLabel: "\u7FA4\u6210\u5458 User ID",
11739
+ groupPlaceholder: "\u586B\u5199\u6570\u5B57 User ID"
11740
+ }),
11741
+ whatsapp: Object.freeze({
11742
+ directUserLabel: "WhatsApp \u7535\u8BDD\u53F7\u7801\u6216 JID",
11743
+ directPlaceholder: "8613800000000 \u6216\u5B8C\u6574 JID",
11744
+ groupUserLabel: "\u7FA4\u6210\u5458\u7535\u8BDD\u53F7\u7801\u6216 JID",
11745
+ groupPlaceholder: "8613800000000 \u6216\u5B8C\u6574 JID"
11746
+ })
11747
+ });
11748
+ function clonePolicy(policy) {
11749
+ const cloneScope = (scope) => ({
11750
+ mode: scope.mode,
11751
+ open: {
11752
+ defaultCanExecuteCommands: scope.open.defaultCanExecuteCommands,
11753
+ commandPermissionOverrides: scope.open.commandPermissionOverrides.map((user) => ({
11754
+ ...user
11755
+ }))
11756
+ },
11757
+ allowlist: {
11758
+ users: scope.allowlist.users.map((user) => ({ ...user }))
11759
+ }
11760
+ });
11761
+ return {
11762
+ direct: cloneScope(policy.direct),
11763
+ group: cloneScope(policy.group)
11764
+ };
11765
+ }
11766
+ function unwrapRpcResult10(result) {
11767
+ if (result?.ok === true) return result.value;
11768
+ if (result?.ok === false) {
11769
+ const error = new Error(result.error?.message || "\u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
11770
+ error.code = result.error?.code;
11771
+ throw error;
11772
+ }
11773
+ return result;
11774
+ }
11775
+ function policyFromSnapshot(value, botId) {
11776
+ const source = value?.snapshot ?? value;
11777
+ const bot = Array.isArray(source?.bots) ? source.bots.find((entry) => entry?.botId === botId) : null;
11778
+ return normalizeAccessPolicy(bot?.accessPolicy ?? source?.accessPolicy ?? source?.policy);
11779
+ }
11780
+ function commandValue(value) {
11781
+ return value === "allow";
11782
+ }
11783
+ function ScenePolicyEditor({
11784
+ scene,
11785
+ title,
11786
+ policy,
11787
+ userLabel,
11788
+ placeholder,
11789
+ disabled = false,
11790
+ unsupported = false,
11791
+ onChange
11792
+ }) {
11793
+ const ownerHelpId = React20.useId();
11794
+ const emptyAllowlistHelpId = React20.useId();
11795
+ const allowlist = policy.mode === "allowlist";
11796
+ const collectionKey = allowlist ? "users" : "commandPermissionOverrides";
11797
+ const branchKey = allowlist ? "allowlist" : "open";
11798
+ const users = policy[branchKey][collectionKey];
11799
+ const emptyAllowlist = allowlist && users.length === 0;
11800
+ const updateUsers = (nextUsers) => onChange({
11801
+ ...policy,
11802
+ [branchKey]: {
11803
+ ...policy[branchKey],
11804
+ [collectionKey]: nextUsers
11805
+ }
11806
+ });
11807
+ const updateUser = (index, patch) => updateUsers(users.map((user, userIndex) => userIndex === index ? { ...user, ...patch } : user));
11808
+ return h2(
11809
+ "fieldset",
11810
+ {
11811
+ className: "dim-accessScene",
11812
+ disabled,
11813
+ "data-scene": scene,
11814
+ "aria-label": localizeText(title)
11815
+ },
11816
+ h2(
11817
+ "legend",
11818
+ null,
11819
+ h2(
11820
+ "span",
11821
+ { className: "dim-accessLegendContent" },
11822
+ h2("span", null, title),
11823
+ h2(
11824
+ "span",
11825
+ { className: "dim-channelHelp dim-accessLegendHelp" },
11826
+ h2("button", {
11827
+ type: "button",
11828
+ className: "dim-channelHelpButton",
11829
+ "aria-label": [localizeText(title), localizeText("\u67E5\u770B\u8BBF\u95EE\u6743\u9650\u8BF4\u660E")].join(" "),
11830
+ "aria-describedby": ownerHelpId
11831
+ }, h2("span", { "aria-hidden": true }, "?")),
11832
+ h2("span", {
11833
+ id: ownerHelpId,
11834
+ className: "dim-channelTooltip dim-accessHelpTooltip",
11835
+ role: "tooltip"
11836
+ }, "\u539F\u6240\u6709\u8005\u6216\u626B\u7801\u63A5\u5165\u8005\u59CB\u7EC8\u53EF\u4EE5\u8BBF\u95EE\u5E76\u6267\u884C\u547D\u4EE4\uFF1B\u4EE5\u4E0B\u8BBE\u7F6E\u4EC5\u7EA6\u675F\u5176\u4ED6\u7528\u6237\u3002")
11837
+ )
11838
+ )
11839
+ ),
11840
+ unsupported ? h2(
11841
+ "div",
11842
+ { className: "dim-accessUnsupported", role: "note" },
11843
+ h2("strong", null, "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A"),
11844
+ 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")
11845
+ ) : h2(
11846
+ React20.Fragment,
11847
+ null,
11848
+ h2(
11849
+ "div",
11850
+ { className: "dim-accessControls", "data-mode": policy.mode },
11851
+ h2(
11852
+ "label",
11853
+ { className: "dim-accessField" },
11854
+ h2("span", null, "\u8BBF\u95EE\u6A21\u5F0F"),
11855
+ h2(
11856
+ "select",
11857
+ {
11858
+ value: policy.mode,
11859
+ "aria-label": [localizeText(title), localizeText("\u8BBF\u95EE\u6A21\u5F0F")].join(" "),
11860
+ onChange: (event) => onChange({ ...policy, mode: event.target.value })
11861
+ },
11862
+ h2("option", { value: "open" }, "\u5141\u8BB8\u6240\u6709\u7528\u6237"),
11863
+ h2("option", { value: "allowlist" }, "\u4EC5\u767D\u540D\u5355\u7528\u6237")
11864
+ )
11865
+ ),
11866
+ allowlist ? null : h2(
11867
+ "label",
11868
+ { className: "dim-accessField" },
11869
+ h2("span", null, "\u9ED8\u8BA4\u547D\u4EE4\u6743\u9650"),
11870
+ h2(
11871
+ "select",
11872
+ {
11873
+ value: policy.open.defaultCanExecuteCommands ? "allow" : "deny",
11874
+ "aria-label": [localizeText(title), localizeText("\u9ED8\u8BA4\u547D\u4EE4\u6743\u9650")].join(" "),
11875
+ onChange: (event) => onChange({
11876
+ ...policy,
11877
+ open: {
11878
+ ...policy.open,
11879
+ defaultCanExecuteCommands: commandValue(event.target.value)
11880
+ }
11881
+ })
11882
+ },
11883
+ h2("option", { value: "allow" }, "\u53EF\u4EE5\u6267\u884C\u547D\u4EE4"),
11884
+ h2("option", { value: "deny" }, "\u4E0D\u53EF\u4EE5\u6267\u884C\u547D\u4EE4")
11885
+ )
11886
+ )
11887
+ ),
11888
+ h2(
11889
+ "div",
11890
+ { className: "dim-accessUsers" },
11891
+ h2(
11892
+ "div",
11893
+ { className: "dim-accessUsersHeading" },
11894
+ h2(
11895
+ "div",
11896
+ { className: "dim-accessUsersTitle" },
11897
+ h2("strong", null, allowlist ? "\u767D\u540D\u5355\u7528\u6237" : "\u547D\u4EE4\u6743\u9650\u4F8B\u5916"),
11898
+ emptyAllowlist ? h2(
11899
+ "span",
11900
+ { className: "dim-channelHelp dim-accessUsersHelp" },
11901
+ h2("button", {
11902
+ type: "button",
11903
+ className: "dim-channelHelpButton",
11904
+ "aria-label": [localizeText(title), localizeText("\u67E5\u770B\u767D\u540D\u5355\u8BF4\u660E")].join(" "),
11905
+ "aria-describedby": emptyAllowlistHelpId
11906
+ }, h2("span", { "aria-hidden": true }, "?")),
11907
+ h2("span", {
11908
+ id: emptyAllowlistHelpId,
11909
+ className: "dim-channelTooltip dim-accessEmptyAllowlistTooltip",
11910
+ role: "tooltip"
11911
+ }, "\u5F53\u524D\u6CA1\u6709\u767D\u540D\u5355\u7528\u6237\uFF0C\u4FDD\u5B58\u540E\u666E\u901A\u7528\u6237\u5C06\u65E0\u6CD5\u4F7F\u7528\u673A\u5668\u4EBA\u3002")
11912
+ ) : null
11913
+ ),
11914
+ h2("button", {
11915
+ type: "button",
11916
+ className: "dim-deliveryButton dim-accessAddUser",
11917
+ "aria-label": [localizeText(title), localizeText("\u65B0\u589E\u7528\u6237")].join(" "),
11918
+ title: localizeText("\u65B0\u589E\u7528\u6237"),
11919
+ onClick: () => updateUsers([...users, {
11920
+ id: "",
11921
+ canExecuteCommands: allowlist ? false : !policy.open.defaultCanExecuteCommands
11922
+ }])
11923
+ }, h2("span", { "aria-hidden": true }, "+"))
11924
+ ),
11925
+ users.length === 0 ? h2("div", { className: "dim-accessUsersEmpty" }, "\u5C1A\u672A\u6DFB\u52A0\u7528\u6237") : h2("ul", { className: "dim-accessUserList" }, users.map((user, index) => h2(
11926
+ "li",
11927
+ { key: `${scene}-${policy.mode}-${index}`, className: "dim-accessUserRow" },
11928
+ h2(
11929
+ "label",
11930
+ { className: "dim-accessField dim-accessUserId" },
11931
+ h2("span", null, userLabel),
11932
+ h2("input", {
11933
+ value: user.id,
11934
+ maxLength: 256,
11935
+ required: true,
11936
+ autoCapitalize: "none",
11937
+ autoCorrect: "off",
11938
+ spellCheck: false,
11939
+ placeholder,
11940
+ "aria-label": [localizeText(title), localizeText(userLabel), index + 1].join(" "),
11941
+ onChange: (event) => updateUser(index, { id: event.target.value })
11942
+ })
11943
+ ),
11944
+ h2(
11945
+ "label",
11946
+ { className: "dim-accessField dim-accessUserCommand" },
11947
+ h2("span", null, "\u547D\u4EE4\u6743\u9650"),
11948
+ h2(
11949
+ "select",
11950
+ {
11951
+ value: user.canExecuteCommands ? "allow" : "deny",
11952
+ "aria-label": [
11953
+ localizeText(title),
11954
+ localizeText("\u7528\u6237"),
11955
+ index + 1,
11956
+ localizeText("\u547D\u4EE4\u6743\u9650")
11957
+ ].join(" "),
11958
+ onChange: (event) => updateUser(index, {
11959
+ canExecuteCommands: commandValue(event.target.value)
11960
+ })
11961
+ },
11962
+ h2("option", { value: "allow" }, "\u53EF\u4EE5\u6267\u884C\u547D\u4EE4"),
11963
+ h2("option", { value: "deny" }, "\u4E0D\u53EF\u4EE5\u6267\u884C\u547D\u4EE4")
11964
+ )
11965
+ ),
11966
+ h2("button", {
11967
+ type: "button",
11968
+ className: "dim-deliveryButton dim-accessDeleteUser",
11969
+ "data-kind": "danger",
11970
+ "aria-label": [
11971
+ localizeText(title),
11972
+ localizeText("\u5220\u9664"),
11973
+ localizeText("\u7528\u6237"),
11974
+ index + 1
11975
+ ].join(" "),
11976
+ onClick: () => updateUsers(users.filter((_, userIndex) => userIndex !== index))
11977
+ }, "\u5220\u9664")
11978
+ )))
11979
+ )
11980
+ )
11981
+ );
11982
+ }
11983
+ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved }) {
11984
+ const definition = ACCESS_CHANNEL_DEFINITIONS[channel4];
11985
+ const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
11986
+ const initialKey = JSON.stringify(initialPolicy);
11987
+ const [draft, setDraft] = React20.useState(() => clonePolicy(
11988
+ initialPolicy ?? DEFAULT_ACCESS_POLICY
11989
+ ));
11990
+ const [saving, setSaving] = React20.useState(false);
11991
+ const [feedback, setFeedback] = React20.useState(null);
11992
+ React20.useEffect(() => {
11993
+ const next = normalizeAccessPolicy(account?.accessPolicy);
11994
+ setDraft(clonePolicy(next ?? DEFAULT_ACCESS_POLICY));
11995
+ }, [account?.botId, initialKey]);
11996
+ React20.useEffect(() => {
11997
+ setFeedback(null);
11998
+ }, [account?.botId]);
11999
+ if (!definition) {
12000
+ return h2(
12001
+ "div",
12002
+ { className: "dim-accessState", role: "alert" },
12003
+ "\u5F53\u524D\u6E20\u9053\u6682\u4E0D\u652F\u6301\u8BBF\u95EE\u8BBE\u7F6E\u3002"
12004
+ );
12005
+ }
12006
+ const save = async (event) => {
12007
+ event.preventDefault();
12008
+ setFeedback(null);
12009
+ setSaving(true);
12010
+ try {
12011
+ const policy = validateAccessPolicy(draft);
12012
+ if (typeof rpcCall !== "function") throw new Error("\u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12013
+ const value = unwrapRpcResult10(await rpcCall(ACCESS_POLICY_ENDPOINT, {
12014
+ botId: account.botId,
12015
+ policy
12016
+ }));
12017
+ const saved = policyFromSnapshot(value, account.botId);
12018
+ if (!saved) throw new Error("\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u5DF2\u4FDD\u5B58\u7684\u8BBF\u95EE\u7B56\u7565\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
12019
+ setDraft(clonePolicy(saved));
12020
+ onSaved?.(saved);
12021
+ setFeedback({ tone: "success", message: "\u8BBF\u95EE\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002" });
12022
+ } catch (error) {
12023
+ setFeedback({
12024
+ tone: "error",
12025
+ message: error?.message || "\u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"
12026
+ });
12027
+ } finally {
12028
+ setSaving(false);
12029
+ }
12030
+ };
12031
+ return h2(
12032
+ "form",
12033
+ {
12034
+ className: "dim-accessPage",
12035
+ onSubmit: (event) => void save(event)
12036
+ },
12037
+ initialPolicy ? null : h2(
12038
+ "div",
12039
+ { className: "dim-accessState", role: "alert" },
12040
+ "\u8BBF\u95EE\u7B56\u7565\u5C1A\u672A\u5C31\u7EEA\uFF0C\u8BF7\u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868\u5237\u65B0\u540E\u91CD\u8BD5\u3002"
12041
+ ),
12042
+ h2(ScenePolicyEditor, {
12043
+ scene: "direct",
12044
+ title: "\u79C1\u804A",
12045
+ policy: draft.direct,
12046
+ userLabel: definition.directUserLabel,
12047
+ placeholder: definition.directPlaceholder,
12048
+ disabled: saving,
12049
+ onChange: (direct) => {
12050
+ setDraft((current) => ({ ...current, direct }));
12051
+ setFeedback(null);
12052
+ }
12053
+ }),
12054
+ h2(ScenePolicyEditor, {
12055
+ scene: "group",
12056
+ title: "\u7FA4\u804A",
12057
+ policy: draft.group,
12058
+ userLabel: definition.groupUserLabel ?? definition.directUserLabel,
12059
+ placeholder: definition.groupPlaceholder ?? definition.directPlaceholder,
12060
+ disabled: saving || definition.groupSupported === false,
12061
+ unsupported: definition.groupSupported === false,
12062
+ onChange: (group) => {
12063
+ setDraft((current) => ({ ...current, group }));
12064
+ setFeedback(null);
12065
+ }
12066
+ }),
12067
+ feedback ? h2("p", {
12068
+ className: "dim-accessFeedback",
12069
+ "data-tone": feedback.tone,
12070
+ role: feedback.tone === "error" ? "alert" : "status",
12071
+ "aria-live": "polite"
12072
+ }, feedback.message) : null,
12073
+ h2(
12074
+ "div",
12075
+ { className: "dim-accessActions" },
12076
+ h2("button", {
12077
+ type: "submit",
12078
+ className: "dim-deliveryButton",
12079
+ "data-kind": "primary",
12080
+ disabled: saving || !initialPolicy
12081
+ }, saving ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
12082
+ )
12083
+ );
12084
+ }
12085
+
12086
+ // plugin-src/client/delivery-settings.js
11710
12087
  var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
11711
12088
  var DELIVERY_DOCS_URL = Object.freeze({
11712
12089
  zh: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.md",
@@ -11720,6 +12097,10 @@ var DELIVERY_ENDPOINTS = Object.freeze({
11720
12097
  delete: "target.delete",
11721
12098
  test: "target.test"
11722
12099
  });
12100
+ var BOT_SETTINGS_TABS = Object.freeze([
12101
+ Object.freeze({ id: "delivery", label: "\u6295\u9012\u8BBE\u7F6E" }),
12102
+ Object.freeze({ id: "access", label: "\u8BBF\u95EE\u8BBE\u7F6E" })
12103
+ ]);
11723
12104
  var CHANNEL_DEFINITIONS = Object.freeze({
11724
12105
  weixin: {
11725
12106
  label: "\u5FAE\u4FE1",
@@ -11797,7 +12178,7 @@ var CHANNEL_DEFINITIONS = Object.freeze({
11797
12178
  function presentError10(error, fallback) {
11798
12179
  return error?.message || fallback;
11799
12180
  }
11800
- function unwrapRpcResult10(result) {
12181
+ function unwrapRpcResult11(result) {
11801
12182
  if (result?.ok === true) return result.value;
11802
12183
  if (result?.ok === false) {
11803
12184
  const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
@@ -12255,8 +12636,15 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12255
12636
  ) : null
12256
12637
  );
12257
12638
  }
12258
- function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBack }) {
12639
+ function DeliveryTargetSettingsPage({
12640
+ channel: channel4,
12641
+ account,
12642
+ rpcCall,
12643
+ accessRpcCall,
12644
+ onBack
12645
+ }) {
12259
12646
  const definition = CHANNEL_DEFINITIONS[channel4];
12647
+ const [activeTabId, setActiveTabId] = React21.useState(BOT_SETTINGS_TABS[0].id);
12260
12648
  const [phase, setPhase] = React21.useState("loading");
12261
12649
  const [targets, setTargets] = React21.useState([]);
12262
12650
  const [suggestionPhase, setSuggestionPhase] = React21.useState("idle");
@@ -12266,10 +12654,14 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12266
12654
  const [editor, setEditor] = React21.useState(null);
12267
12655
  const [saving, setSaving] = React21.useState(false);
12268
12656
  const [botCopyState, setBotCopyState] = React21.useState(null);
12657
+ const [accessPolicy, setAccessPolicy] = React21.useState(account.accessPolicy);
12269
12658
  const mounted = React21.useRef(true);
12659
+ React21.useEffect(() => {
12660
+ setAccessPolicy(account.accessPolicy);
12661
+ }, [account.botId, account.accessPolicy]);
12270
12662
  const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
12271
12663
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12272
- return unwrapRpcResult10(await rpcCall(endpoint, payload, signal));
12664
+ return unwrapRpcResult11(await rpcCall(endpoint, payload, signal));
12273
12665
  }, [rpcCall]);
12274
12666
  const loadTargets = React21.useCallback(async ({ signal, silent = false } = {}) => {
12275
12667
  if (!silent) setPhase("loading");
@@ -12370,6 +12762,9 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12370
12762
  setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002"));
12371
12763
  }
12372
12764
  };
12765
+ const activeTab = BOT_SETTINGS_TABS.find((tab) => tab.id === activeTabId) ?? BOT_SETTINGS_TABS[0];
12766
+ const activeTabDomId = `dim-bot-settings-${activeTab.id}-tab`;
12767
+ const activePanelId = `dim-bot-settings-${activeTab.id}-panel`;
12373
12768
  return h2(
12374
12769
  "section",
12375
12770
  {
@@ -12379,114 +12774,147 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12379
12774
  h2(
12380
12775
  "header",
12381
12776
  { className: "dim-deliveryHeader" },
12382
- h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868"),
12383
- h2(
12384
- "div",
12385
- { className: "dim-deliveryHeaderMeta" },
12386
- h2(
12387
- "a",
12388
- {
12389
- className: "dim-deliveryDocsLink",
12390
- href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh,
12391
- target: "_blank",
12392
- rel: "noopener noreferrer",
12393
- "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863"
12394
- },
12395
- h2("span", null, "\u4F7F\u7528\u6587\u6863"),
12396
- h2("span", { "aria-hidden": "true" }, "\u2197")
12397
- )
12398
- )
12777
+ h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868")
12399
12778
  ),
12400
12779
  h2(
12401
- "section",
12402
- { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" },
12403
- h2(
12404
- "div",
12405
- null,
12406
- h2(
12407
- "h2",
12408
- { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" },
12409
- account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E"
12410
- )
12411
- ),
12412
- h2(
12413
- "div",
12414
- { className: "dim-deliveryBotId" },
12415
- h2("span", null, "Bot ID"),
12416
- h2("code", { title: account.botId }, account.botId),
12417
- h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236")
12418
- ),
12419
- botCopyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, botCopyState) : null
12780
+ "div",
12781
+ { className: "dim-botSettingsTabsBar" },
12782
+ h2("nav", {
12783
+ className: "dim-botSettingsTabs",
12784
+ role: "tablist",
12785
+ "aria-label": "\u673A\u5668\u4EBA\u8BBE\u7F6E\u9875\u7B7E"
12786
+ }, BOT_SETTINGS_TABS.map((tab) => h2("button", {
12787
+ key: tab.id,
12788
+ id: `dim-bot-settings-${tab.id}-tab`,
12789
+ type: "button",
12790
+ role: "tab",
12791
+ className: "dim-botSettingsTab",
12792
+ "aria-selected": tab.id === activeTab.id,
12793
+ "aria-controls": `dim-bot-settings-${tab.id}-panel`,
12794
+ tabIndex: tab.id === activeTab.id ? 0 : -1,
12795
+ onClick: () => setActiveTabId(tab.id)
12796
+ }, tab.label)))
12420
12797
  ),
12421
12798
  h2(
12422
- "section",
12423
- { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" },
12424
- h2(
12425
- "div",
12426
- { className: "dim-deliverySectionHeading" },
12799
+ "div",
12800
+ {
12801
+ id: activePanelId,
12802
+ className: "dim-botSettingsTabPanel",
12803
+ role: "tabpanel",
12804
+ "aria-labelledby": activeTabDomId
12805
+ },
12806
+ activeTab.id === "access" ? h2(AccessPolicySettingsPage, {
12807
+ channel: channel4,
12808
+ account: { ...account, accessPolicy },
12809
+ rpcCall: accessRpcCall,
12810
+ onSaved: setAccessPolicy
12811
+ }) : h2(
12812
+ React21.Fragment,
12813
+ null,
12427
12814
  h2(
12428
- "div",
12429
- null,
12430
- h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"),
12431
- account.connected ? null : h2("p", null, "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002")
12815
+ "section",
12816
+ { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" },
12817
+ h2(
12818
+ "div",
12819
+ { className: "dim-deliveryIdentityHeading" },
12820
+ h2(
12821
+ "h2",
12822
+ { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" },
12823
+ account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E"
12824
+ ),
12825
+ h2(
12826
+ "a",
12827
+ {
12828
+ className: "dim-deliveryDocsLink",
12829
+ href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh,
12830
+ target: "_blank",
12831
+ rel: "noopener noreferrer",
12832
+ "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863"
12833
+ },
12834
+ h2("span", null, "\u4F7F\u7528\u6587\u6863"),
12835
+ h2("span", { "aria-hidden": "true" }, "\u2197")
12836
+ )
12837
+ ),
12838
+ h2(
12839
+ "div",
12840
+ { className: "dim-deliveryBotId" },
12841
+ h2("span", null, "Bot ID"),
12842
+ h2("code", { title: account.botId }, account.botId),
12843
+ h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236")
12844
+ ),
12845
+ botCopyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, botCopyState) : null
12432
12846
  ),
12433
- h2(DeliveryButton, {
12434
- kind: "primary",
12435
- onClick: openSuggestionPicker,
12436
- disabled: Boolean(editor) || phase !== "ready",
12437
- title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6"
12438
- }, "\u65B0\u5EFA\u76EE\u6807")
12439
- ),
12440
- editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, {
12441
- definition,
12442
- phase: suggestionPhase,
12443
- suggestions,
12444
- error: suggestionError,
12445
- targets,
12446
- onRefresh: loadSuggestions,
12447
- onSelect: selectSuggestion,
12448
- onManual: () => setEditor({
12449
- mode: "create",
12450
- source: "manual",
12451
- draftKey: "manual",
12452
- initialValue: { targetId: randomTargetId(targets) }
12453
- }),
12454
- onCancel: () => setEditor(null)
12455
- }) : editor ? h2(TargetForm, {
12456
- key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey,
12457
- definition,
12458
- mode: editor.mode,
12459
- initialValue: editor.mode === "edit" ? editor.target : editor.initialValue,
12460
- source: editor.source,
12461
- busy: saving,
12462
- connected: account.connected,
12463
- onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null),
12464
- onSave: saveTarget,
12465
- onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, {
12466
- botId: account.botId,
12467
- target
12468
- })
12469
- }) : null,
12470
- phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2(
12471
- "div",
12472
- { className: "dim-deliveryState", role: "alert" },
12473
- h2("p", null, error),
12474
- h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6")
12475
- ) : targets.length === 0 ? h2(
12476
- "div",
12477
- { className: "dim-deliveryState dim-deliveryEmpty" },
12478
- h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"),
12479
- h2("p", null, "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002")
12480
- ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, {
12481
- key: target.targetId,
12482
- definition,
12483
- target,
12484
- botId: account.botId,
12485
- connected: account.connected,
12486
- rpcCall: invoke,
12487
- onChanged: () => loadTargets({ silent: true }),
12488
- onEdit: () => setEditor({ mode: "edit", target, source: "edit" })
12489
- })))
12847
+ h2(
12848
+ "section",
12849
+ { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" },
12850
+ h2(
12851
+ "div",
12852
+ { className: "dim-deliverySectionHeading" },
12853
+ h2(
12854
+ "div",
12855
+ null,
12856
+ h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"),
12857
+ account.connected ? null : h2("p", null, "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002")
12858
+ ),
12859
+ h2(DeliveryButton, {
12860
+ kind: "primary",
12861
+ onClick: openSuggestionPicker,
12862
+ disabled: Boolean(editor) || phase !== "ready",
12863
+ title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6"
12864
+ }, "\u65B0\u5EFA\u76EE\u6807")
12865
+ ),
12866
+ editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, {
12867
+ definition,
12868
+ phase: suggestionPhase,
12869
+ suggestions,
12870
+ error: suggestionError,
12871
+ targets,
12872
+ onRefresh: loadSuggestions,
12873
+ onSelect: selectSuggestion,
12874
+ onManual: () => setEditor({
12875
+ mode: "create",
12876
+ source: "manual",
12877
+ draftKey: "manual",
12878
+ initialValue: { targetId: randomTargetId(targets) }
12879
+ }),
12880
+ onCancel: () => setEditor(null)
12881
+ }) : editor ? h2(TargetForm, {
12882
+ key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey,
12883
+ definition,
12884
+ mode: editor.mode,
12885
+ initialValue: editor.mode === "edit" ? editor.target : editor.initialValue,
12886
+ source: editor.source,
12887
+ busy: saving,
12888
+ connected: account.connected,
12889
+ onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null),
12890
+ onSave: saveTarget,
12891
+ onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, {
12892
+ botId: account.botId,
12893
+ target
12894
+ })
12895
+ }) : null,
12896
+ phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2(
12897
+ "div",
12898
+ { className: "dim-deliveryState", role: "alert" },
12899
+ h2("p", null, error),
12900
+ h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6")
12901
+ ) : targets.length === 0 ? h2(
12902
+ "div",
12903
+ { className: "dim-deliveryState dim-deliveryEmpty" },
12904
+ h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"),
12905
+ h2("p", null, "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002")
12906
+ ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, {
12907
+ key: target.targetId,
12908
+ definition,
12909
+ target,
12910
+ botId: account.botId,
12911
+ connected: account.connected,
12912
+ rpcCall: invoke,
12913
+ onChanged: () => loadTargets({ silent: true }),
12914
+ onEdit: () => setEditor({ mode: "edit", target, source: "edit" })
12915
+ })))
12916
+ )
12917
+ )
12490
12918
  )
12491
12919
  );
12492
12920
  }
@@ -12776,10 +13204,15 @@ var CSS11 = String.raw`
12776
13204
  .dim-contextHeaderTooltip { width: min(340px, calc(100vw - 72px)); }
12777
13205
  .dim-contextClose { width: 30px; height: 30px; flex: none; display: grid; place-items: center; padding: 0; border: 0; border-radius: 6px; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; cursor: pointer; }
12778
13206
  .dim-contextClose:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
12779
- .dim-contextSection { min-width: 0; margin: 14px 0; padding: 0; border: 0; }
12780
- .dim-contextSection legend { margin-bottom: 8px; padding: 0; font-weight: 500; }
13207
+ .dim-contextTabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3px; margin-top: 14px; padding: 3px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 8px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
13208
+ .dim-contextTab { min-width: 0; min-height: 34px; padding: 5px 12px; border: 0; border-radius: 6px; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-weight: 500; cursor: pointer; transition: color .15s ease, background .15s ease, box-shadow .15s ease; }
13209
+ .dim-contextTab:hover:not(:disabled):not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
13210
+ .dim-contextTab[aria-selected="true"] { color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 3px rgb(31 35 41 / 12%); }
13211
+ .dim-contextTabPanel[hidden] { display: none; }
13212
+ .dim-contextSection { min-width: 0; margin: 0; padding: 0; border: 0; }
13213
+ .dim-contextScope { margin-top: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-1, #fff); }
13214
+ .dim-contextScopeBlock { margin-top: 12px; }
12781
13215
  .dim-contextLegend { position: relative; display: inline-flex; align-items: center; gap: 6px; }
12782
- .dim-contextSwitches { display: grid; grid-template-columns: 1fr; gap: 2px; }
12783
13216
  .dim-contextSwitchRow { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 34px; cursor: pointer; }
12784
13217
  .dim-contextSwitchLabel { min-width: 0; display: inline-flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
12785
13218
  .dim-contextUnavailable { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 16px; font-weight: 400; }
@@ -12822,7 +13255,7 @@ var CSS11 = String.raw`
12822
13255
  .dim-contextEntry:focus-visible, .dim-contextDialog button:focus-visible, .dim-contextDialog input:focus-visible, .dim-contextDialog textarea:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
12823
13256
  .dim-contextEntry:disabled, .dim-contextDialog button:disabled, .dim-contextDialog input:disabled, .dim-contextDialog textarea:disabled { opacity: .55; cursor: not-allowed; }
12824
13257
  @media (pointer: coarse) {
12825
- .dim-contextEntry, .dim-contextClose, .dim-contextFooter button, .dim-contextTextActions button, .dim-contextField, .dim-contextSwitchRow { min-height: 44px; }
13258
+ .dim-contextEntry, .dim-contextTab, .dim-contextClose, .dim-contextFooter button, .dim-contextTextActions button, .dim-contextField, .dim-contextSwitchRow { min-height: 44px; }
12826
13259
  .dim-contextClose, .dim-contextTextActions button { min-width: 44px; }
12827
13260
  .dim-contextGuidance textarea { font-size: 16px; }
12828
13261
  }
@@ -12912,9 +13345,18 @@ var CSS11 = String.raw`
12912
13345
  .dim-botSettingsButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
12913
13346
  .dim-botSettingsTooltip { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: max-content; padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
12914
13347
  .dim-botSettingsAction:hover .dim-botSettingsTooltip, .dim-botSettingsAction:focus-within .dim-botSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
12915
- .dim-deliveryPage { min-width: 0; display: grid; gap: 14px; }
13348
+ .dim-deliveryPage { min-width: 0; display: grid; }
12916
13349
  .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
12917
- .dim-deliveryHeaderMeta { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 10px; text-align: right; }
13350
+ .dim-botSettingsTabsBar { min-width: 0; margin-top: 10px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
13351
+ .dim-botSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; }
13352
+ .dim-botSettingsTabs::-webkit-scrollbar { display: none; }
13353
+ .dim-botSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; }
13354
+ .dim-botSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; }
13355
+ .dim-botSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); }
13356
+ .dim-botSettingsTab[aria-selected="true"] { color: var(--dsw-alias-state-business-primary, #3370ff); font-weight: 650; }
13357
+ .dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); }
13358
+ .dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; }
13359
+ .dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; }
12918
13360
  .dim-deliveryDocsLink { min-height: 30px; flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); font-size: 12px; line-height: 18px; text-decoration: none; white-space: nowrap; }
12919
13361
  .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
12920
13362
  .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
@@ -12926,6 +13368,7 @@ var CSS11 = String.raw`
12926
13368
  .dim-deliveryButton[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
12927
13369
  .dim-deliveryBack { flex: none; border-color: transparent; background: transparent; }
12928
13370
  .dim-deliveryIdentity, .dim-deliveryTargets { min-width: 0; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
13371
+ .dim-deliveryIdentityHeading { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
12929
13372
  .dim-deliveryBotName, .dim-deliverySectionHeading h3 { min-width: 0; overflow: hidden; margin: 0; font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
12930
13373
  .dim-deliveryIdentity > div:first-child p, .dim-deliverySectionHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
12931
13374
  .dim-deliveryBotId { min-width: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; align-items: center; gap: 10px; margin-top: 12px; padding: 10px 12px; border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
@@ -12968,6 +13411,38 @@ var CSS11 = String.raw`
12968
13411
  .dim-targetField input[readonly] { color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
12969
13412
  .dim-targetFormError { margin: 10px 0 0; font-size: 12px; line-height: 18px; }
12970
13413
  .dim-targetFormActions { display: flex; justify-content: flex-end; gap: 7px; margin-top: 12px; }
13414
+ .dim-accessPage { min-width: 0; display: grid; gap: 14px; }
13415
+ .dim-accessScene { position: relative; min-width: 0; margin: 0; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
13416
+ .dim-accessScene > legend { padding: 0 6px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; line-height: 22px; font-weight: 650; }
13417
+ .dim-accessLegendContent { display: inline-flex; align-items: center; gap: 6px; }
13418
+ .dim-panel .dim-accessLegendHelp { position: static; }
13419
+ .dim-accessLegendHelp .dim-channelTooltip { top: 20px; right: auto; left: 16px; width: min(320px, calc(100% - 32px)); max-width: none; }
13420
+ .dim-accessControls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
13421
+ .dim-accessControls[data-mode="allowlist"] { grid-template-columns: minmax(0, 1fr); }
13422
+ .dim-accessField { min-width: 0; display: grid; align-content: start; gap: 5px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
13423
+ .dim-accessField input, .dim-accessField select { width: 100%; min-width: 0; height: 36px; padding: 0 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
13424
+ .dim-accessField input:focus, .dim-accessField select:focus { outline: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 28%, transparent); border-color: var(--dsw-alias-state-business-primary, #3370ff); }
13425
+ .dim-accessUsers { min-width: 0; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
13426
+ .dim-accessUsersHeading { position: relative; min-width: 0; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
13427
+ .dim-accessUsersHeading > div { min-width: 0; }
13428
+ .dim-accessUsersTitle { display: inline-flex; align-items: center; gap: 6px; }
13429
+ .dim-accessUsersHeading strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; line-height: 20px; font-weight: 620; }
13430
+ .dim-accessUsersHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
13431
+ .dim-panel .dim-accessUsersHelp { position: static; }
13432
+ .dim-accessUsersHelp .dim-channelTooltip { top: calc(100% + 7px); right: auto; left: 0; width: min(320px, 100%); max-width: none; }
13433
+ .dim-accessAddUser { width: 32px; height: 32px; min-height: 32px; flex: 0 0 32px; padding: 0; font-size: 20px; line-height: 1; }
13434
+ .dim-accessUsersEmpty { margin-top: 10px; padding: 15px 12px; border: 1px dashed var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 18px; text-align: center; }
13435
+ .dim-accessUserList { display: grid; gap: 9px; margin: 10px 0 0; padding: 0; list-style: none; }
13436
+ .dim-accessUserRow { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) minmax(145px, 180px) max-content; align-items: end; gap: 10px; padding: 11px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
13437
+ .dim-accessDeleteUser { margin-bottom: 1px; }
13438
+ .dim-accessUnsupported { padding: 18px 14px; border: 1px dashed var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); text-align: center; }
13439
+ .dim-accessUnsupported strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; line-height: 20px; }
13440
+ .dim-accessUnsupported p { margin: 4px 0 0; font-size: 12px; line-height: 18px; }
13441
+ .dim-accessState { padding: 11px 13px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 24%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 9px; color: var(--dsw-alias-state-warn-primary, #d97706); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 7%, var(--dsw-alias-bg-layer-1, #fff)); font-size: 12px; line-height: 18px; }
13442
+ .dim-accessFeedback { margin: 0; padding: 10px 12px; border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font-size: 12px; line-height: 18px; }
13443
+ .dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
13444
+ .dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
13445
+ .dim-accessActions { display: flex; justify-content: flex-end; }
12971
13446
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
12972
13447
  .dim-panel .ddt-headingCopy { display: none; }
12973
13448
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -12991,6 +13466,9 @@ var CSS11 = String.raw`
12991
13466
  .dim-targetActions { justify-content: flex-start; }
12992
13467
  .dim-targetFormGrid { grid-template-columns: minmax(0, 1fr); }
12993
13468
  .dim-targetSuggestionHeading { align-items: stretch; flex-direction: column; }
13469
+ .dim-accessControls { grid-template-columns: minmax(0, 1fr); }
13470
+ .dim-accessUserRow { grid-template-columns: minmax(0, 1fr); }
13471
+ .dim-accessDeleteUser { justify-self: start; }
12994
13472
  }
12995
13473
  @media (max-width: 840px) {
12996
13474
  .dim-title { align-items: flex-start; }
@@ -13014,11 +13492,12 @@ var CSS11 = String.raw`
13014
13492
  .dim-rail { grid-template-columns: minmax(0, 1fr); }
13015
13493
  .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; }
13016
13494
  .dim-loopbackRecoveryAction { width: 100%; }
13017
- .dim-deliveryHeader, .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
13018
- .dim-deliveryHeaderMeta { justify-content: space-between; text-align: left; }
13495
+ .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
13496
+ .dim-botSettingsTabs { gap: 18px; }
13019
13497
  .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; }
13020
13498
  .dim-deliveryBotId > span { grid-column: 1 / -1; }
13021
13499
  .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
13500
+ .dim-accessActions .dim-deliveryButton { width: 100%; }
13022
13501
  .dim-directoryPickerBackdrop { padding: 10px; }
13023
13502
  .dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
13024
13503
  .dim-directoryPickerHeader { padding: 18px 17px 14px; }
@@ -13830,6 +14309,7 @@ function IMSettingsTab({
13830
14309
  channel: active.id,
13831
14310
  account: deliverySettings,
13832
14311
  rpcCall: rpcCalls.deliveryRpcCall,
14312
+ accessRpcCall: rpcCalls[`${active.id}RpcCall`],
13833
14313
  onBack: () => setDeliverySettings(null)
13834
14314
  }) : active.id === "weixin" ? h2(WeixinSettingsTab, { rpcCall: rpcCalls.weixinRpcCall }) : active.id === "feishu" ? h2(FeishuSettingsTab, { rpcCall: rpcCalls.feishuRpcCall }) : active.id === "dingtalk" ? h2(DingtalkSettingsTab, { rpcCall: rpcCalls.dingtalkRpcCall }) : active.id === "wecom" ? h2(WecomSettingsTab, { rpcCall: rpcCalls.wecomRpcCall }) : active.id === "qq" ? h2(QqSettingsTab, { rpcCall: rpcCalls.qqRpcCall }) : active.id === "slack" ? h2(SlackSettingsTab, { rpcCall: rpcCalls.slackRpcCall }) : active.id === "telegram" ? h2(TelegramSettingsTab, { rpcCall: rpcCalls.telegramRpcCall }) : active.id === "discord" ? h2(DiscordSettingsTab, { rpcCall: rpcCalls.discordRpcCall }) : active.id === "whatsapp" ? h2(WhatsappSettingsTab, { rpcCall: rpcCalls.whatsappRpcCall }) : h2(OfficeSettingsTab, { rpcCall: rpcCalls.officeRpcCall })
13835
14315
  )