@xmanrui/dsh-im 4.3.0 → 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 (68) hide show
  1. package/lib/client.js +951 -626
  2. package/lib/index.js +233 -233
  3. package/package.json +1 -1
  4. package/plugin-src/client/access-policy-settings.js +351 -0
  5. package/plugin-src/client/channel-card-meta.js +2 -1
  6. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  7. package/plugin-src/client/channels/dingtalk/index.js +1 -0
  8. package/plugin-src/client/channels/feishu/api.js +5 -0
  9. package/plugin-src/client/channels/feishu/index.js +1 -0
  10. package/plugin-src/client/channels/qq/api.js +5 -0
  11. package/plugin-src/client/channels/qq/index.js +1 -0
  12. package/plugin-src/client/channels/shared/token-api.js +5 -0
  13. package/plugin-src/client/channels/shared/token-channel.js +1 -0
  14. package/plugin-src/client/channels/telegram/api.js +2 -17
  15. package/plugin-src/client/channels/telegram/index.js +0 -112
  16. package/plugin-src/client/channels/telegram/styles.js +0 -28
  17. package/plugin-src/client/channels/wecom/api.js +5 -0
  18. package/plugin-src/client/channels/wecom/index.js +1 -0
  19. package/plugin-src/client/channels/weixin/api.js +5 -0
  20. package/plugin-src/client/channels/weixin/index.js +1 -0
  21. package/plugin-src/client/channels/whatsapp/api.js +4 -10
  22. package/plugin-src/client/channels/whatsapp/index.js +1 -125
  23. package/plugin-src/client/channels/whatsapp/styles.js +0 -25
  24. package/plugin-src/client/delivery-settings.js +61 -9
  25. package/plugin-src/client/i18n.js +56 -1
  26. package/plugin-src/client/index.js +1 -0
  27. package/plugin-src/client/styles.js +50 -4
  28. package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
  29. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  30. package/plugin-src/host/channels/feishu/production.mjs +39 -1
  31. package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
  32. package/plugin-src/host/channels/qq/production.mjs +12 -1
  33. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  34. package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
  35. package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
  36. package/plugin-src/host/channels/shared/production.mjs +15 -1
  37. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  38. package/plugin-src/host/channels/slack/production.mjs +12 -1
  39. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  40. package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
  41. package/plugin-src/host/channels/wecom/production.mjs +12 -1
  42. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  43. package/plugin-src/host/channels/weixin/production.mjs +12 -1
  44. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  45. package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
  46. package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
  47. package/src/channels/dingtalk/dingtalk-bridge.mjs +61 -9
  48. package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
  49. package/src/channels/discord/discord-runtime.mjs +29 -1
  50. package/src/channels/feishu/bridge.mjs +188 -80
  51. package/src/channels/feishu/feishu-runtime.mjs +4 -0
  52. package/src/channels/qq/qq-bridge.mjs +59 -17
  53. package/src/channels/qq/qq-runtime.mjs +20 -2
  54. package/src/channels/shared/access-policy.mjs +210 -0
  55. package/src/channels/shared/bot-workspace-store.mjs +176 -33
  56. package/src/channels/shared/command-permission.mjs +32 -0
  57. package/src/channels/shared/compact-command.mjs +5 -2
  58. package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
  59. package/src/channels/shared/inbound-access.mjs +43 -0
  60. package/src/channels/shared/text-harness-bridge.mjs +47 -4
  61. package/src/channels/shared/workspace-command.mjs +10 -1
  62. package/src/channels/slack/slack-runtime.mjs +4 -0
  63. package/src/channels/telegram/telegram-runtime.mjs +5 -13
  64. package/src/channels/wecom/wecom-bridge.mjs +51 -4
  65. package/src/channels/wecom/wecom-runtime.mjs +4 -0
  66. package/src/channels/weixin/weixin-bridge.mjs +59 -16
  67. package/src/channels/weixin/weixin-runtime.mjs +4 -0
  68. 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.3.0",
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",
@@ -1355,7 +1411,6 @@ var EN = Object.freeze({
1355
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.",
1356
1412
  "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token": "Enter the Bot Token from @BotFather",
1357
1413
  "\u8BBF\u95EE\u6A21\u5F0F": "Access mode",
1358
- "\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
1359
1414
  "Telegram \u8BBF\u95EE\u6A21\u5F0F": "Telegram access mode",
1360
1415
  "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E": "View Telegram access mode details",
1361
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.",
@@ -1885,6 +1940,135 @@ function normalizeLastMessageError(value) {
1885
1940
  return code && reason && message && referenceId && at !== null ? { code, reason, message, referenceId, at } : null;
1886
1941
  }
1887
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
+
1888
2072
  // src/channels/shared/context-enhancement.mjs
1889
2073
  var CONTEXT_ENHANCEMENT_FIELDS = Object.freeze([
1890
2074
  "channel",
@@ -1911,18 +2095,18 @@ var DEFAULT_CONTEXT_ENHANCEMENT_CONFIG = Object.freeze({
1911
2095
  })
1912
2096
  });
1913
2097
  var CONFIG_KEYS = ["group", "direct"];
1914
- var SCOPE_KEYS = ["enabled", "fields", "guidance"];
2098
+ var SCOPE_KEYS2 = ["enabled", "fields", "guidance"];
1915
2099
  var LEGACY_CONFIG_KEYS = ["groupEnabled", "directEnabled", "fields", "guidance"];
1916
2100
  function invalidConfig(message) {
1917
2101
  const error = new TypeError(message);
1918
2102
  error.code = "context-enhancement-invalid";
1919
2103
  return error;
1920
2104
  }
1921
- function hasExactKeys(input, keys) {
2105
+ function hasExactKeys2(input, keys) {
1922
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));
1923
2107
  }
1924
2108
  function validateContextEnhancementScope(input) {
1925
- if (!hasExactKeys(input, SCOPE_KEYS)) {
2109
+ if (!hasExactKeys2(input, SCOPE_KEYS2)) {
1926
2110
  throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
1927
2111
  }
1928
2112
  const { enabled, fields, guidance } = input;
@@ -1942,7 +2126,7 @@ function validateContextEnhancementScope(input) {
1942
2126
  });
1943
2127
  }
1944
2128
  function validateContextEnhancementConfig(input) {
1945
- if (!hasExactKeys(input, CONFIG_KEYS)) {
2129
+ if (!hasExactKeys2(input, CONFIG_KEYS)) {
1946
2130
  throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
1947
2131
  }
1948
2132
  return Object.freeze({
@@ -1951,7 +2135,7 @@ function validateContextEnhancementConfig(input) {
1951
2135
  });
1952
2136
  }
1953
2137
  function migrateLegacyContextEnhancementConfig(input) {
1954
- if (!hasExactKeys(input, LEGACY_CONFIG_KEYS)) {
2138
+ if (!hasExactKeys2(input, LEGACY_CONFIG_KEYS)) {
1955
2139
  throw invalidConfig("\u8BF7\u63D0\u4EA4\u5B8C\u6574\u7684\u4E0A\u4E0B\u6587\u589E\u5F3A\u8BBE\u7F6E\u3002");
1956
2140
  }
1957
2141
  return validateContextEnhancementConfig({
@@ -1991,7 +2175,8 @@ var DINGTALK_ENDPOINTS = Object.freeze({
1991
2175
  deleteBot: "bot.delete",
1992
2176
  setWorkspace: "bot.workspace.set",
1993
2177
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
1994
- setContextEnhancement: "bot.context-enhancement.set"
2178
+ setContextEnhancement: "bot.context-enhancement.set",
2179
+ setAccessPolicy: "bot.access-policy.set"
1995
2180
  });
1996
2181
  var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
1997
2182
  var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
@@ -2121,6 +2306,7 @@ function normalizeBot(value) {
2121
2306
  workspace: optionalString(value.workspace, 4096) ?? "",
2122
2307
  agentPreset: normalizeAgentPresetId(value.agentPreset),
2123
2308
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
2309
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
2124
2310
  bot: {
2125
2311
  name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
2126
2312
  clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
@@ -3230,7 +3416,7 @@ function SettingsGlyph() {
3230
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" })
3231
3417
  );
3232
3418
  }
3233
- function BotSettingsButton({ channel: channel4, botId, botName, connected }) {
3419
+ function BotSettingsButton({ channel: channel4, botId, botName, connected, accessPolicy }) {
3234
3420
  const { openBotSettings } = React9.useContext(BotSettingsContext);
3235
3421
  const tooltipId = React9.useId();
3236
3422
  return h2(
@@ -3246,7 +3432,8 @@ function BotSettingsButton({ channel: channel4, botId, botName, connected }) {
3246
3432
  channel: channel4,
3247
3433
  botId,
3248
3434
  botName,
3249
- connected: Boolean(connected)
3435
+ connected: Boolean(connected),
3436
+ accessPolicy
3250
3437
  })
3251
3438
  }, h2(SettingsGlyph)),
3252
3439
  h2("span", {
@@ -3809,7 +3996,8 @@ function AccountCard({
3809
3996
  channel: "dingtalk",
3810
3997
  botId: account.botId,
3811
3998
  botName: account.bot.name,
3812
- connected: account.connected
3999
+ connected: account.connected,
4000
+ accessPolicy: account.accessPolicy
3813
4001
  })
3814
4002
  )
3815
4003
  ),
@@ -4464,12 +4652,13 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4464
4652
  deleteBot: "bot.delete",
4465
4653
  setWorkspace: "bot.workspace.set",
4466
4654
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
4467
- setContextEnhancement: "bot.context-enhancement.set"
4655
+ setContextEnhancement: "bot.context-enhancement.set",
4656
+ setAccessPolicy: "bot.access-policy.set"
4468
4657
  });
4469
4658
  function createTokenChannelApi(channel4, connectionSummary, {
4470
4659
  normalizeBotExtension = () => ({})
4471
4660
  } = {}) {
4472
- const unwrapRpcResult11 = (result) => {
4661
+ const unwrapRpcResult12 = (result) => {
4473
4662
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
4474
4663
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
4475
4664
  }
@@ -4492,6 +4681,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
4492
4681
  workspace: text2(value.workspace, "", 4096),
4493
4682
  agentPreset: normalizeAgentPresetId(value.agentPreset),
4494
4683
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
4684
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
4495
4685
  bot: {
4496
4686
  name: text2(value.bot?.name, `${channel4}\u673A\u5668\u4EBA`, 100),
4497
4687
  username: text2(value.bot?.username, "", 100),
@@ -4529,7 +4719,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
4529
4719
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
4530
4720
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
4531
4721
  });
4532
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult11, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4722
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult12, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4533
4723
  }
4534
4724
 
4535
4725
  // plugin-src/client/channels/discord/api.js
@@ -4639,7 +4829,8 @@ function createTokenChannelSettings(definition) {
4639
4829
  channel: channel4.toLowerCase(),
4640
4830
  botId: account.botId,
4641
4831
  botName: account.bot.name,
4642
- connected: account.connected
4832
+ connected: account.connected,
4833
+ accessPolicy: account.accessPolicy
4643
4834
  })
4644
4835
  )
4645
4836
  ),
@@ -5075,6 +5266,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
5075
5266
  setWorkspace: "bot.workspace.set",
5076
5267
  setAgentPreset: "bot.preset.set",
5077
5268
  setContextEnhancement: "bot.context-enhancement.set",
5269
+ setAccessPolicy: "bot.access-policy.set",
5078
5270
  setGroupResponseMode: "bot.group-response-mode.set",
5079
5271
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
5080
5272
  testConnection: "connection.test",
@@ -5224,6 +5416,7 @@ function normalizeBotConnection(value, fallbackBotId) {
5224
5416
  workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
5225
5417
  agentPreset: normalizeAgentPresetId(value.agentPreset),
5226
5418
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
5419
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
5227
5420
  groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
5228
5421
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
5229
5422
  bot: normalizeBot2(value.bot),
@@ -5957,7 +6150,8 @@ function BotCard({
5957
6150
  channel: "feishu",
5958
6151
  botId: connection.botId,
5959
6152
  botName: bot.name,
5960
- connected
6153
+ connected,
6154
+ accessPolicy: connection.accessPolicy
5961
6155
  })
5962
6156
  )
5963
6157
  ),
@@ -7430,7 +7624,8 @@ var QQ_ENDPOINTS = Object.freeze({
7430
7624
  deleteBot: "bot.delete",
7431
7625
  setWorkspace: "bot.workspace.set",
7432
7626
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
7433
- setContextEnhancement: "bot.context-enhancement.set"
7627
+ setContextEnhancement: "bot.context-enhancement.set",
7628
+ setAccessPolicy: "bot.access-policy.set"
7434
7629
  });
7435
7630
  var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
7436
7631
  var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -7496,6 +7691,7 @@ function normalizeBot3(value) {
7496
7691
  workspace: text3(value.workspace, "", 4096),
7497
7692
  agentPreset: normalizeAgentPresetId(value.agentPreset),
7498
7693
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
7694
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
7499
7695
  bot: {
7500
7696
  name: text3(value.bot?.name, "QQ\u673A\u5668\u4EBA", 100),
7501
7697
  appIdMasked: text3(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -7836,7 +8032,8 @@ function AccountCard2({
7836
8032
  channel: "qq",
7837
8033
  botId: account.botId,
7838
8034
  botName: account.bot.name,
7839
- connected: account.connected
8035
+ connected: account.connected,
8036
+ accessPolicy: account.accessPolicy
7840
8037
  })
7841
8038
  )
7842
8039
  ),
@@ -8773,59 +8970,18 @@ var SlackAccountCard = channel2.AccountCard;
8773
8970
 
8774
8971
  // plugin-src/client/channels/telegram/api.js
8775
8972
  var TELEGRAM_RPC_CHANNEL = "/telegram";
8776
- var TELEGRAM_ENDPOINTS = Object.freeze({
8777
- ...TOKEN_BOT_ENDPOINTS,
8778
- setAccessPolicy: "bot.access-policy.set"
8779
- });
8780
- var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2", {
8781
- normalizeBotExtension: (value) => {
8782
- const source = value?.accessPolicy;
8783
- const accessMode = source?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible";
8784
- const allowedUsers = Array.isArray(source?.allowedUsers) ? [...new Set(source.allowedUsers.filter((entry) => typeof entry === "string" && /^[1-9]\d{0,15}$/.test(entry)))] : [];
8785
- return { accessPolicy: { accessMode, allowedUsers } };
8786
- }
8787
- });
8973
+ var TELEGRAM_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
8974
+ var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2");
8788
8975
  var unwrapRpcResult6 = api3.unwrapRpcResult;
8789
8976
  var normalizeSnapshot5 = api3.normalizeSnapshot;
8790
8977
  var presentError6 = api3.presentError;
8791
8978
 
8792
- // plugin-src/client/channels/telegram/index.js
8793
- var React17 = __toESM(require("react"), 1);
8794
-
8795
8979
  // plugin-src/client/channels/telegram/styles.js
8796
8980
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
8797
8981
  var CSS7 = String.raw`
8798
8982
  .dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
8799
8983
  .dtg-avatar { color: #fff; background: #229ed9; }
8800
8984
  .dtg-avatar svg { display: block; }
8801
- .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); }
8802
- .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; }
8803
- .dtg-accessHeading > strong { min-width: 0; font-size: 13px; overflow-wrap: anywhere; }
8804
- .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; }
8805
- .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; }
8806
- .dtg-accessBadge[data-mode="private-allowlist"] { color: #a15c00; background: #fff3d6; }
8807
- .dtg-accessHelp { position: static; display: inline-flex; flex: none; }
8808
- .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; }
8809
- .dtg-accessHelpButton:hover { border-color: #229ed9; color: #1178a8; background: #eaf7fd; }
8810
- .dtg-accessHelpButton:focus-visible { outline: none; border-color: #229ed9; box-shadow: 0 0 0 3px color-mix(in srgb, #229ed9 18%, transparent); }
8811
- .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; }
8812
- .dtg-accessTooltipItem { display: grid; gap: 2px; }
8813
- .dtg-accessTooltipItem + .dtg-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
8814
- .dtg-accessTooltipItem strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 17px; font-weight: 700; }
8815
- .dtg-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; font-weight: 400; }
8816
- .dtg-accessHelp:hover .dtg-accessTooltip, .dtg-accessHelp:focus-within .dtg-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
8817
- .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; }
8818
- .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; }
8819
- .dtg-accessField select { height: 34px; padding: 0 9px; }
8820
- .dtg-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
8821
- .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; }
8822
- .dtg-accessField > span, .dtg-accessField small { overflow-wrap: anywhere; }
8823
- .dtg-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
8824
- .dtg-accessWarning, .dtg-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
8825
- .dtg-accessWarning { color: #a15c00; }
8826
- .dtg-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
8827
- .dtg-accessActions { min-width: 0; max-width: 100%; display: flex; justify-content: flex-end; flex-wrap: wrap; }
8828
- .dtg-accessActions .ddt-button { max-width: 100%; white-space: normal; }
8829
8985
  `;
8830
8986
  function installTelegramStyles() {
8831
8987
  if (typeof document === "undefined") return () => {
@@ -8842,146 +8998,6 @@ function installTelegramStyles() {
8842
8998
  }
8843
8999
 
8844
9000
  // plugin-src/client/channels/telegram/index.js
8845
- function policyFor(account) {
8846
- return {
8847
- accessMode: account?.accessPolicy?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible",
8848
- allowedUsers: Array.isArray(account?.accessPolicy?.allowedUsers) ? account.accessPolicy.allowedUsers : []
8849
- };
8850
- }
8851
- function allowedUsersFromText(value) {
8852
- const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
8853
- if (entries.some((entry) => !/^[1-9]\d{0,15}$/.test(entry))) {
8854
- throw new TypeError("User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
8855
- }
8856
- return [...new Set(entries)];
8857
- }
8858
- function TelegramAccessSettings({ account, busy = false, onSave }) {
8859
- const policy = policyFor(account);
8860
- const sourceUsers = policy.allowedUsers.join("\n");
8861
- const accessHelpId = React17.useId();
8862
- const [accessMode, setAccessMode] = React17.useState(policy.accessMode);
8863
- const [allowedUsers, setAllowedUsers] = React17.useState(sourceUsers);
8864
- const [error, setError] = React17.useState(null);
8865
- React17.useEffect(() => {
8866
- setAccessMode(policy.accessMode);
8867
- setAllowedUsers(sourceUsers);
8868
- setError(null);
8869
- }, [policy.accessMode, sourceUsers]);
8870
- const save = async (event) => {
8871
- event.preventDefault();
8872
- setError(null);
8873
- try {
8874
- const normalized = allowedUsersFromText(allowedUsers);
8875
- if (typeof onSave !== "function") throw new Error("Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
8876
- await onSave({ accessMode, allowedUsers: normalized });
8877
- } catch (caught) {
8878
- setError(caught?.message ?? "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
8879
- }
8880
- };
8881
- const privateAllowlist = accessMode === "private-allowlist";
8882
- const savedPrivateAllowlist = policy.accessMode === "private-allowlist";
8883
- const emptyAllowlist = privateAllowlist && allowedUsers.trim() === "";
8884
- return h2(
8885
- "form",
8886
- { className: "dtg-access", onSubmit: save },
8887
- h2(
8888
- "div",
8889
- { className: "dtg-accessHeading" },
8890
- h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
8891
- h2(
8892
- "span",
8893
- { className: "dtg-accessStatus" },
8894
- h2(
8895
- "span",
8896
- { className: "dtg-accessBadge", "data-mode": policy.accessMode },
8897
- savedPrivateAllowlist ? "\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F" : "\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F"
8898
- ),
8899
- h2(
8900
- "span",
8901
- { className: "dtg-accessHelp" },
8902
- h2("button", {
8903
- type: "button",
8904
- className: "dtg-accessHelpButton",
8905
- "aria-label": "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
8906
- "aria-describedby": accessHelpId
8907
- }, h2("span", { "aria-hidden": "true" }, "?")),
8908
- h2(
8909
- "span",
8910
- {
8911
- id: accessHelpId,
8912
- className: "dtg-accessTooltip",
8913
- role: "tooltip"
8914
- },
8915
- h2(
8916
- "span",
8917
- { className: "dtg-accessTooltipItem" },
8918
- h2("strong", null, "\u517C\u5BB9\u6A21\u5F0F"),
8919
- 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")
8920
- ),
8921
- h2(
8922
- "span",
8923
- { className: "dtg-accessTooltipItem" },
8924
- h2("strong", null, "\u5B89\u5168\u6A21\u5F0F"),
8925
- h2("span", null, "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002")
8926
- )
8927
- )
8928
- )
8929
- )
8930
- ),
8931
- h2(
8932
- "label",
8933
- { className: "dtg-accessField" },
8934
- h2("span", null, "\u6A21\u5F0F"),
8935
- h2(
8936
- "select",
8937
- {
8938
- value: accessMode,
8939
- disabled: busy,
8940
- "aria-label": "Telegram \u8BBF\u95EE\u6A21\u5F0F",
8941
- onChange: (event) => {
8942
- setAccessMode(event.target.value);
8943
- setError(null);
8944
- }
8945
- },
8946
- h2("option", { value: "compatible" }, "\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
8947
- h2("option", { value: "private-allowlist" }, "\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09")
8948
- )
8949
- ),
8950
- h2(
8951
- "label",
8952
- { className: "dtg-accessField" },
8953
- h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID"),
8954
- h2("textarea", {
8955
- value: allowedUsers,
8956
- disabled: busy || !privateAllowlist,
8957
- rows: 3,
8958
- placeholder: "\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID",
8959
- "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID",
8960
- onChange: (event) => {
8961
- setAllowedUsers(event.target.value);
8962
- setError(null);
8963
- }
8964
- }),
8965
- 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")
8966
- ),
8967
- emptyAllowlist ? h2(
8968
- "p",
8969
- { className: "dtg-accessWarning", role: "status" },
8970
- "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002"
8971
- ) : null,
8972
- error ? h2("p", { className: "dtg-accessError", role: "alert" }, error) : null,
8973
- h2(
8974
- "div",
8975
- { className: "dtg-accessActions" },
8976
- h2("button", {
8977
- type: "submit",
8978
- className: "ddt-button",
8979
- "data-kind": "secondary",
8980
- disabled: busy
8981
- }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
8982
- )
8983
- );
8984
- }
8985
9001
  var channel3 = createTokenChannelSettings({
8986
9002
  channel: "Telegram",
8987
9003
  endpoints: TELEGRAM_ENDPOINTS,
@@ -8994,9 +9010,7 @@ var channel3 = createTokenChannelSettings({
8994
9010
  tokenPlaceholder: "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token",
8995
9011
  emptyTitle: "\u63A5\u5165 Telegram \u673A\u5668\u4EBA",
8996
9012
  emptyDescription: "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002",
8997
- platformLabel: "Telegram",
8998
- AccountSettings: TelegramAccessSettings,
8999
- accountSettingsEndpoint: TELEGRAM_ENDPOINTS.setAccessPolicy
9013
+ platformLabel: "Telegram"
9000
9014
  });
9001
9015
  var TelegramSettingsTab = channel3.SettingsTab;
9002
9016
  var TelegramAccountCard = channel3.AccountCard;
@@ -9013,7 +9027,8 @@ var WECOM_ENDPOINTS = Object.freeze({
9013
9027
  deleteBot: "bot.delete",
9014
9028
  setWorkspace: "bot.workspace.set",
9015
9029
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
9016
- setContextEnhancement: "bot.context-enhancement.set"
9030
+ setContextEnhancement: "bot.context-enhancement.set",
9031
+ setAccessPolicy: "bot.access-policy.set"
9017
9032
  });
9018
9033
  var PROVISION_STATES3 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
9019
9034
  var ACCOUNT_STATES4 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -9085,6 +9100,7 @@ function normalizeBot4(value) {
9085
9100
  workspace: text4(value.workspace, "", 4096),
9086
9101
  agentPreset: normalizeAgentPresetId(value.agentPreset),
9087
9102
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
9103
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
9088
9104
  bot: {
9089
9105
  name: text4(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA", 100),
9090
9106
  appIdMasked: text4(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -9125,7 +9141,7 @@ function formatRemaining4(milliseconds) {
9125
9141
  }
9126
9142
 
9127
9143
  // plugin-src/client/channels/wecom/index.js
9128
- var React18 = __toESM(require("react"), 1);
9144
+ var React17 = __toESM(require("react"), 1);
9129
9145
 
9130
9146
  // plugin-src/client/channels/wecom/styles.js
9131
9147
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -9150,7 +9166,7 @@ function installWecomStyles() {
9150
9166
 
9151
9167
  // plugin-src/client/channels/wecom/index.js
9152
9168
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
9153
- 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) {
9154
9170
  return h2("button", {
9155
9171
  ...props,
9156
9172
  ref,
@@ -9408,7 +9424,8 @@ function AccountCard3({
9408
9424
  channel: "wecom",
9409
9425
  botId: account.botId,
9410
9426
  botName: account.bot.name,
9411
- connected: account.connected
9427
+ connected: account.connected,
9428
+ accessPolicy: account.accessPolicy
9412
9429
  })
9413
9430
  )
9414
9431
  ),
@@ -9461,27 +9478,27 @@ function AccountCard3({
9461
9478
  );
9462
9479
  }
9463
9480
  function WecomSettingsTab({ rpcCall }) {
9464
- const [model, setModel] = React18.useState({
9481
+ const [model, setModel] = React17.useState({
9465
9482
  phase: "loading",
9466
9483
  bots: [],
9467
9484
  totals: { configured: 0, connected: 0 },
9468
9485
  error: null,
9469
9486
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
9470
9487
  });
9471
- const [provision, setProvision] = React18.useState(null);
9472
- const [busy, setBusy] = React18.useState(false);
9473
- const [busyByBot, setBusyByBot] = React18.useState({});
9474
- const [feedbackByBot, setFeedbackByBot] = React18.useState({});
9475
- const [removeTarget, setRemoveTarget] = React18.useState(null);
9476
- const [credentialOpen, setCredentialOpen] = React18.useState(false);
9477
- const [credentialError, setCredentialError] = React18.useState(null);
9478
- const [notice, setNotice] = React18.useState("");
9479
- const [now, setNow] = React18.useState(Date.now());
9480
- 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);
9481
9498
  const workspaceFence = useWorkspaceSnapshotFence();
9482
- const addButtonRef = React18.useRef(null);
9483
- const noticeFrameRef = React18.useRef(null);
9484
- const announce = React18.useCallback((message) => {
9499
+ const addButtonRef = React17.useRef(null);
9500
+ const noticeFrameRef = React17.useRef(null);
9501
+ const announce = React17.useCallback((message) => {
9485
9502
  if (!mounted.current) return;
9486
9503
  if (noticeFrameRef.current !== null) {
9487
9504
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -9495,7 +9512,7 @@ function WecomSettingsTab({ rpcCall }) {
9495
9512
  });
9496
9513
  }
9497
9514
  }, []);
9498
- React18.useEffect(() => {
9515
+ React17.useEffect(() => {
9499
9516
  const disposeDingtalk = installDingtalkStyles();
9500
9517
  const disposeWecom = installWecomStyles();
9501
9518
  mounted.current = true;
@@ -9509,11 +9526,11 @@ function WecomSettingsTab({ rpcCall }) {
9509
9526
  disposeDingtalk();
9510
9527
  };
9511
9528
  }, []);
9512
- const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
9529
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
9513
9530
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
9514
9531
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
9515
9532
  }, [rpcCall]);
9516
- const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9533
+ const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9517
9534
  const workspaceVersion = workspaceFence.beginStatus();
9518
9535
  if (workspaceVersion === null) return void 0;
9519
9536
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -9539,12 +9556,12 @@ function WecomSettingsTab({ rpcCall }) {
9539
9556
  return void 0;
9540
9557
  }
9541
9558
  }, [invoke, workspaceFence]);
9542
- React18.useEffect(() => {
9559
+ React17.useEffect(() => {
9543
9560
  const controller = new AbortController();
9544
9561
  void loadStatus({ signal: controller.signal, restore: true });
9545
9562
  return () => controller.abort();
9546
9563
  }, [loadStatus]);
9547
- React18.useEffect(() => {
9564
+ React17.useEffect(() => {
9548
9565
  if (model.phase !== "ready") return void 0;
9549
9566
  const controller = new AbortController();
9550
9567
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -9553,12 +9570,12 @@ function WecomSettingsTab({ rpcCall }) {
9553
9570
  window.clearInterval(timer);
9554
9571
  };
9555
9572
  }, [loadStatus, model.phase]);
9556
- React18.useEffect(() => {
9573
+ React17.useEffect(() => {
9557
9574
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
9558
9575
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
9559
9576
  return () => window.clearInterval(timer);
9560
9577
  }, [provision?.attemptId, provision?.status]);
9561
- const startProvisioning = React18.useCallback(async (replace = false) => {
9578
+ const startProvisioning = React17.useCallback(async (replace = false) => {
9562
9579
  setCredentialOpen(false);
9563
9580
  setCredentialError(null);
9564
9581
  setBusy(true);
@@ -9576,7 +9593,7 @@ function WecomSettingsTab({ rpcCall }) {
9576
9593
  if (mounted.current) setBusy(false);
9577
9594
  }
9578
9595
  }, [invoke, provision?.attemptId]);
9579
- const bindCredentials = React18.useCallback(async ({ identity, secret }) => {
9596
+ const bindCredentials = React17.useCallback(async ({ identity, secret }) => {
9580
9597
  const snapshotVersion = workspaceFence.beginMutation();
9581
9598
  setBusy(true);
9582
9599
  setCredentialError(null);
@@ -9604,7 +9621,7 @@ function WecomSettingsTab({ rpcCall }) {
9604
9621
  if (mounted.current) setBusy(false);
9605
9622
  }
9606
9623
  }, [invoke, loadStatus, workspaceFence]);
9607
- const closeProvision = React18.useCallback(async () => {
9624
+ const closeProvision = React17.useCallback(async () => {
9608
9625
  setBusy(true);
9609
9626
  try {
9610
9627
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -9615,7 +9632,7 @@ function WecomSettingsTab({ rpcCall }) {
9615
9632
  if (mounted.current) setBusy(false);
9616
9633
  }
9617
9634
  }, [invoke, provision?.attemptId, provision?.status]);
9618
- React18.useEffect(() => {
9635
+ React17.useEffect(() => {
9619
9636
  const attemptId = provision?.attemptId;
9620
9637
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
9621
9638
  const controller = new AbortController();
@@ -9645,7 +9662,7 @@ function WecomSettingsTab({ rpcCall }) {
9645
9662
  window.clearTimeout(timer);
9646
9663
  };
9647
9664
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
9648
- const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
9665
+ const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
9649
9666
  const snapshotVersion = workspaceFence.beginMutation();
9650
9667
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
9651
9668
  try {
@@ -9670,7 +9687,7 @@ function WecomSettingsTab({ rpcCall }) {
9670
9687
  });
9671
9688
  }
9672
9689
  }, [invoke, loadStatus, workspaceFence]);
9673
- const reconnect = React18.useCallback(async (account) => {
9690
+ const reconnect = React17.useCallback(async (account) => {
9674
9691
  setFeedbackByBot((current) => {
9675
9692
  const next = { ...current };
9676
9693
  delete next[account.botId];
@@ -9797,7 +9814,7 @@ function WecomSettingsTab({ rpcCall }) {
9797
9814
  }),
9798
9815
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
9799
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(
9800
- React18.Fragment,
9817
+ React17.Fragment,
9801
9818
  null,
9802
9819
  credentialView,
9803
9820
  provisionView,
@@ -9808,7 +9825,7 @@ function WecomSettingsTab({ rpcCall }) {
9808
9825
  }
9809
9826
 
9810
9827
  // plugin-src/client/channels/weixin/index.js
9811
- var React19 = __toESM(require("react"), 1);
9828
+ var React18 = __toESM(require("react"), 1);
9812
9829
 
9813
9830
  // plugin-src/client/channels/weixin/api.js
9814
9831
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -9822,7 +9839,8 @@ var WEIXIN_ENDPOINTS = Object.freeze({
9822
9839
  deleteBot: "bot.delete",
9823
9840
  setWorkspace: "bot.workspace.set",
9824
9841
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
9825
- setContextEnhancement: "bot.context-enhancement.set"
9842
+ setContextEnhancement: "bot.context-enhancement.set",
9843
+ setAccessPolicy: "bot.access-policy.set"
9826
9844
  });
9827
9845
  var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
9828
9846
  var PROVISION_STATES4 = /* @__PURE__ */ new Set([
@@ -9914,6 +9932,7 @@ function normalizeBot5(value) {
9914
9932
  workspace: string(value.workspace).slice(0, 4096),
9915
9933
  agentPreset: normalizeAgentPresetId(value.agentPreset),
9916
9934
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
9935
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
9917
9936
  bot: {
9918
9937
  name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
9919
9938
  accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
@@ -9965,7 +9984,7 @@ function formatRemaining5(milliseconds) {
9965
9984
  }
9966
9985
 
9967
9986
  // plugin-src/client/channels/weixin/index.js
9968
- 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) {
9969
9988
  return h2("button", {
9970
9989
  ...props,
9971
9990
  ref,
@@ -10038,14 +10057,14 @@ function EmptyView5({ onStart, busy }) {
10038
10057
  );
10039
10058
  }
10040
10059
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10041
- const [imageFailed, setImageFailed] = React19.useState(false);
10060
+ const [imageFailed, setImageFailed] = React18.useState(false);
10042
10061
  const source = safeQrSource5(provision.qrCodeDataUrl);
10043
10062
  const href = safeVerificationUrl(provision.verificationUrl);
10044
10063
  const remaining = Math.max(0, provision.expiresAt - now);
10045
10064
  const expired = remaining === 0 || provision.status === "expired";
10046
10065
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
10047
10066
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
10048
- React19.useEffect(() => setImageFailed(false), [source]);
10067
+ React18.useEffect(() => setImageFailed(false), [source]);
10049
10068
  return h2(
10050
10069
  "div",
10051
10070
  { className: "dxw-card dim-surfaceCard" },
@@ -10112,9 +10131,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10112
10131
  );
10113
10132
  }
10114
10133
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
10115
- const [code, setCode] = React19.useState("");
10134
+ const [code, setCode] = React18.useState("");
10116
10135
  const valid = /^\d{4,8}$/.test(code);
10117
- React19.useEffect(() => setCode(""), [provision.attemptId]);
10136
+ React18.useEffect(() => setCode(""), [provision.attemptId]);
10118
10137
  return h2(
10119
10138
  "div",
10120
10139
  { className: "dxw-card dim-surfaceCard" },
@@ -10251,7 +10270,8 @@ function AccountCard4({
10251
10270
  channel: "weixin",
10252
10271
  botId: account.botId,
10253
10272
  botName: account.bot.name,
10254
- connected: account.connected
10273
+ connected: account.connected,
10274
+ accessPolicy: account.accessPolicy
10255
10275
  })
10256
10276
  )
10257
10277
  ),
@@ -10357,7 +10377,7 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
10357
10377
  };
10358
10378
  }
10359
10379
  function WeixinSettingsTab({ rpcCall }) {
10360
- const [model, setModel] = React19.useState({
10380
+ const [model, setModel] = React18.useState({
10361
10381
  phase: "loading",
10362
10382
  bots: [],
10363
10383
  totals: EMPTY_TOTALS3,
@@ -10365,33 +10385,33 @@ function WeixinSettingsTab({ rpcCall }) {
10365
10385
  error: null,
10366
10386
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
10367
10387
  });
10368
- const [provision, setProvision] = React19.useState(null);
10369
- const [busy, setBusy] = React19.useState(false);
10370
- const [busyByBot, setBusyByBot] = React19.useState({});
10371
- const [feedbackByBot, setFeedbackByBot] = React19.useState({});
10372
- const [removeTarget, setRemoveTarget] = React19.useState(null);
10373
- const [notice, setNotice] = React19.useState("");
10374
- const [now, setNow] = React19.useState(() => Date.now());
10375
- const addButtonRef = React19.useRef(null);
10376
- 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);
10377
10397
  const workspaceFence = useWorkspaceSnapshotFence();
10378
10398
  const scheduleAnimationFrame = useAnimationFrameScheduler();
10379
- React19.useEffect(() => {
10399
+ React18.useEffect(() => {
10380
10400
  mountedRef.current = true;
10381
10401
  return () => {
10382
10402
  mountedRef.current = false;
10383
10403
  };
10384
10404
  }, []);
10385
- const announce = React19.useCallback((value) => {
10405
+ const announce = React18.useCallback((value) => {
10386
10406
  setNotice("");
10387
10407
  scheduleAnimationFrame(() => {
10388
10408
  if (value) setNotice(value);
10389
10409
  }, "announcement");
10390
10410
  }, [scheduleAnimationFrame]);
10391
- const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
10411
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
10392
10412
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
10393
10413
  }, [rpcCall]);
10394
- const loadStatus = React19.useCallback(async ({
10414
+ const loadStatus = React18.useCallback(async ({
10395
10415
  signal,
10396
10416
  silent = false,
10397
10417
  restoreProvisioning = false
@@ -10428,12 +10448,12 @@ function WeixinSettingsTab({ rpcCall }) {
10428
10448
  return void 0;
10429
10449
  }
10430
10450
  }, [invoke, workspaceFence]);
10431
- React19.useEffect(() => {
10451
+ React18.useEffect(() => {
10432
10452
  const controller = new AbortController();
10433
10453
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
10434
10454
  return () => controller.abort();
10435
10455
  }, [loadStatus]);
10436
- React19.useEffect(() => {
10456
+ React18.useEffect(() => {
10437
10457
  if (model.phase !== "ready") return void 0;
10438
10458
  const controller = new AbortController();
10439
10459
  let running = false;
@@ -10452,12 +10472,12 @@ function WeixinSettingsTab({ rpcCall }) {
10452
10472
  window.clearInterval(timer);
10453
10473
  };
10454
10474
  }, [loadStatus, model.phase]);
10455
- React19.useEffect(() => {
10475
+ React18.useEffect(() => {
10456
10476
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
10457
10477
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
10458
10478
  return () => window.clearInterval(timer);
10459
10479
  }, [provision?.attemptId, provision?.status]);
10460
- const startProvisioning = React19.useCallback(async ({ replace = false } = {}) => {
10480
+ const startProvisioning = React18.useCallback(async ({ replace = false } = {}) => {
10461
10481
  setBusy(true);
10462
10482
  try {
10463
10483
  if (replace && provision?.attemptId) {
@@ -10478,7 +10498,7 @@ function WeixinSettingsTab({ rpcCall }) {
10478
10498
  setBusy(false);
10479
10499
  }
10480
10500
  }, [announce, invoke, provision?.attemptId]);
10481
- const cancelProvisioning = React19.useCallback(async () => {
10501
+ const cancelProvisioning = React18.useCallback(async () => {
10482
10502
  setBusy(true);
10483
10503
  try {
10484
10504
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -10493,7 +10513,7 @@ function WeixinSettingsTab({ rpcCall }) {
10493
10513
  setBusy(false);
10494
10514
  }
10495
10515
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
10496
- const submitVerification = React19.useCallback(async (verifyCode) => {
10516
+ const submitVerification = React18.useCallback(async (verifyCode) => {
10497
10517
  if (!provision?.attemptId) return;
10498
10518
  setBusy(true);
10499
10519
  try {
@@ -10509,7 +10529,7 @@ function WeixinSettingsTab({ rpcCall }) {
10509
10529
  setBusy(false);
10510
10530
  }
10511
10531
  }, [announce, invoke, provision?.attemptId]);
10512
- React19.useEffect(() => {
10532
+ React18.useEffect(() => {
10513
10533
  const attemptId = provision?.attemptId;
10514
10534
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
10515
10535
  const controller = new AbortController();
@@ -10557,7 +10577,7 @@ function WeixinSettingsTab({ rpcCall }) {
10557
10577
  controller.abort();
10558
10578
  };
10559
10579
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
10560
- const setBotBusy = React19.useCallback((botId, value) => {
10580
+ const setBotBusy = React18.useCallback((botId, value) => {
10561
10581
  setBusyByBot((current) => {
10562
10582
  const next = { ...current };
10563
10583
  if (value) next[botId] = value;
@@ -10565,7 +10585,7 @@ function WeixinSettingsTab({ rpcCall }) {
10565
10585
  return next;
10566
10586
  });
10567
10587
  }, []);
10568
- const reconnect = React19.useCallback(async (account) => {
10588
+ const reconnect = React18.useCallback(async (account) => {
10569
10589
  const snapshotVersion = workspaceFence.beginMutation();
10570
10590
  setBotBusy(account.botId, "reconnect");
10571
10591
  setFeedbackByBot((current) => {
@@ -10616,7 +10636,7 @@ function WeixinSettingsTab({ rpcCall }) {
10616
10636
  setBotBusy(account.botId, null);
10617
10637
  }
10618
10638
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
10619
- const saveWorkspace = React19.useCallback(async (account, workspace) => {
10639
+ const saveWorkspace = React18.useCallback(async (account, workspace) => {
10620
10640
  const workspaceVersion = workspaceFence.beginMutation();
10621
10641
  setBotBusy(account.botId, "workspace");
10622
10642
  try {
@@ -10640,7 +10660,7 @@ function WeixinSettingsTab({ rpcCall }) {
10640
10660
  if (mountedRef.current) setBotBusy(account.botId, null);
10641
10661
  }
10642
10662
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10643
- const saveBotSetting = React19.useCallback(async (account, operation, endpoint, payload) => {
10663
+ const saveBotSetting = React18.useCallback(async (account, operation, endpoint, payload) => {
10644
10664
  const snapshotVersion = workspaceFence.beginMutation();
10645
10665
  setBotBusy(account.botId, operation);
10646
10666
  try {
@@ -10664,7 +10684,7 @@ function WeixinSettingsTab({ rpcCall }) {
10664
10684
  if (mountedRef.current) setBotBusy(account.botId, null);
10665
10685
  }
10666
10686
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
10667
- const remove = React19.useCallback(async (account) => {
10687
+ const remove = React18.useCallback(async (account) => {
10668
10688
  const snapshotVersion = workspaceFence.beginMutation();
10669
10689
  setBotBusy(account.botId, "delete");
10670
10690
  try {
@@ -10748,7 +10768,7 @@ function WeixinSettingsTab({ rpcCall }) {
10748
10768
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
10749
10769
  )
10750
10770
  ) : h2(
10751
- React19.Fragment,
10771
+ React18.Fragment,
10752
10772
  null,
10753
10773
  provisionView,
10754
10774
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -10975,12 +10995,7 @@ function normalizeBot6(value) {
10975
10995
  workspace: text5(value.workspace, "", 4096),
10976
10996
  agentPreset: normalizeAgentPresetId(value.agentPreset),
10977
10997
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
10978
- accessPolicy: {
10979
- accessMode: ["self-only", "private-allowlist", "open"].includes(
10980
- value.accessPolicy?.accessMode
10981
- ) ? value.accessPolicy.accessMode : "self-only",
10982
- allowedNumbers: Array.isArray(value.accessPolicy?.allowedNumbers) ? [...new Set(value.accessPolicy.allowedNumbers.filter((entry) => typeof entry === "string" && /^[1-9]\d{4,14}$/.test(entry)))] : []
10983
- },
10998
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
10984
10999
  bot: {
10985
11000
  name: text5(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
10986
11001
  idMasked: text5(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
@@ -11022,7 +11037,7 @@ function formatRemaining6(milliseconds) {
11022
11037
  }
11023
11038
 
11024
11039
  // plugin-src/client/channels/whatsapp/index.js
11025
- var React20 = __toESM(require("react"), 1);
11040
+ var React19 = __toESM(require("react"), 1);
11026
11041
 
11027
11042
  // plugin-src/client/channels/whatsapp/styles.js
11028
11043
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -11030,31 +11045,6 @@ var CSS10 = String.raw`
11030
11045
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
11031
11046
  .dwa-avatar { color: #fff; background: #25d366; }
11032
11047
  .dwa-avatar svg { display: block; }
11033
- .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); }
11034
- .dwa-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
11035
- .dwa-accessHeading > strong { font-size: 13px; }
11036
- .dwa-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
11037
- .dwa-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #08785f; background: #eafbf0; font-size: 11px; font-weight: 700; }
11038
- .dwa-accessBadge[data-mode="private-allowlist"] { color: #0f6f8f; background: #eaf7fd; }
11039
- .dwa-accessBadge[data-mode="open"] { color: #a15c00; background: #fff3d6; }
11040
- .dwa-accessHelp { position: relative; display: inline-flex; flex: none; }
11041
- .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; }
11042
- .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; }
11043
- .dwa-accessTooltipItem { display: grid; gap: 2px; }
11044
- .dwa-accessTooltipItem + .dwa-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
11045
- .dwa-accessTooltipItem strong { font-size: 12px; line-height: 17px; }
11046
- .dwa-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
11047
- .dwa-accessHelp:hover .dwa-accessTooltip, .dwa-accessHelp:focus-within .dwa-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
11048
- .dwa-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
11049
- .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; }
11050
- .dwa-accessField select { height: 34px; padding: 0 9px; }
11051
- .dwa-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
11052
- .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; }
11053
- .dwa-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
11054
- .dwa-accessWarning, .dwa-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
11055
- .dwa-accessWarning { color: #a15c00; }
11056
- .dwa-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
11057
- .dwa-accessActions { display: flex; justify-content: flex-end; }
11058
11048
  `;
11059
11049
  function installWhatsappStyles() {
11060
11050
  if (typeof document === "undefined") return () => {
@@ -11072,193 +11062,43 @@ function installWhatsappStyles() {
11072
11062
 
11073
11063
  // plugin-src/client/channels/whatsapp/index.js
11074
11064
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
11075
- function accessPolicyFor(account) {
11076
- const accessMode = ["self-only", "private-allowlist", "open"].includes(
11077
- account?.accessPolicy?.accessMode
11078
- ) ? account.accessPolicy.accessMode : "self-only";
11079
- return {
11080
- accessMode,
11081
- allowedNumbers: Array.isArray(account?.accessPolicy?.allowedNumbers) ? account.accessPolicy.allowedNumbers : []
11082
- };
11065
+ var Button14 = React19.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11066
+ return h2("button", {
11067
+ ...props,
11068
+ ref,
11069
+ type: "button",
11070
+ className: `ddt-button ${className}`.trim(),
11071
+ "data-kind": kind
11072
+ }, children);
11073
+ });
11074
+ function checkedTime6(value) {
11075
+ if (!value) return "\u5C1A\u672A\u68C0\u67E5";
11076
+ try {
11077
+ return new Intl.DateTimeFormat("zh-CN", {
11078
+ hour: "2-digit",
11079
+ minute: "2-digit",
11080
+ second: "2-digit"
11081
+ }).format(new Date(value));
11082
+ } catch {
11083
+ return "\u521A\u521A";
11084
+ }
11083
11085
  }
11084
- function allowedNumbersFromText(value) {
11085
- const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
11086
- const normalized = entries.map((entry) => entry.replace(/^\+/, ""));
11087
- if (normalized.some((entry) => !/^[1-9]\d{4,14}$/.test(entry))) {
11088
- throw new TypeError("\u7535\u8BDD\u53F7\u7801\u5FC5\u987B\u5305\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
11086
+ function connectionTestNotice3(value) {
11087
+ if (value?.testMessage?.sent === true) {
11088
+ return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230 WhatsApp \u81EA\u804A\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
11089
11089
  }
11090
- return [...new Set(normalized)];
11091
- }
11092
- function WhatsappAccessSettings({ account, busy = false, onSave }) {
11093
- const policy = accessPolicyFor(account);
11094
- const sourceNumbers = policy.allowedNumbers.join("\n");
11095
- const helpId = React20.useId();
11096
- const [accessMode, setAccessMode] = React20.useState(policy.accessMode);
11097
- const [allowedNumbers, setAllowedNumbers] = React20.useState(sourceNumbers);
11098
- const [error, setError] = React20.useState(null);
11099
- React20.useEffect(() => {
11100
- setAccessMode(policy.accessMode);
11101
- setAllowedNumbers(sourceNumbers);
11102
- setError(null);
11103
- }, [policy.accessMode, sourceNumbers]);
11104
- const save = async (event) => {
11105
- event.preventDefault();
11106
- setError(null);
11107
- try {
11108
- const normalized = allowedNumbersFromText(allowedNumbers);
11109
- if (typeof onSave !== "function") throw new Error("WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
11110
- await onSave({ accessMode, allowedNumbers: normalized });
11111
- } catch (caught) {
11112
- setError(caught?.message ?? "WhatsApp \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
11113
- }
11114
- };
11115
- const allowlistEnabled = accessMode === "private-allowlist";
11116
- const labels = {
11117
- "self-only": "\u4EC5\u81EA\u5DF1\u6A21\u5F0F",
11118
- "private-allowlist": "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F",
11119
- open: "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"
11120
- };
11090
+ if (value?.testMessage?.code === "test-target-unavailable") {
11091
+ return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684 WhatsApp \u81EA\u804A\u76EE\u6807\u3002";
11092
+ }
11093
+ return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
11094
+ }
11095
+ function Heading6({ totals, busy, onAdd, addButtonRef }) {
11121
11096
  return h2(
11122
- "form",
11123
- { className: "dwa-access", onSubmit: save },
11097
+ "div",
11098
+ { className: "ddt-heading" },
11124
11099
  h2(
11125
11100
  "div",
11126
- { className: "dwa-accessHeading" },
11127
- h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
11128
- h2(
11129
- "span",
11130
- { className: "dwa-accessStatus" },
11131
- h2(
11132
- "span",
11133
- { className: "dwa-accessBadge", "data-mode": policy.accessMode },
11134
- ["\u5DF2\u751F\u6548\uFF1A", labels[policy.accessMode]]
11135
- ),
11136
- h2(
11137
- "span",
11138
- { className: "dwa-accessHelp" },
11139
- h2("button", {
11140
- type: "button",
11141
- className: "dwa-accessHelpButton",
11142
- "aria-label": "\u67E5\u770B WhatsApp \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
11143
- "aria-describedby": helpId
11144
- }, h2("span", { "aria-hidden": "true" }, "?")),
11145
- h2(
11146
- "span",
11147
- { id: helpId, className: "dwa-accessTooltip", role: "tooltip" },
11148
- h2(
11149
- "span",
11150
- { className: "dwa-accessTooltipItem" },
11151
- h2("strong", null, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F"),
11152
- h2("span", null, "\u53EA\u54CD\u5E94\u5DF2\u7ED1\u5B9A WhatsApp \u8D26\u53F7\u7684\u81EA\u804A\u6D88\u606F\u3002")
11153
- ),
11154
- h2(
11155
- "span",
11156
- { className: "dwa-accessTooltipItem" },
11157
- h2("strong", null, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
11158
- h2("span", null, "\u54CD\u5E94\u81EA\u804A\u548C\u767D\u540D\u5355\u8054\u7CFB\u4EBA\u7684\u79C1\u804A\uFF0C\u5FFD\u7565\u7FA4\u804A\u3002")
11159
- ),
11160
- h2(
11161
- "span",
11162
- { className: "dwa-accessTooltipItem" },
11163
- h2("strong", null, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F"),
11164
- 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")
11165
- )
11166
- )
11167
- )
11168
- )
11169
- ),
11170
- h2(
11171
- "label",
11172
- { className: "dwa-accessField" },
11173
- h2("span", null, "\u6A21\u5F0F"),
11174
- h2(
11175
- "select",
11176
- {
11177
- value: accessMode,
11178
- disabled: busy,
11179
- "aria-label": "WhatsApp \u8BBF\u95EE\u6A21\u5F0F",
11180
- onChange: (event) => {
11181
- setAccessMode(event.target.value);
11182
- setError(null);
11183
- }
11184
- },
11185
- h2("option", { value: "self-only" }, "\u4EC5\u81EA\u5DF1\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
11186
- h2("option", { value: "private-allowlist" }, "\u6307\u5B9A\u8054\u7CFB\u4EBA\u6A21\u5F0F"),
11187
- h2("option", { value: "open" }, "\u5F00\u653E\u54CD\u5E94\u6A21\u5F0F")
11188
- )
11189
- ),
11190
- allowlistEnabled ? h2(
11191
- "label",
11192
- { className: "dwa-accessField" },
11193
- h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801"),
11194
- h2("textarea", {
11195
- value: allowedNumbers,
11196
- disabled: busy,
11197
- rows: 3,
11198
- placeholder: "\u6BCF\u884C\u4E00\u4E2A\u542B\u56FD\u5BB6\u6216\u5730\u533A\u4EE3\u7801\u7684\u53F7\u7801",
11199
- "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 WhatsApp \u7535\u8BDD\u53F7\u7801",
11200
- onChange: (event) => {
11201
- setAllowedNumbers(event.target.value);
11202
- setError(null);
11203
- }
11204
- }),
11205
- h2("small", null, "\u53EF\u4EE5\u5305\u542B\u5F00\u5934\u7684 +\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u81EA\u52A8\u79FB\u9664\u3002")
11206
- ) : null,
11207
- allowlistEnabled && allowedNumbers.trim() === "" ? h2(
11208
- "p",
11209
- { className: "dwa-accessWarning", role: "status" },
11210
- "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u5C06\u53EA\u63A5\u53D7\u81EA\u804A\u6D88\u606F\u3002"
11211
- ) : null,
11212
- error ? h2("p", { className: "dwa-accessError", role: "alert" }, error) : null,
11213
- h2(
11214
- "div",
11215
- { className: "dwa-accessActions" },
11216
- h2("button", {
11217
- type: "submit",
11218
- className: "ddt-button",
11219
- "data-kind": "secondary",
11220
- disabled: busy
11221
- }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
11222
- )
11223
- );
11224
- }
11225
- var Button14 = React20.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11226
- return h2("button", {
11227
- ...props,
11228
- ref,
11229
- type: "button",
11230
- className: `ddt-button ${className}`.trim(),
11231
- "data-kind": kind
11232
- }, children);
11233
- });
11234
- function checkedTime6(value) {
11235
- if (!value) return "\u5C1A\u672A\u68C0\u67E5";
11236
- try {
11237
- return new Intl.DateTimeFormat("zh-CN", {
11238
- hour: "2-digit",
11239
- minute: "2-digit",
11240
- second: "2-digit"
11241
- }).format(new Date(value));
11242
- } catch {
11243
- return "\u521A\u521A";
11244
- }
11245
- }
11246
- function connectionTestNotice3(value) {
11247
- if (value?.testMessage?.sent === true) {
11248
- return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230 WhatsApp \u81EA\u804A\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
11249
- }
11250
- if (value?.testMessage?.code === "test-target-unavailable") {
11251
- return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684 WhatsApp \u81EA\u804A\u76EE\u6807\u3002";
11252
- }
11253
- return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
11254
- }
11255
- function Heading6({ totals, busy, onAdd, addButtonRef }) {
11256
- return h2(
11257
- "div",
11258
- { className: "ddt-heading" },
11259
- h2(
11260
- "div",
11261
- { className: "ddt-tools" },
11101
+ { className: "ddt-tools" },
11262
11102
  h2(
11263
11103
  "div",
11264
11104
  { className: "dim-bindActions" },
@@ -11446,7 +11286,6 @@ function WhatsappAccountCard({
11446
11286
  onWorkspaceSave,
11447
11287
  onAgentPresetSave,
11448
11288
  onContextEnhancementSave,
11449
- onAccessPolicySave,
11450
11289
  onRequestRemove,
11451
11290
  onConfirmRemove,
11452
11291
  onCancelRemove
@@ -11493,7 +11332,8 @@ function WhatsappAccountCard({
11493
11332
  channel: "whatsapp",
11494
11333
  botId: account.botId,
11495
11334
  botName: account.bot.name,
11496
- connected: account.connected
11335
+ connected: account.connected,
11336
+ accessPolicy: account.accessPolicy
11497
11337
  })
11498
11338
  )
11499
11339
  ),
@@ -11512,11 +11352,6 @@ function WhatsappAccountCard({
11512
11352
  disabled: Boolean(busy),
11513
11353
  onSave: onContextEnhancementSave
11514
11354
  }),
11515
- h2(WhatsappAccessSettings, {
11516
- account,
11517
- busy: Boolean(busy),
11518
- onSave: onAccessPolicySave
11519
- }),
11520
11355
  h2(
11521
11356
  "div",
11522
11357
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -11559,23 +11394,23 @@ function WhatsappAccountCard({
11559
11394
  );
11560
11395
  }
11561
11396
  function WhatsappSettingsTab({ rpcCall }) {
11562
- const [model, setModel] = React20.useState({
11397
+ const [model, setModel] = React19.useState({
11563
11398
  phase: "loading",
11564
11399
  bots: [],
11565
11400
  totals: { configured: 0, connected: 0 },
11566
11401
  error: null,
11567
11402
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
11568
11403
  });
11569
- const [provision, setProvision] = React20.useState(null);
11570
- const [busy, setBusy] = React20.useState(false);
11571
- const [busyByBot, setBusyByBot] = React20.useState({});
11572
- const [testNoticeByBot, setTestNoticeByBot] = React20.useState({});
11573
- const [removeTarget, setRemoveTarget] = React20.useState(null);
11574
- const [now, setNow] = React20.useState(Date.now());
11575
- 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);
11576
11411
  const workspaceFence = useWorkspaceSnapshotFence();
11577
- const addButtonRef = React20.useRef(null);
11578
- React20.useEffect(() => {
11412
+ const addButtonRef = React19.useRef(null);
11413
+ React19.useEffect(() => {
11579
11414
  const disposeDingtalk = installDingtalkStyles();
11580
11415
  const disposeWhatsapp = installWhatsappStyles();
11581
11416
  mounted.current = true;
@@ -11585,11 +11420,11 @@ function WhatsappSettingsTab({ rpcCall }) {
11585
11420
  disposeDingtalk();
11586
11421
  };
11587
11422
  }, []);
11588
- const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
11423
+ const invoke = React19.useCallback(async (endpoint, payload = {}, signal) => {
11589
11424
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
11590
11425
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
11591
11426
  }, [rpcCall]);
11592
- const loadStatus = React20.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11427
+ const loadStatus = React19.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11593
11428
  const workspaceVersion = workspaceFence.beginStatus();
11594
11429
  if (workspaceVersion === null) return void 0;
11595
11430
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -11619,12 +11454,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11619
11454
  return void 0;
11620
11455
  }
11621
11456
  }, [invoke, workspaceFence]);
11622
- React20.useEffect(() => {
11457
+ React19.useEffect(() => {
11623
11458
  const controller = new AbortController();
11624
11459
  void loadStatus({ signal: controller.signal, restore: true });
11625
11460
  return () => controller.abort();
11626
11461
  }, [loadStatus]);
11627
- React20.useEffect(() => {
11462
+ React19.useEffect(() => {
11628
11463
  if (model.phase !== "ready") return void 0;
11629
11464
  const controller = new AbortController();
11630
11465
  const timer = window.setInterval(
@@ -11636,12 +11471,12 @@ function WhatsappSettingsTab({ rpcCall }) {
11636
11471
  window.clearInterval(timer);
11637
11472
  };
11638
11473
  }, [loadStatus, model.phase]);
11639
- React20.useEffect(() => {
11474
+ React19.useEffect(() => {
11640
11475
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
11641
11476
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
11642
11477
  return () => window.clearInterval(timer);
11643
11478
  }, [provision?.attemptId, provision?.status]);
11644
- const startProvisioning = React20.useCallback(async (replace = false) => {
11479
+ const startProvisioning = React19.useCallback(async (replace = false) => {
11645
11480
  setBusy(true);
11646
11481
  try {
11647
11482
  if (replace && provision?.attemptId) {
@@ -11659,7 +11494,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11659
11494
  if (mounted.current) setBusy(false);
11660
11495
  }
11661
11496
  }, [invoke, provision?.attemptId]);
11662
- const closeProvision = React20.useCallback(async () => {
11497
+ const closeProvision = React19.useCallback(async () => {
11663
11498
  setBusy(true);
11664
11499
  try {
11665
11500
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -11670,7 +11505,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11670
11505
  if (mounted.current) setBusy(false);
11671
11506
  }
11672
11507
  }, [invoke, provision?.attemptId, provision?.status]);
11673
- React20.useEffect(() => {
11508
+ React19.useEffect(() => {
11674
11509
  const attemptId = provision?.attemptId;
11675
11510
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
11676
11511
  const controller = new AbortController();
@@ -11711,7 +11546,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11711
11546
  if (timer) window.clearTimeout(timer);
11712
11547
  };
11713
11548
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
11714
- const botAction = React20.useCallback(async (account, operation, endpoint, payload) => {
11549
+ const botAction = React19.useCallback(async (account, operation, endpoint, payload) => {
11715
11550
  const snapshotVersion = workspaceFence.beginMutation();
11716
11551
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
11717
11552
  if (operation === "reconnect") {
@@ -11794,12 +11629,6 @@ function WhatsappSettingsTab({ rpcCall }) {
11794
11629
  WHATSAPP_ENDPOINTS.setContextEnhancement,
11795
11630
  { botId: account.botId, config }
11796
11631
  ),
11797
- onAccessPolicySave: (accessPolicy) => botAction(
11798
- account,
11799
- "access",
11800
- WHATSAPP_ENDPOINTS.setAccessPolicy,
11801
- { botId: account.botId, ...accessPolicy }
11802
- ),
11803
11632
  onRequestRemove: () => setRemoveTarget(account.botId),
11804
11633
  onCancelRemove: () => setRemoveTarget(null),
11805
11634
  onConfirmRemove: async () => {
@@ -11836,7 +11665,7 @@ function WhatsappSettingsTab({ rpcCall }) {
11836
11665
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
11837
11666
  )
11838
11667
  ) : h2(
11839
- React20.Fragment,
11668
+ React19.Fragment,
11840
11669
  null,
11841
11670
  provision?.status === "pending" ? h2(QrPanel5, {
11842
11671
  provision,
@@ -11857,6 +11686,404 @@ function WhatsappSettingsTab({ rpcCall }) {
11857
11686
 
11858
11687
  // plugin-src/client/delivery-settings.js
11859
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
11860
12087
  var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
11861
12088
  var DELIVERY_DOCS_URL = Object.freeze({
11862
12089
  zh: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.md",
@@ -11870,6 +12097,10 @@ var DELIVERY_ENDPOINTS = Object.freeze({
11870
12097
  delete: "target.delete",
11871
12098
  test: "target.test"
11872
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
+ ]);
11873
12104
  var CHANNEL_DEFINITIONS = Object.freeze({
11874
12105
  weixin: {
11875
12106
  label: "\u5FAE\u4FE1",
@@ -11947,7 +12178,7 @@ var CHANNEL_DEFINITIONS = Object.freeze({
11947
12178
  function presentError10(error, fallback) {
11948
12179
  return error?.message || fallback;
11949
12180
  }
11950
- function unwrapRpcResult10(result) {
12181
+ function unwrapRpcResult11(result) {
11951
12182
  if (result?.ok === true) return result.value;
11952
12183
  if (result?.ok === false) {
11953
12184
  const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
@@ -12405,8 +12636,15 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
12405
12636
  ) : null
12406
12637
  );
12407
12638
  }
12408
- function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBack }) {
12639
+ function DeliveryTargetSettingsPage({
12640
+ channel: channel4,
12641
+ account,
12642
+ rpcCall,
12643
+ accessRpcCall,
12644
+ onBack
12645
+ }) {
12409
12646
  const definition = CHANNEL_DEFINITIONS[channel4];
12647
+ const [activeTabId, setActiveTabId] = React21.useState(BOT_SETTINGS_TABS[0].id);
12410
12648
  const [phase, setPhase] = React21.useState("loading");
12411
12649
  const [targets, setTargets] = React21.useState([]);
12412
12650
  const [suggestionPhase, setSuggestionPhase] = React21.useState("idle");
@@ -12416,10 +12654,14 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12416
12654
  const [editor, setEditor] = React21.useState(null);
12417
12655
  const [saving, setSaving] = React21.useState(false);
12418
12656
  const [botCopyState, setBotCopyState] = React21.useState(null);
12657
+ const [accessPolicy, setAccessPolicy] = React21.useState(account.accessPolicy);
12419
12658
  const mounted = React21.useRef(true);
12659
+ React21.useEffect(() => {
12660
+ setAccessPolicy(account.accessPolicy);
12661
+ }, [account.botId, account.accessPolicy]);
12420
12662
  const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
12421
12663
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12422
- return unwrapRpcResult10(await rpcCall(endpoint, payload, signal));
12664
+ return unwrapRpcResult11(await rpcCall(endpoint, payload, signal));
12423
12665
  }, [rpcCall]);
12424
12666
  const loadTargets = React21.useCallback(async ({ signal, silent = false } = {}) => {
12425
12667
  if (!silent) setPhase("loading");
@@ -12520,6 +12762,9 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12520
12762
  setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002"));
12521
12763
  }
12522
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`;
12523
12768
  return h2(
12524
12769
  "section",
12525
12770
  {
@@ -12529,114 +12774,147 @@ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBac
12529
12774
  h2(
12530
12775
  "header",
12531
12776
  { className: "dim-deliveryHeader" },
12532
- h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868"),
12533
- h2(
12534
- "div",
12535
- { className: "dim-deliveryHeaderMeta" },
12536
- h2(
12537
- "a",
12538
- {
12539
- className: "dim-deliveryDocsLink",
12540
- href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh,
12541
- target: "_blank",
12542
- rel: "noopener noreferrer",
12543
- "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863"
12544
- },
12545
- h2("span", null, "\u4F7F\u7528\u6587\u6863"),
12546
- h2("span", { "aria-hidden": "true" }, "\u2197")
12547
- )
12548
- )
12777
+ h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868")
12549
12778
  ),
12550
12779
  h2(
12551
- "section",
12552
- { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" },
12553
- h2(
12554
- "div",
12555
- null,
12556
- h2(
12557
- "h2",
12558
- { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" },
12559
- account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E"
12560
- )
12561
- ),
12562
- h2(
12563
- "div",
12564
- { className: "dim-deliveryBotId" },
12565
- h2("span", null, "Bot ID"),
12566
- h2("code", { title: account.botId }, account.botId),
12567
- h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236")
12568
- ),
12569
- 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)))
12570
12797
  ),
12571
12798
  h2(
12572
- "section",
12573
- { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" },
12574
- h2(
12575
- "div",
12576
- { 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,
12577
12814
  h2(
12578
- "div",
12579
- null,
12580
- h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"),
12581
- 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
12582
12846
  ),
12583
- h2(DeliveryButton, {
12584
- kind: "primary",
12585
- onClick: openSuggestionPicker,
12586
- disabled: Boolean(editor) || phase !== "ready",
12587
- title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6"
12588
- }, "\u65B0\u5EFA\u76EE\u6807")
12589
- ),
12590
- editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, {
12591
- definition,
12592
- phase: suggestionPhase,
12593
- suggestions,
12594
- error: suggestionError,
12595
- targets,
12596
- onRefresh: loadSuggestions,
12597
- onSelect: selectSuggestion,
12598
- onManual: () => setEditor({
12599
- mode: "create",
12600
- source: "manual",
12601
- draftKey: "manual",
12602
- initialValue: { targetId: randomTargetId(targets) }
12603
- }),
12604
- onCancel: () => setEditor(null)
12605
- }) : editor ? h2(TargetForm, {
12606
- key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey,
12607
- definition,
12608
- mode: editor.mode,
12609
- initialValue: editor.mode === "edit" ? editor.target : editor.initialValue,
12610
- source: editor.source,
12611
- busy: saving,
12612
- connected: account.connected,
12613
- onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null),
12614
- onSave: saveTarget,
12615
- onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, {
12616
- botId: account.botId,
12617
- target
12618
- })
12619
- }) : null,
12620
- phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2(
12621
- "div",
12622
- { className: "dim-deliveryState", role: "alert" },
12623
- h2("p", null, error),
12624
- h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6")
12625
- ) : targets.length === 0 ? h2(
12626
- "div",
12627
- { className: "dim-deliveryState dim-deliveryEmpty" },
12628
- h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"),
12629
- 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")
12630
- ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, {
12631
- key: target.targetId,
12632
- definition,
12633
- target,
12634
- botId: account.botId,
12635
- connected: account.connected,
12636
- rpcCall: invoke,
12637
- onChanged: () => loadTargets({ silent: true }),
12638
- onEdit: () => setEditor({ mode: "edit", target, source: "edit" })
12639
- })))
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
+ )
12640
12918
  )
12641
12919
  );
12642
12920
  }
@@ -13067,9 +13345,18 @@ var CSS11 = String.raw`
13067
13345
  .dim-botSettingsButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
13068
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; }
13069
13347
  .dim-botSettingsAction:hover .dim-botSettingsTooltip, .dim-botSettingsAction:focus-within .dim-botSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
13070
- .dim-deliveryPage { min-width: 0; display: grid; gap: 14px; }
13348
+ .dim-deliveryPage { min-width: 0; display: grid; }
13071
13349
  .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
13072
- .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; }
13073
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; }
13074
13361
  .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
13075
13362
  .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
@@ -13081,6 +13368,7 @@ var CSS11 = String.raw`
13081
13368
  .dim-deliveryButton[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
13082
13369
  .dim-deliveryBack { flex: none; border-color: transparent; background: transparent; }
13083
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; }
13084
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; }
13085
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; }
13086
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); }
@@ -13123,6 +13411,38 @@ var CSS11 = String.raw`
13123
13411
  .dim-targetField input[readonly] { color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
13124
13412
  .dim-targetFormError { margin: 10px 0 0; font-size: 12px; line-height: 18px; }
13125
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; }
13126
13446
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
13127
13447
  .dim-panel .ddt-headingCopy { display: none; }
13128
13448
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -13146,6 +13466,9 @@ var CSS11 = String.raw`
13146
13466
  .dim-targetActions { justify-content: flex-start; }
13147
13467
  .dim-targetFormGrid { grid-template-columns: minmax(0, 1fr); }
13148
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; }
13149
13472
  }
13150
13473
  @media (max-width: 840px) {
13151
13474
  .dim-title { align-items: flex-start; }
@@ -13169,11 +13492,12 @@ var CSS11 = String.raw`
13169
13492
  .dim-rail { grid-template-columns: minmax(0, 1fr); }
13170
13493
  .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; }
13171
13494
  .dim-loopbackRecoveryAction { width: 100%; }
13172
- .dim-deliveryHeader, .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
13173
- .dim-deliveryHeaderMeta { justify-content: space-between; text-align: left; }
13495
+ .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
13496
+ .dim-botSettingsTabs { gap: 18px; }
13174
13497
  .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; }
13175
13498
  .dim-deliveryBotId > span { grid-column: 1 / -1; }
13176
13499
  .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
13500
+ .dim-accessActions .dim-deliveryButton { width: 100%; }
13177
13501
  .dim-directoryPickerBackdrop { padding: 10px; }
13178
13502
  .dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
13179
13503
  .dim-directoryPickerHeader { padding: 18px 17px 14px; }
@@ -13985,6 +14309,7 @@ function IMSettingsTab({
13985
14309
  channel: active.id,
13986
14310
  account: deliverySettings,
13987
14311
  rpcCall: rpcCalls.deliveryRpcCall,
14312
+ accessRpcCall: rpcCalls[`${active.id}RpcCall`],
13988
14313
  onBack: () => setDeliverySettings(null)
13989
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 })
13990
14315
  )