@xmanrui/dsh-im 4.14.0 → 4.16.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 (69) hide show
  1. package/README.en.md +10 -2
  2. package/README.md +17 -6
  3. package/lib/client.js +1980 -616
  4. package/lib/index.js +303 -293
  5. package/package.json +6 -2
  6. package/plugin-src/client/access-policy-settings.js +6 -0
  7. package/plugin-src/client/channels/feishu/index.js +16 -4
  8. package/plugin-src/client/channels/wecom-app/api.js +124 -0
  9. package/plugin-src/client/channels/wecom-app/index.js +612 -0
  10. package/plugin-src/client/channels/wecom-app/styles.js +25 -0
  11. package/plugin-src/client/delivery-settings.js +7 -0
  12. package/plugin-src/client/i18n.js +64 -1
  13. package/plugin-src/client/index.js +21 -0
  14. package/plugin-src/client/model-setting.js +135 -69
  15. package/plugin-src/client/session-channel-logos.js +241 -0
  16. package/plugin-src/client/styles.js +22 -7
  17. package/plugin-src/host/channels/dingtalk/index.mjs +8 -15
  18. package/plugin-src/host/channels/discord/index.mjs +8 -10
  19. package/plugin-src/host/channels/feishu/index.mjs +8 -15
  20. package/plugin-src/host/channels/office/index.mjs +9 -5
  21. package/plugin-src/host/channels/qq/index.mjs +8 -15
  22. package/plugin-src/host/channels/shared/access-policy-production.mjs +1 -1
  23. package/plugin-src/host/channels/shared/model-setting-rpc.mjs +1 -2
  24. package/plugin-src/host/channels/shared/startup-error.mjs +53 -0
  25. package/plugin-src/host/channels/shared/startup.mjs +47 -0
  26. package/plugin-src/host/channels/shared/workspace-rpc.mjs +1 -0
  27. package/plugin-src/host/channels/slack/index.mjs +8 -10
  28. package/plugin-src/host/channels/telegram/index.mjs +8 -10
  29. package/plugin-src/host/channels/wecom/index.mjs +8 -15
  30. package/plugin-src/host/channels/wecom/rpc.mjs +6 -1
  31. package/plugin-src/host/channels/wecom-app/index.mjs +33 -0
  32. package/plugin-src/host/channels/wecom-app/production.mjs +217 -0
  33. package/plugin-src/host/channels/wecom-app/rpc.mjs +225 -0
  34. package/plugin-src/host/channels/weixin/index.mjs +8 -15
  35. package/plugin-src/host/channels/whatsapp/index.mjs +8 -15
  36. package/plugin-src/host/delivery-adapter.mjs +4 -0
  37. package/plugin-src/host/delivery-suggestions.mjs +4 -0
  38. package/plugin-src/host/index.mjs +21 -2
  39. package/plugin-src/host/session-title-prefix.mjs +122 -0
  40. package/scripts/verify-model-setting.mjs +54 -0
  41. package/scripts/verify-session-channel-logos.mjs +59 -0
  42. package/scripts/verify-session-title-prefix.mjs +149 -0
  43. package/src/channels/feishu/bridge.mjs +305 -12
  44. package/src/channels/feishu/feishu-cards.mjs +38 -0
  45. package/src/channels/feishu/feishu-channel.mjs +88 -20
  46. package/src/channels/feishu/plugin-controller.mjs +1 -0
  47. package/src/channels/feishu/repair-manager.mjs +3 -2
  48. package/src/channels/shared/bot-workspace-store.mjs +11 -3
  49. package/src/channels/shared/command-catalog.mjs +102 -0
  50. package/src/channels/shared/i18n-en/feishu.mjs +14 -2
  51. package/src/channels/shared/i18n-en/shared-a.mjs +3 -0
  52. package/src/channels/shared/i18n-en/shared-c.mjs +9 -0
  53. package/src/channels/shared/i18n-en/wecom-app.mjs +33 -0
  54. package/src/channels/shared/i18n-en.mjs +2 -0
  55. package/src/channels/shared/model-setting.mjs +43 -8
  56. package/src/channels/shared/session-channel-labels.mjs +26 -0
  57. package/src/channels/shared/text-harness-bridge.mjs +2 -26
  58. package/src/channels/telegram/telegram-api.mjs +18 -6
  59. package/src/channels/telegram/telegram-runtime.mjs +34 -32
  60. package/src/channels/wecom/send-error.mjs +31 -0
  61. package/src/channels/wecom/wecom-bridge.mjs +59 -17
  62. package/src/channels/wecom-app/callback-server.mjs +471 -0
  63. package/src/channels/wecom-app/config-store.mjs +240 -0
  64. package/src/channels/wecom-app/harness-client.mjs +11 -0
  65. package/src/channels/wecom-app/state-store.mjs +107 -0
  66. package/src/channels/wecom-app/wecom-app-api.mjs +432 -0
  67. package/src/channels/wecom-app/wecom-app-bridge.mjs +1059 -0
  68. package/src/channels/wecom-app/wecom-app-controller.mjs +440 -0
  69. package/src/channels/wecom-app/wecom-app-runtime.mjs +221 -0
package/lib/client.js CHANGED
@@ -270,14 +270,14 @@ var require_semver = __commonJS({
270
270
  if (!m[4]) {
271
271
  this.prerelease = [];
272
272
  } else {
273
- this.prerelease = m[4].split(".").map((id5) => {
274
- if (/^[0-9]+$/.test(id5)) {
275
- const num = +id5;
273
+ this.prerelease = m[4].split(".").map((id6) => {
274
+ if (/^[0-9]+$/.test(id6)) {
275
+ const num = +id6;
276
276
  if (num >= 0 && num < MAX_SAFE_INTEGER) {
277
277
  return num;
278
278
  }
279
279
  }
280
- return id5;
280
+ return id6;
281
281
  });
282
282
  }
283
283
  this.build = m[5] ? m[5].split(".") : [];
@@ -566,13 +566,13 @@ __export(index_exports, {
566
566
  name: () => name
567
567
  });
568
568
  module.exports = __toCommonJS(index_exports);
569
- var React27 = __toESM(require("react"), 1);
569
+ var React28 = __toESM(require("react"), 1);
570
570
 
571
571
  // package.json
572
572
  var package_default = {
573
573
  name: "@xmanrui/dsh-im",
574
- version: "4.14.0",
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.",
574
+ version: "4.16.0",
575
+ description: "\u628A\u5341\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect ten IM channels and a public AI Office to a local DeepSeek Harness.",
576
576
  keywords: [
577
577
  "deepseek-harness",
578
578
  "dsh",
@@ -606,6 +606,10 @@ var package_default = {
606
606
  {
607
607
  name: "Chan-0312",
608
608
  url: "https://github.com/Chan-0312"
609
+ },
610
+ {
611
+ name: "BuvkB",
612
+ url: "https://github.com/BuvkB"
609
613
  }
610
614
  ],
611
615
  license: "MIT",
@@ -1075,6 +1079,7 @@ var EN = Object.freeze({
1075
1079
  "\u5F00\u542F\uFF08\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF09": "On (auto-open a dedicated Feishu topic)",
1076
1080
  "\u5F00\u542F\u540E\uFF0C\u7FA4\u804A\u4E2D\u5411\u673A\u5668\u4EBA\u63D0\u95EE\u4F1A\u81EA\u52A8\u5F00\u542F\u72EC\u7ACB\u98DE\u4E66\u8BDD\u9898\uFF0C\u56DE\u590D\u843D\u5728\u8BDD\u9898\u5185\uFF1B\u6BCF\u4E2A\u8BDD\u9898\u662F dsh \u4F1A\u8BDD\u5217\u8868\u91CC\u4E00\u6761\u72EC\u7ACB\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u4E92\u4E0D\u4E32\u3002\u79C1\u804A\u4E0D\u53D7\u5F71\u54CD\u3002": "When enabled, questions to the bot in a group auto-open a dedicated Feishu topic and replies stay inside it. Each topic is an independent conversation in dsh with its own context. Private chats are unaffected.",
1077
1081
  "\u5206\u6B65\u76F4\u63A8": "Step push",
1082
+ "\u67E5\u770B\u5206\u6B65\u76F4\u63A8\u8BF4\u660E": "View step push help",
1078
1083
  "\u5F00\u542F\u540E\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E": "Push tool calls and process notes step by step",
1079
1084
  "\u5173\u95ED\uFF08\u4FDD\u6301\u6D41\u5F0F\u5361\u6A21\u5F0F\uFF09": "Off (keep the streaming card)",
1080
1085
  "\u5F00\u542F\uFF08\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E\uFF09": "On (push tool calls and process notes step by step)",
@@ -1213,6 +1218,14 @@ var EN = Object.freeze({
1213
1218
  "Agent Preset \u65E0\u6548\u3002": "The Agent Preset is invalid.",
1214
1219
  "Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002": "The Agent Preset does not exist or is unavailable.",
1215
1220
  "\u6A21\u578B": "Model",
1221
+ "\u6A21\u578B\u4E0E\u601D\u8003\u5F3A\u5EA6": "Model and reasoning effort",
1222
+ "\u601D\u8003\u5F3A\u5EA6": "Reasoning effort",
1223
+ "\u8DDF\u968F\u6A21\u578B\u9ED8\u8BA4": "Model default",
1224
+ "\u5148\u9009\u62E9\u6A21\u578B\uFF0C\u518D\u8BBE\u7F6E\u601D\u8003\u5F3A\u5EA6\u3002": "Select a model to configure its reasoning effort.",
1225
+ "\u8BE5\u6A21\u578B\u672A\u63D0\u4F9B\u53EF\u8C03\u8282\u7684\u601D\u8003\u5F3A\u5EA6\u3002": "This model does not provide adjustable reasoning effort.",
1226
+ "\u5DF2\u4FDD\u5B58\u7684\u601D\u8003\u5F3A\u5EA6\u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u5F3A\u5EA6\u6216\u6062\u590D\u9ED8\u8BA4\u3002": "The saved reasoning effort is unavailable. Choose another effort or restore the default.",
1227
+ "\u4F7F\u7528\u6A21\u578B\u6216\u670D\u52A1\u63D0\u4F9B\u65B9\u7684\u9ED8\u8BA4\u601D\u8003\u5F3A\u5EA6\u3002": "Use the default reasoning effort from the model or provider.",
1228
+ "\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u5F3A\u5EA6\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002": "This model does not support the selected reasoning effort. Choose another effort.",
1216
1229
  "\u67E5\u770B\u6A21\u578B\u8BBE\u7F6E\u8BF4\u660E": "View model setting help",
1217
1230
  "\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B": "Follow the default model",
1218
1231
  "\u6A21\u578B\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the model. Try again.",
@@ -1313,7 +1326,7 @@ var EN = Object.freeze({
1313
1326
  "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "The permission update was submitted. Enabling all-message mode and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.",
1314
1327
  "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002": "Requesting a group-message permission QR code for the existing Feishu app\u2026",
1315
1328
  "\u7FA4\u6D88\u606F\u6743\u9650\u6CA1\u6709\u5F00\u901A\u5B8C\u6210": "Group-message permission was not granted",
1316
- "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with the missing card-button callback, im:message:readonly for reading images or files in user messages (shown by Feishu as \u201CRead direct and group messages\u201D), im:resource for uploading images or files sent by the bot, and application:app_slash_command:read / write for the native command panel. It does not create a new app. The confirmation page shows only missing items; this bot reconnects briefly afterward, while other bots are unaffected.",
1329
+ "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\u3001\u63A5\u6536\u7FA4\u5185\u5176\u4ED6\u673A\u5668\u4EBA @ \u5F53\u524D\u673A\u5668\u4EBA\u6240\u9700\u7684 im:message.group_at_msg.include_bot:readonly\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with the missing card-button callback, im:message:readonly for reading images or files in user messages (shown by Feishu as \u201CRead direct and group messages\u201D), im:resource for uploading images or files sent by the bot, im:message.group_at_msg.include_bot:readonly for receiving group messages from other bots that mention this bot, and application:app_slash_command:read / write for the native command panel. It does not create a new app. The confirmation page shows only missing items; this bot reconnects briefly afterward, while other bots are unaffected.",
1317
1330
  "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\u540D\u79F0\uFF0C\u5E76\u786E\u8BA4\u53EA\u65B0\u589E\u5F53\u524D\u7F3A\u5C11\u7684\u4E0A\u8FF0\u914D\u7F6E": "Review the existing app name and confirm that only the missing items described above are added",
1318
1331
  "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6743\u9650\u4E0E\u56DE\u8C03\u8865\u5168\u5B8C\u6210": "Keep this page open until permissions and the callback are complete",
1319
1332
  "\u53D6\u6D88\u8865\u5168": "Cancel setup",
@@ -1744,7 +1757,61 @@ var EN = Object.freeze({
1744
1757
  "\u786E\u8BA4\u6E05\u7406\u8FC7\u671F\u9644\u4EF6": "Confirm expired attachment cleanup",
1745
1758
  "\u786E\u8BA4\u6E05\u7406\u5F53\u524D\u5DF2\u8FC7\u671F\u7684\u9644\u4EF6\uFF1F": "Clean up the attachments that are currently expired?",
1746
1759
  "\u786E\u8BA4\u6E05\u7406": "Confirm cleanup",
1747
- "\u6B63\u5728\u6E05\u7406\u2026": "Cleaning up\u2026"
1760
+ "\u6B63\u5728\u6E05\u7406\u2026": "Cleaning up\u2026",
1761
+ "\u6B63\u5728\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u72B6\u6001\u2026": "Loading WeCom app status\u2026",
1762
+ "\u6DFB\u52A0\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528": "Add WeCom app",
1763
+ "\u6536\u8D77\u8868\u5355": "Collapse form",
1764
+ "\u6DFB\u52A0\u4F01\u4E1A\u5FAE\u4FE1\u81EA\u5EFA\u5E94\u7528": "Add a WeCom self-built app",
1765
+ "\u6B63\u5728\u586B\u5199": "Filling in details",
1766
+ "\u5C1A\u672A\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528": "No WeCom app connected yet",
1767
+ "\u5728\u4F01\u4E1A\u5FAE\u4FE1\u4E2D\u521B\u5EFA\u81EA\u5EFA\u5E94\u7528\uFF0C\u5373\u53EF\u5728\u5FAE\u4FE1\u91CC\u4F7F\u7528": "Create a self-built app in WeCom to use it inside WeChat",
1768
+ "\u5728\u5E94\u7528\u7BA1\u7406\u540E\u53F0\u521B\u5EFA\u81EA\u5EFA\u5E94\u7528\uFF0C\u5F00\u542F\u300C\u63A5\u6536\u6D88\u606F\u300D\u5E76\u586B\u5165\u4E0B\u9762\u7684\u53C2\u6570\u3002\u6210\u5458\u7684\u5FAE\u4FE1\u5173\u6CE8\u8BE5\u4F01\u4E1A\u7684\u5FAE\u4FE1\u63D2\u4EF6\u540E\uFF0C\u5C31\u80FD\u5728\u5FAE\u4FE1\u4E2D\u4E0E\u672C\u5E94\u7528\u53CC\u5411\u5BF9\u8BDD\u3002": "Create the self-built app in the WeCom admin console, enable receive-messages, and fill in the parameters below. Once members follow the enterprise WeChat plugin, they can chat with this app directly inside WeChat.",
1769
+ "\u586B\u5199\u5E94\u7528\u53C2\u6570": "Fill in app parameters",
1770
+ "\u4F01\u4E1A ID\uFF08CorpID\uFF09": "Corp ID",
1771
+ "\u4F8B\u5982 ww1234567890abcdef": "for example ww1234567890abcdef",
1772
+ "\u5E94\u7528 AgentId": "AgentId",
1773
+ "\u4F8B\u5982 1000002": "for example 1000002",
1774
+ "\u5E94\u7528 Secret": "App Secret",
1775
+ "\u5E94\u7528\u8BE6\u60C5\u9875\u7684 Secret": "The Secret from the app details page",
1776
+ "\u56DE\u8C03 Token": "Callback Token",
1777
+ "\u63A5\u6536\u6D88\u606F \u2192 API \u63A5\u6536\u4E2D\u7684 Token": "The Token from Receive messages \u2192 API receive",
1778
+ "43 \u4F4D\u5B57\u6BCD\u6216\u6570\u5B57": "43 alphanumeric characters",
1779
+ "\u4EE3\u7406\u5730\u5740\uFF08\u53EF\u9009\uFF09": "API proxy (optional)",
1780
+ "\u7559\u7A7A\u76F4\u8FDE qyapi.weixin.qq.com": "leave empty to reach qyapi.weixin.qq.com directly",
1781
+ "\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF08\u53EF\u9009\uFF09": "Public callback base URL (optional)",
1782
+ "\u4F8B\u5982 https://im.example.com": "for example https://im.example.com",
1783
+ "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5F00": "Streaming replies: on",
1784
+ "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5173": "Streaming replies: off",
1785
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5BA2\u6237\u7AEF\u5B9E\u65F6\u51FA\u5B57\uFF1B\u5FAE\u4FE1\u7AEF\u4E0D\u652F\u6301\u65F6\u81EA\u52A8\u6539\u4E3A\u6574\u6BB5\u53D1\u9001": "Streams replies in the WeCom client; falls back to segmented text messages when the WeChat side does not support it",
1786
+ "\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u81EA\u5EFA\u5E94\u7528": "Bind a WeCom self-built app",
1787
+ "\u56DE\u8C03 URL\uFF08\u586B\u5165\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u300C\u63A5\u6536\u6D88\u606F \u2192 \u8BBE\u7F6EAPI\u63A5\u6536\u300D\uFF09": "Callback URL (paste into the WeCom console under Receive messages \u2192 API receive)",
1788
+ "\u672A\u914D\u7F6E\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF1A\u8BF7\u5728\u672C\u9875\u300C\u8BBE\u7F6E\u300D\u4E2D\u586B\u5199\u56DE\u8C03\u57FA\u5740\uFF0C\u6216\u624B\u52A8\u62FC\u63A5 \u56DE\u8C03\u57FA\u5740 + \u8DEF\u5F84": "No public callback base URL configured: fill it in under Settings on this page, or manually combine the base URL and the path",
1789
+ "\u91CD\u7F6E\u5BC6\u94A5": "Reset secret",
1790
+ "\u91CD\u7F6E\u4E2D\u2026": "Resetting\u2026",
1791
+ "\u91CD\u7F6E\u5BC6\u94A5\u540E\u56DE\u8C03 URL \u4F1A\u53D8\u5316\uFF0C\u9700\u8981\u540C\u6B65\u66F4\u65B0\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u3002\u82E5\u516C\u7F51\u53CD\u4EE3\u672A\u5C31\u7EEA\uFF0C\u53EF\u4E34\u65F6\u4F7F\u7528 http://\u670D\u52A1\u5668IP:\u7AEF\u53E3 \u5F62\u5F0F\u7684\u56DE\u8C03\u5730\u5740\u3002": "Resetting the secret changes the callback URL; update the WeCom console accordingly. If the public reverse proxy is not ready yet, a http://server-ip:port callback address works temporarily.",
1792
+ "\u4FDD\u5B58\u8BBE\u7F6E": "Save settings",
1793
+ "\u8FD9\u4F1A\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u56DE\u8C03\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u7684\u5E94\u7528\u4E0D\u4F1A\u88AB\u5220\u9664\u3002": "This removes the locally stored app credentials, callback configuration, and session mappings. The app in the WeCom console is not deleted.",
1794
+ "\u4FDD\u7559\u5E94\u7528": "Keep app",
1795
+ "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528": "Connected WeCom apps",
1796
+ "HTTP \u56DE\u8C03\u901A\u9053": "HTTP callback channel",
1797
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528": "WeCom app",
1798
+ "\u4F01\u4E1A ID \u5DF2\u4FDD\u5B58": "Corp ID saved securely",
1799
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u56DE\u8C03\u901A\u9053\u5C31\u7EEA": "WeCom app callback channel is ready",
1800
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA": "WeCom app connection is not ready",
1801
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u4ECD\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002": "The WeCom app is still not ready; the plugin keeps retrying automatically.",
1802
+ "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002": "Connection check finished.",
1803
+ "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002": "Connection check finished; the test message was sent.",
1804
+ "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u5E94\u7528\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002": "Connection check finished. The app has not received a private chat usable for testing yet.",
1805
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u5DF2\u7ED1\u5B9A\u3002\u8BF7\u628A\u56DE\u8C03 URL \u586B\u5165\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u5E76\u4FDD\u5B58\u3002": "The WeCom app is bound. Paste the callback URL into the WeCom console and save.",
1806
+ "\u56DE\u8C03\u5BC6\u94A5\u5DF2\u91CD\u7F6E\uFF0C\u8BF7\u628A\u65B0\u7684\u56DE\u8C03 URL \u66F4\u65B0\u5230\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u3002": "The callback secret was reset; update the new callback URL in the WeCom console.",
1807
+ "\u56DE\u8C03\u5BC6\u94A5\u91CD\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "The callback secret reset failed; try again later.",
1808
+ "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u72B6\u6001": "Could not load WeCom app status",
1809
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8BBE\u7F6E": "WeCom app settings",
1810
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5": "The WeCom app settings page is missing the RPC connection",
1811
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u64CD\u4F5C\u5931\u8D25": "The WeCom app operation failed",
1812
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5": "The WeCom app operation failed; try again later",
1813
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94": "The WeCom app service returned an unrecognized response",
1814
+ "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "The WeCom app service did not return a valid bot list"
1748
1815
  });
1749
1816
  var en = EN;
1750
1817
  var zh = Object.freeze(Object.fromEntries(
@@ -1760,78 +1827,78 @@ function isEnglish() {
1760
1827
  function channelName(value) {
1761
1828
  return localizeText(value);
1762
1829
  }
1763
- function translateDynamic(text6) {
1764
- const guidanceLimit = /^增强提示词不得超过 (\d+) 个字符。$/.exec(text6);
1830
+ function translateDynamic(text7) {
1831
+ const guidanceLimit = /^增强提示词不得超过 (\d+) 个字符。$/.exec(text7);
1765
1832
  if (guidanceLimit) return `Guidance must not exceed ${guidanceLimit[1]} characters.`;
1766
- let match = /^(\d+) \/ (\d+) 在线$/.exec(text6);
1833
+ let match = /^(\d+) \/ (\d+) 在线$/.exec(text7);
1767
1834
  if (match) return `${match[1]}/${match[2]} online`;
1768
- match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text6);
1835
+ match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text7);
1769
1836
  if (match) return `${match[2]} of ${match[1]} bots online`;
1770
- match = /^请输入 -1、0 或 1~(\d+) 之间的整数。$/.exec(text6);
1837
+ match = /^请输入 -1、0 或 1~(\d+) 之间的整数。$/.exec(text7);
1771
1838
  if (match) return `Enter -1, 0, or an integer from 1 to ${match[1]}.`;
1772
- match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text6);
1839
+ match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text7);
1773
1840
  if (match) return `Loading ${channelName(match[1])} bot status\u2026`;
1774
- match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text6);
1841
+ match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text7);
1775
1842
  if (match) return `Could not load ${channelName(match[1])} bot status`;
1776
- match = /^尚未接入\s*(.+?)\s*机器人$/.exec(text6);
1843
+ match = /^尚未接入\s*(.+?)\s*机器人$/.exec(text7);
1777
1844
  if (match) return `No ${channelName(match[1])} bot connected yet`;
1778
- match = /^已接入的\s*(.+?)\s*机器人$/.exec(text6);
1845
+ match = /^已接入的\s*(.+?)\s*机器人$/.exec(text7);
1779
1846
  if (match) return `Connected ${channelName(match[1])} bots`;
1780
- match = /^手动接入(.+)机器人$/.exec(text6);
1847
+ match = /^手动接入(.+)机器人$/.exec(text7);
1781
1848
  if (match) return `Connect ${channelName(match[1])} bot manually`;
1782
- match = /^(.+) 设置$/.exec(text6);
1849
+ match = /^(.+) 设置$/.exec(text7);
1783
1850
  if (match) return `${channelName(match[1])} settings`;
1784
- match = /^从 DeepSeek Harness 移除“(.+)”?$/.exec(text6);
1851
+ match = /^从 DeepSeek Harness 移除“(.+)”?$/.exec(text7);
1785
1852
  if (match) return `Remove \u201C${match[1]}\u201D from DeepSeek Harness?`;
1786
- match = /^从 DeepSeek Harness 移除(.+)$/.exec(text6);
1853
+ match = /^从 DeepSeek Harness 移除(.+)$/.exec(text7);
1787
1854
  if (match) return `Remove ${match[1]} from DeepSeek Harness`;
1788
- match = /^(.+)的飞书授权流程$/.exec(text6);
1855
+ match = /^(.+)的飞书授权流程$/.exec(text7);
1789
1856
  if (match) return `Feishu authorization flow for ${match[1]}`;
1790
- match = /^用于为(.+)补全权限与回调的一次性授权二维码$/.exec(text6);
1857
+ match = /^用于为(.+)补全权限与回调的一次性授权二维码$/.exec(text7);
1791
1858
  if (match) return `One-time QR code for completing permissions and the callback for ${match[1]}`;
1792
- match = /^用于为(.+)开通群消息权限的一次性授权二维码$/.exec(text6);
1859
+ match = /^用于为(.+)开通群消息权限的一次性授权二维码$/.exec(text7);
1793
1860
  if (match) return `One-time QR code for granting group-message permission to ${match[1]}`;
1794
- match = /^正在为「(.+)」补全权限与回调$/.exec(text6);
1861
+ match = /^正在为「(.+)」补全权限与回调$/.exec(text7);
1795
1862
  if (match) return `Completing permissions and the callback for \u201C${match[1]}\u201D`;
1796
- match = /^正在为「(.+)」开通群消息权限$/.exec(text6);
1863
+ match = /^正在为「(.+)」开通群消息权限$/.exec(text7);
1797
1864
  if (match) return `Granting group-message permission to \u201C${match[1]}\u201D`;
1798
- match = /^为(.+)补全权限与回调$/.exec(text6);
1865
+ match = /^为(.+)补全权限与回调$/.exec(text7);
1799
1866
  if (match) return `Complete permissions and the callback for ${match[1]}`;
1800
- match = /^(.+)的权限补全二维码已生成,请使用飞书扫码。$/.exec(text6);
1867
+ match = /^(.+)的权限补全二维码已生成,请使用飞书扫码。$/.exec(text7);
1801
1868
  if (match) return `Permission-completion QR code generated for ${match[1]}. Scan it with Feishu.`;
1802
- match = /^(.+)的群消息权限二维码已生成,请使用飞书确认。$/.exec(text6);
1869
+ match = /^(.+)的群消息权限二维码已生成,请使用飞书确认。$/.exec(text7);
1803
1870
  if (match) return `Group-message permission QR code generated for ${match[1]}. Confirm it with Feishu.`;
1804
- match = /^(.+)的权限与回调已补全。$/.exec(text6);
1871
+ match = /^(.+)的权限与回调已补全。$/.exec(text7);
1805
1872
  if (match) return `Permissions and the callback completed for ${match[1]}.`;
1806
- match = /^(.+)已开通群消息权限,并启用“响应所有群消息”。$/.exec(text6);
1873
+ match = /^(.+)已开通群消息权限,并启用“响应所有群消息”。$/.exec(text7);
1807
1874
  if (match) return `${match[1]} now has group-message permission and \u201CRespond to all group messages\u201D is enabled.`;
1808
- match = /^(检查连接|重试连接)(.+)$/.exec(text6);
1875
+ match = /^(检查连接|重试连接)(.+)$/.exec(text7);
1809
1876
  if (match) return `${localizeText(match[1])} ${match[2]}`;
1810
- match = /^移除(.+)$/.exec(text6);
1877
+ match = /^移除(.+)$/.exec(text7);
1811
1878
  if (match) return `Remove ${match[1]}`;
1812
- match = /^这会停止消息连接,并删除本机保存的 (.+)、机器人配置及会话映射。(.+)中的机器人不会被自动删除。$/.exec(text6);
1879
+ match = /^这会停止消息连接,并删除本机保存的 (.+)、机器人配置及会话映射。(.+)中的机器人不会被自动删除。$/.exec(text7);
1813
1880
  if (match) {
1814
1881
  return `This stops the message connection and removes the locally stored ${localizeText(match[1])}, bot configuration, and session mappings. The bot in ${localizeText(match[2])} is not deleted.`;
1815
1882
  }
1816
- match = /^二维码剩余 (.+)$/.exec(text6);
1883
+ match = /^二维码剩余 (.+)$/.exec(text7);
1817
1884
  if (match) return `QR code expires in ${match[1]}`;
1818
- match = /^最近一条消息处理失败:(.+)$/.exec(text6);
1885
+ match = /^最近一条消息处理失败:(.+)$/.exec(text7);
1819
1886
  if (match) return `Latest message failed: ${localizeText(match[1])}`;
1820
- match = /^图片下载失败(HTTP (.+)),请重新发送后再试。$/.exec(text6);
1887
+ match = /^图片下载失败(HTTP (.+)),请重新发送后再试。$/.exec(text7);
1821
1888
  if (match) return `The image download failed (HTTP ${match[1]}). Send it again.`;
1822
- match = /^一次最多只能处理 (\d+) 张图片。$/.exec(text6);
1889
+ match = /^一次最多只能处理 (\d+) 张图片。$/.exec(text7);
1823
1890
  if (match) return `A message can contain at most ${match[1]} images.`;
1824
- match = /^状态刷新失败:(.+)$/.exec(text6);
1891
+ match = /^状态刷新失败:(.+)$/.exec(text7);
1825
1892
  if (match) return `Status refresh failed: ${match[1]}`;
1826
- match = /^状态自动刷新失败:(.+)$/.exec(text6);
1893
+ match = /^状态自动刷新失败:(.+)$/.exec(text7);
1827
1894
  if (match) return `Automatic status refresh failed: ${match[1]}`;
1828
- match = /^操作失败:(.+)$/.exec(text6);
1895
+ match = /^操作失败:(.+)$/.exec(text7);
1829
1896
  if (match) return `Operation failed: ${match[1]}`;
1830
- match = /^连接检查失败:(.+)(参考号:(.+))$/.exec(text6);
1897
+ match = /^连接检查失败:(.+)(参考号:(.+))$/.exec(text7);
1831
1898
  if (match) return `Connection check failed: ${localizeText(match[1])} (reference: ${match[2]})`;
1832
- match = /^连接检查失败:(.+)$/.exec(text6);
1899
+ match = /^连接检查失败:(.+)$/.exec(text7);
1833
1900
  if (match) return `Connection check failed: ${match[1]}`;
1834
- match = /^移除失败:(.+)$/.exec(text6);
1901
+ match = /^移除失败:(.+)$/.exec(text7);
1835
1902
  if (match) return `Removal failed: ${match[1]}`;
1836
1903
  const phrases = [
1837
1904
  ["\u4F01\u4E1A\u5FAE\u4FE1", "WeCom"],
@@ -1868,7 +1935,7 @@ function translateDynamic(text6) {
1868
1935
  ["\u8FDE\u63A5\u72B6\u6001", "connection status"],
1869
1936
  ["\u4E8C\u7EF4\u7801", "QR code"]
1870
1937
  ];
1871
- let output = text6;
1938
+ let output = text7;
1872
1939
  for (const [source, target] of phrases) output = output.replaceAll(source, target);
1873
1940
  return output;
1874
1941
  }
@@ -1912,8 +1979,8 @@ var EMPTY_AGENT_PRESET_CATALOG = Object.freeze({
1912
1979
  var AgentPresetCatalogContext = React3.createContext(EMPTY_AGENT_PRESET_CATALOG);
1913
1980
  function normalizeAgentPresetId(value) {
1914
1981
  if (typeof value !== "string") return "";
1915
- const id5 = value.trim();
1916
- return PRESET_ID.test(id5) ? id5 : "";
1982
+ const id6 = value.trim();
1983
+ return PRESET_ID.test(id6) ? id6 : "";
1917
1984
  }
1918
1985
  function normalizeAgentPresetCatalog(value) {
1919
1986
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -1922,11 +1989,11 @@ function normalizeAgentPresetCatalog(value) {
1922
1989
  const items = [];
1923
1990
  const seen = /* @__PURE__ */ new Set();
1924
1991
  for (const entry of Array.isArray(value.items) ? value.items : []) {
1925
- const id5 = typeof entry === "string" ? normalizeAgentPresetId(entry) : normalizeAgentPresetId(entry?.id);
1926
- if (!id5 || seen.has(id5)) continue;
1927
- seen.add(id5);
1928
- const label = typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim().slice(0, 128) : typeof entry?.name === "string" && entry.name.trim() ? entry.name.trim().slice(0, 128) : id5;
1929
- items.push({ id: id5, label });
1992
+ const id6 = typeof entry === "string" ? normalizeAgentPresetId(entry) : normalizeAgentPresetId(entry?.id);
1993
+ if (!id6 || seen.has(id6)) continue;
1994
+ seen.add(id6);
1995
+ const label = typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim().slice(0, 128) : typeof entry?.name === "string" && entry.name.trim() ? entry.name.trim().slice(0, 128) : id6;
1996
+ items.push({ id: id6, label });
1930
1997
  }
1931
1998
  return {
1932
1999
  defaultId: normalizeAgentPresetId(value.defaultId),
@@ -2022,14 +2089,15 @@ var React4 = __toESM(require("react"), 1);
2022
2089
  // src/channels/shared/model-setting.mjs
2023
2090
  var MAX_PROVIDER_ID_LENGTH = 256;
2024
2091
  var MAX_MODEL_ID_LENGTH = 1024;
2092
+ var MAX_EFFORT_ID_LENGTH = 256;
2025
2093
  var MAX_LABEL_LENGTH = 256;
2026
2094
  var UNSAFE_DISPLAY_TEXT = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]+/gu;
2027
2095
  var UNSAFE_ID_TEXT = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
2028
2096
  function cleanId(value, maxLength) {
2029
2097
  if (typeof value !== "string") return "";
2030
- const id5 = value.trim();
2031
- if (!id5 || id5.length > maxLength || UNSAFE_ID_TEXT.test(id5)) return "";
2032
- return id5;
2098
+ const id6 = value.trim();
2099
+ if (!id6 || id6.length > maxLength || UNSAFE_ID_TEXT.test(id6)) return "";
2100
+ return id6;
2033
2101
  }
2034
2102
  function cleanLabel(value, fallback) {
2035
2103
  if (typeof value !== "string") return fallback;
@@ -2045,7 +2113,27 @@ function normalizeModelSelection(value) {
2045
2113
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2046
2114
  const provider = cleanId(value.provider, MAX_PROVIDER_ID_LENGTH);
2047
2115
  const model = cleanId(value.model, MAX_MODEL_ID_LENGTH);
2048
- return provider && model ? { provider, model } : null;
2116
+ const reasoningEffort = value.reasoningEffort === void 0 ? void 0 : cleanId(value.reasoningEffort, MAX_EFFORT_ID_LENGTH);
2117
+ if (!provider || !model || reasoningEffort === "") return null;
2118
+ return { provider, model, ...reasoningEffort === void 0 ? {} : { reasoningEffort } };
2119
+ }
2120
+ function sameModelSelection(left, right) {
2121
+ return left?.provider === right?.provider && left?.model === right?.model && left?.reasoningEffort === right?.reasoningEffort;
2122
+ }
2123
+ function normalizeReasoning(value) {
2124
+ if (!value || typeof value !== "object" || !Array.isArray(value.efforts)) return void 0;
2125
+ const efforts = [];
2126
+ const seen = /* @__PURE__ */ new Set();
2127
+ for (const entry of value.efforts) {
2128
+ const id6 = cleanId(entry?.id, MAX_EFFORT_ID_LENGTH);
2129
+ if (!id6 || seen.has(id6)) continue;
2130
+ seen.add(id6);
2131
+ const description = cleanLabel(entry.description, "");
2132
+ efforts.push({ id: id6, name: cleanLabel(entry.name, id6), ...description ? { description } : {} });
2133
+ }
2134
+ if (!efforts.length) return void 0;
2135
+ const defaultEffort = cleanId(value.defaultEffort, MAX_EFFORT_ID_LENGTH);
2136
+ return { efforts, ...seen.has(defaultEffort) ? { defaultEffort } : {} };
2049
2137
  }
2050
2138
  function modelSelectionId(value) {
2051
2139
  const selection = normalizeModelSelection(value);
@@ -2069,9 +2157,13 @@ function normalizeModelCatalog(value) {
2069
2157
  const key = `${provider}\0${model}`;
2070
2158
  if (seen.has(key)) continue;
2071
2159
  seen.add(key);
2160
+ const description = cleanLabel(entry.description, "");
2161
+ const reasoning = normalizeReasoning(entry.reasoning);
2072
2162
  models.push({
2073
2163
  id: model,
2074
- name: cleanLabel(entry.name, model)
2164
+ name: cleanLabel(entry.name, model),
2165
+ ...description ? { description } : {},
2166
+ ...reasoning ? { reasoning } : {}
2075
2167
  });
2076
2168
  }
2077
2169
  if (models.length > 0) {
@@ -2085,109 +2177,244 @@ function normalizeModelCatalog(value) {
2085
2177
  const failures = [];
2086
2178
  for (const candidate of Array.isArray(value.failures) ? value.failures : []) {
2087
2179
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
2088
- const id5 = cleanId(candidate.id, MAX_PROVIDER_ID_LENGTH);
2089
- if (!id5) continue;
2090
- failures.push({ id: id5, name: cleanLabel(candidate.name, id5) });
2180
+ const id6 = cleanId(candidate.id, MAX_PROVIDER_ID_LENGTH);
2181
+ if (!id6) continue;
2182
+ failures.push({ id: id6, name: cleanLabel(candidate.name, id6) });
2091
2183
  }
2092
2184
  return { groups, failures };
2093
2185
  }
2186
+ function modelCatalogEntry(catalog, selection) {
2187
+ const normalized = normalizeModelSelection(selection);
2188
+ if (!normalized) return void 0;
2189
+ return normalizeModelCatalog(catalog).groups.filter((group) => group.id === normalized.provider).flatMap((group) => group.models).find((model) => model.id === normalized.model);
2190
+ }
2094
2191
 
2095
2192
  // plugin-src/client/model-setting.js
2096
2193
  var SET_MODEL_ENDPOINT = "bot.model.set";
2097
2194
  var ModelCatalogContext = React4.createContext(EMPTY_MODEL_CATALOG);
2098
- function optionValue(selection) {
2099
- return JSON.stringify([selection.provider, selection.model]);
2195
+ function chevron(open = false) {
2196
+ return h2(
2197
+ "svg",
2198
+ {
2199
+ width: 14,
2200
+ height: 14,
2201
+ viewBox: "0 0 16 16",
2202
+ "aria-hidden": true,
2203
+ className: "dim-modelChevron",
2204
+ style: open ? { transform: "rotate(90deg)" } : void 0
2205
+ },
2206
+ h2("path", { d: "m6 4 4 4-4 4", fill: "none", stroke: "currentColor", strokeWidth: 1.5 })
2207
+ );
2100
2208
  }
2101
2209
  function ModelEditor({ model = null, disabled = false, onSave }) {
2102
- const catalog = normalizeModelCatalog(
2103
- React4.useContext(ModelCatalogContext) ?? EMPTY_MODEL_CATALOG
2104
- );
2105
- const helpId = React4.useId();
2106
- const current = normalizeModelSelection(model);
2210
+ const catalog = normalizeModelCatalog(React4.useContext(ModelCatalogContext));
2211
+ const id6 = React4.useId();
2212
+ const rootRef = React4.useRef(null);
2213
+ const menuRef = React4.useRef(null);
2214
+ const triggerRef = React4.useRef(null);
2215
+ const restoreFocusRef = React4.useRef(false);
2216
+ const savingRef = React4.useRef(false);
2217
+ const [pane, setPane] = React4.useState(null);
2107
2218
  const [saving, setSaving] = React4.useState(false);
2108
2219
  const [error, setError] = React4.useState(null);
2109
- const currentValue = current ? optionValue(current) : "";
2110
- const currentAvailable = !current || catalog.groups.some(
2111
- (group) => group.id === current.provider && group.models.some((entry) => entry.id === current.model)
2112
- );
2113
- const selections = /* @__PURE__ */ new Map();
2114
- for (const group of catalog.groups) {
2115
- for (const entry of group.models) {
2116
- const selection = { provider: group.id, model: entry.id };
2117
- selections.set(optionValue(selection), selection);
2220
+ const current = normalizeModelSelection(model);
2221
+ const entry = modelCatalogEntry(catalog, current);
2222
+ const currentAvailable = !current || Boolean(entry);
2223
+ const reasoning = entry?.reasoning;
2224
+ const effort = current?.reasoningEffort;
2225
+ const effectiveEffort = effort ?? reasoning?.defaultEffort;
2226
+ const effortEntry = reasoning?.efforts.find((level) => level.id === effectiveEffort);
2227
+ const effortUnavailable = effort !== void 0 && !reasoning?.efforts.some((level) => level.id === effort);
2228
+ const defaultName = reasoning?.efforts.find((level) => level.id === reasoning.defaultEffort)?.name;
2229
+ const defaultLabel = localizeText("\u8DDF\u968F\u6A21\u578B\u9ED8\u8BA4") + (defaultName ? ` \xB7 ${defaultName}` : "");
2230
+ const effortLabel = !current ? localizeText("\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B") : effort === void 0 ? defaultLabel : effortEntry?.name ?? effort;
2231
+ const effortHint = !current ? "\u5148\u9009\u62E9\u6A21\u578B\uFF0C\u518D\u8BBE\u7F6E\u601D\u8003\u5F3A\u5EA6\u3002" : !currentAvailable ? null : effortUnavailable ? "\u5DF2\u4FDD\u5B58\u7684\u601D\u8003\u5F3A\u5EA6\u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u5F3A\u5EA6\u6216\u6062\u590D\u9ED8\u8BA4\u3002" : !reasoning ? "\u8BE5\u6A21\u578B\u672A\u63D0\u4F9B\u53EF\u8C03\u8282\u7684\u601D\u8003\u5F3A\u5EA6\u3002" : null;
2232
+ const effortDisabled = !current || !currentAvailable || !reasoning && effort === void 0;
2233
+ const close = (restoreFocus = false) => {
2234
+ restoreFocusRef.current = restoreFocus;
2235
+ setPane(null);
2236
+ };
2237
+ React4.useEffect(() => {
2238
+ if (!pane && !saving && !disabled && restoreFocusRef.current) {
2239
+ restoreFocusRef.current = false;
2240
+ triggerRef.current?.focus();
2118
2241
  }
2119
- }
2120
- if (current && !currentAvailable) selections.set(currentValue, current);
2121
- const change = async (event) => {
2122
- const nextValue = event.target.value;
2123
- if (nextValue === currentValue || saving || disabled) return;
2124
- const next = nextValue ? selections.get(nextValue) : null;
2125
- if (nextValue && !next) return;
2242
+ }, [pane, saving, disabled]);
2243
+ React4.useEffect(() => {
2244
+ if (!pane) return void 0;
2245
+ const options = menuRef.current?.querySelectorAll('[role="menuitemradio"]');
2246
+ const selected = menuRef.current?.querySelector('[aria-checked="true"]');
2247
+ (selected ?? options?.[0])?.focus();
2248
+ const outside = (event) => {
2249
+ if (!rootRef.current?.contains(event.target)) setPane(null);
2250
+ };
2251
+ globalThis.document?.addEventListener("mousedown", outside);
2252
+ return () => globalThis.document?.removeEventListener("mousedown", outside);
2253
+ }, [pane]);
2254
+ const save = async (next) => {
2255
+ if (savingRef.current || disabled) return;
2256
+ if (sameModelSelection(current, next)) {
2257
+ close(true);
2258
+ return;
2259
+ }
2260
+ savingRef.current = true;
2126
2261
  setSaving(true);
2127
2262
  setError(null);
2128
2263
  try {
2129
2264
  await onSave?.(next);
2265
+ close(true);
2130
2266
  } catch (cause) {
2131
2267
  setError(cause?.message ?? "\u6A21\u578B\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2132
2268
  } finally {
2269
+ savingRef.current = false;
2133
2270
  setSaving(false);
2134
2271
  }
2135
2272
  };
2273
+ const option = (key, label, description, selected, next) => h2(
2274
+ "button",
2275
+ {
2276
+ key,
2277
+ type: "button",
2278
+ role: "menuitemradio",
2279
+ "aria-checked": selected,
2280
+ className: "dim-modelOption",
2281
+ disabled: disabled || saving,
2282
+ onClick: () => {
2283
+ void save(next);
2284
+ }
2285
+ },
2286
+ h2(
2287
+ "span",
2288
+ { className: "dim-modelOptionCopy" },
2289
+ h2("span", { className: "dim-modelOptionName" }, label),
2290
+ description ? h2("span", { className: "dim-modelDescription" }, description) : null
2291
+ ),
2292
+ h2("span", { className: "dim-modelCheck", "aria-hidden": true }, selected ? "\u2713" : "")
2293
+ );
2294
+ const row = (key, label, value, blocked = false) => h2(
2295
+ "button",
2296
+ {
2297
+ type: "button",
2298
+ className: "dim-modelRow",
2299
+ disabled: disabled || saving || blocked,
2300
+ "aria-label": label,
2301
+ "aria-haspopup": "menu",
2302
+ "aria-expanded": pane === key,
2303
+ "aria-controls": pane === key ? `${id6}-menu` : void 0,
2304
+ "aria-describedby": key === "effort" && effortHint ? `${id6}-hint` : void 0,
2305
+ onClick: (event) => {
2306
+ triggerRef.current = event.currentTarget;
2307
+ setPane(pane === key ? null : key);
2308
+ }
2309
+ },
2310
+ h2("span", { className: "dim-modelRowLabel" }, label),
2311
+ h2("span", { className: "dim-modelValue", title: value }, value),
2312
+ chevron(pane === key)
2313
+ );
2136
2314
  return h2(
2137
2315
  "div",
2138
- { className: "dim-preset dim-modelSetting" },
2316
+ {
2317
+ ref: rootRef,
2318
+ className: "dim-preset dim-modelSetting",
2319
+ onBlur: (event) => {
2320
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) close();
2321
+ },
2322
+ onKeyDown: (event) => {
2323
+ if (!pane) return;
2324
+ if (event.key === "Escape") {
2325
+ event.preventDefault();
2326
+ event.stopPropagation();
2327
+ close(true);
2328
+ }
2329
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
2330
+ const items = [...menuRef.current?.querySelectorAll('[role="menuitemradio"]:not(:disabled)') ?? []];
2331
+ if (!items.length) return;
2332
+ event.preventDefault();
2333
+ const at = items.indexOf(globalThis.document?.activeElement);
2334
+ const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : (at + (event.key === "ArrowDown" ? 1 : -1) + items.length) % items.length;
2335
+ items[next]?.focus();
2336
+ }
2337
+ },
2139
2338
  h2(
2140
2339
  "div",
2141
2340
  { className: "dim-presetHeader" },
2142
2341
  h2(
2143
2342
  "span",
2144
2343
  { className: "dim-presetTitle" },
2145
- h2("span", null, "\u6A21\u578B"),
2344
+ "\u6A21\u578B\u4E0E\u601D\u8003\u5F3A\u5EA6",
2146
2345
  h2(
2147
2346
  "span",
2148
2347
  { className: "dim-presetHelp" },
2149
- h2("button", {
2150
- type: "button",
2151
- className: "dim-presetHelpButton",
2152
- "aria-label": "\u67E5\u770B\u6A21\u578B\u8BBE\u7F6E\u8BF4\u660E",
2153
- "aria-describedby": helpId
2154
- }, h2("span", { "aria-hidden": "true" }, "?")),
2155
- h2("span", {
2156
- id: helpId,
2157
- className: "dim-presetTooltip",
2158
- role: "tooltip"
2159
- }, "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002")
2348
+ h2(
2349
+ "button",
2350
+ {
2351
+ type: "button",
2352
+ className: "dim-presetHelpButton",
2353
+ "aria-label": "\u67E5\u770B\u6A21\u578B\u8BBE\u7F6E\u8BF4\u660E",
2354
+ "aria-describedby": `${id6}-help`
2355
+ },
2356
+ h2("span", { "aria-hidden": true }, "?")
2357
+ ),
2358
+ h2(
2359
+ "span",
2360
+ { id: `${id6}-help`, className: "dim-presetTooltip", role: "tooltip" },
2361
+ "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002"
2362
+ )
2160
2363
  )
2161
2364
  ),
2162
- saving ? h2("span", { className: "dim-presetStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
2365
+ saving ? h2("span", { className: "dim-presetStatus", role: "status" }, "\u4FDD\u5B58\u4E2D\u2026") : null
2163
2366
  ),
2164
- h2(
2165
- "select",
2367
+ row("model", "\u6A21\u578B", entry?.name ?? (current ? modelSelectionId(current) : localizeText("\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B"))),
2368
+ row("effort", "\u601D\u8003\u5F3A\u5EA6", effortLabel, effortDisabled),
2369
+ pane ? h2(
2370
+ "div",
2166
2371
  {
2167
- className: "dim-presetSelect dim-modelSelect",
2168
- value: currentValue,
2169
- disabled: disabled || saving,
2170
- "aria-label": "\u6A21\u578B",
2171
- onChange: (event) => {
2172
- void change(event);
2173
- }
2372
+ ref: menuRef,
2373
+ id: `${id6}-menu`,
2374
+ role: "menu",
2375
+ "aria-label": pane === "model" ? "\u6A21\u578B" : "\u601D\u8003\u5F3A\u5EA6",
2376
+ "aria-busy": saving,
2377
+ className: "dim-modelMenu"
2174
2378
  },
2175
- h2("option", { value: "" }, "\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B"),
2176
- ...catalog.groups.map((group) => h2(
2177
- "optgroup",
2178
- { key: group.id, label: group.name },
2179
- ...group.models.map((entry) => {
2180
- const selection = { provider: group.id, model: entry.id };
2181
- const id5 = modelSelectionId(selection);
2182
- return h2(
2183
- "option",
2184
- { key: id5, value: optionValue(selection) },
2185
- entry.name && entry.name !== entry.id ? `${entry.name}\uFF08${id5}\uFF09` : id5
2186
- );
2187
- })
2188
- )),
2189
- current && !currentAvailable ? h2("option", { value: currentValue }, [modelSelectionId(current), "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09"]) : null
2190
- ),
2379
+ pane === "model" ? [
2380
+ option("default", "\u8DDF\u968F\u9ED8\u8BA4\u6A21\u578B", null, !current, null),
2381
+ ...catalog.groups.map((group) => h2(
2382
+ "section",
2383
+ { key: group.id, role: "group", "aria-label": group.name },
2384
+ h2("div", { className: "dim-modelGroupTitle" }, group.name),
2385
+ ...group.models.map((choice) => {
2386
+ const selected = current?.provider === group.id && current?.model === choice.id;
2387
+ return option(
2388
+ choice.id,
2389
+ choice.name,
2390
+ choice.description ?? `${group.id}/${choice.id}`,
2391
+ selected,
2392
+ selected ? current : { provider: group.id, model: choice.id }
2393
+ );
2394
+ })
2395
+ ))
2396
+ ] : [
2397
+ option(
2398
+ "provider-default",
2399
+ defaultLabel,
2400
+ "\u4F7F\u7528\u6A21\u578B\u6216\u670D\u52A1\u63D0\u4F9B\u65B9\u7684\u9ED8\u8BA4\u601D\u8003\u5F3A\u5EA6\u3002",
2401
+ effort === void 0,
2402
+ current ? { provider: current.provider, model: current.model } : null
2403
+ ),
2404
+ ...(reasoning?.efforts ?? []).map((level) => option(
2405
+ `effort:${level.id}`,
2406
+ level.name,
2407
+ level.description,
2408
+ effort === level.id,
2409
+ { ...current, reasoningEffort: level.id }
2410
+ ))
2411
+ ]
2412
+ ) : null,
2413
+ effortHint ? h2("p", {
2414
+ id: `${id6}-hint`,
2415
+ className: "dim-modelHint",
2416
+ role: effortUnavailable ? "status" : void 0
2417
+ }, effortHint) : null,
2191
2418
  error || !currentAvailable ? h2(
2192
2419
  "p",
2193
2420
  { className: "dim-presetError", role: error ? "alert" : "status" },
@@ -2238,11 +2465,11 @@ function normalizeAccessPolicyUserId(value) {
2238
2465
  value = String(value);
2239
2466
  }
2240
2467
  if (typeof value !== "string") throw invalidAccessPolicy("\u7528\u6237\u6807\u8BC6\u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3002");
2241
- const id5 = value.trim();
2242
- if (!id5 || id5.length > ACCESS_POLICY_USER_ID_MAX_LENGTH || CONTROL_CHARACTERS.test(id5)) {
2468
+ const id6 = value.trim();
2469
+ if (!id6 || id6.length > ACCESS_POLICY_USER_ID_MAX_LENGTH || CONTROL_CHARACTERS.test(id6)) {
2243
2470
  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`);
2244
2471
  }
2245
- return id5;
2472
+ return id6;
2246
2473
  }
2247
2474
  function validateAccessPolicyUser(input) {
2248
2475
  if (!hasExactKeys(input, USER_KEYS)) {
@@ -2259,7 +2486,7 @@ function validateAccessPolicyUser(input) {
2259
2486
  function validateUsers(input, { listMessage, duplicateMessage }) {
2260
2487
  if (!Array.isArray(input)) throw invalidAccessPolicy(listMessage);
2261
2488
  const users = input.map(validateAccessPolicyUser);
2262
- if (new Set(users.map(({ id: id5 }) => id5)).size !== users.length) {
2489
+ if (new Set(users.map(({ id: id6 }) => id6)).size !== users.length) {
2263
2490
  throw invalidAccessPolicy(duplicateMessage);
2264
2491
  }
2265
2492
  return Object.freeze(users);
@@ -2481,8 +2708,8 @@ function optionalString(value, maxLength = 240) {
2481
2708
  return trimmed ? trimmed.slice(0, maxLength) : void 0;
2482
2709
  }
2483
2710
  function opaqueId(value) {
2484
- const id5 = optionalString(value, 128);
2485
- return id5 && /^[a-z\d_-]+$/i.test(id5) ? id5 : void 0;
2711
+ const id6 = optionalString(value, 128);
2712
+ return id6 && /^[a-z\d_-]+$/i.test(id6) ? id6 : void 0;
2486
2713
  }
2487
2714
  function timestamp(value) {
2488
2715
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -2813,13 +3040,13 @@ function CollapsibleAccountSection({
2813
3040
  defaultOpen = false,
2814
3041
  open: controlledOpen,
2815
3042
  onToggle,
2816
- id: id5,
3043
+ id: id6,
2817
3044
  className = "",
2818
3045
  children
2819
3046
  }) {
2820
3047
  const [uncontrolledOpen, setUncontrolledOpen] = React6.useState(defaultOpen);
2821
3048
  const open = controlledOpen ?? uncontrolledOpen;
2822
- const contentId = id5 ? `${id5}-content` : void 0;
3049
+ const contentId = id6 ? `${id6}-content` : void 0;
2823
3050
  const toggle = () => {
2824
3051
  if (controlledOpen === void 0) setUncontrolledOpen((value) => !value);
2825
3052
  onToggle?.(!open);
@@ -3492,7 +3719,7 @@ function ContextEnhancementScopeEditor({
3492
3719
  )
3493
3720
  );
3494
3721
  }
3495
- function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id: id5 }) {
3722
+ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id: id6 }) {
3496
3723
  const [draft, setDraft] = React9.useState(() => {
3497
3724
  const normalized = normalizeContextEnhancementConfig(config);
3498
3725
  return {
@@ -3584,7 +3811,7 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
3584
3811
  }, h2(
3585
3812
  "section",
3586
3813
  {
3587
- id: id5,
3814
+ id: id6,
3588
3815
  ref: dialogRef,
3589
3816
  className: "dim-contextDialog",
3590
3817
  role: "dialog",
@@ -3850,7 +4077,7 @@ function messageErrorTime(value) {
3850
4077
  return null;
3851
4078
  }
3852
4079
  }
3853
- function ChannelListHeading({ className = "", id: id5, title, connectionLabel }) {
4080
+ function ChannelListHeading({ className = "", id: id6, title, connectionLabel }) {
3854
4081
  const helpId = React11.useId();
3855
4082
  return h2(
3856
4083
  "div",
@@ -3858,7 +4085,7 @@ function ChannelListHeading({ className = "", id: id5, title, connectionLabel })
3858
4085
  h2(
3859
4086
  "div",
3860
4087
  { className: "dim-listTitle" },
3861
- h2("h3", id5 ? { id: id5 } : null, title),
4088
+ h2("h3", id6 ? { id: id6 } : null, title),
3862
4089
  h2(
3863
4090
  "span",
3864
4091
  { className: "dim-channelHelp" },
@@ -5117,7 +5344,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
5117
5344
  function createTokenChannelApi(channel4, connectionSummary, {
5118
5345
  normalizeBotExtension = () => ({})
5119
5346
  } = {}) {
5120
- const unwrapRpcResult13 = (result) => {
5347
+ const unwrapRpcResult14 = (result) => {
5121
5348
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
5122
5349
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
5123
5350
  }
@@ -5128,7 +5355,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
5128
5355
  }
5129
5356
  return result.value;
5130
5357
  };
5131
- const normalizeBot7 = (value) => {
5358
+ const normalizeBot8 = (value) => {
5132
5359
  if (!isRecord2(value) || !id(value.botId)) return void 0;
5133
5360
  const connected = value.connected === true;
5134
5361
  const state = ACCOUNT_STATES2.has(value.state) ? value.state : "offline";
@@ -5162,12 +5389,12 @@ function createTokenChannelApi(channel4, connectionSummary, {
5162
5389
  ...isRecord2(extension) ? extension : {}
5163
5390
  };
5164
5391
  };
5165
- const normalizeSnapshot9 = (value) => {
5392
+ const normalizeSnapshot10 = (value) => {
5166
5393
  const source = isRecord2(value?.snapshot) ? value.snapshot : value;
5167
5394
  if (!isRecord2(source) || !Array.isArray(source.bots)) {
5168
5395
  throw new Error(`${channel4} \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868`);
5169
5396
  }
5170
- const bots = source.bots.map(normalizeBot7).filter(Boolean);
5397
+ const bots = source.bots.map(normalizeBot8).filter(Boolean);
5171
5398
  return {
5172
5399
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
5173
5400
  bots,
@@ -5176,11 +5403,11 @@ function createTokenChannelApi(channel4, connectionSummary, {
5176
5403
  modelCatalog: normalizeModelCatalog(source.modelCatalog)
5177
5404
  };
5178
5405
  };
5179
- const presentError13 = (error) => ({
5406
+ const presentError14 = (error) => ({
5180
5407
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
5181
5408
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
5182
5409
  });
5183
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult13, normalizeSnapshot: normalizeSnapshot9, presentError: presentError13 });
5410
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult14, normalizeSnapshot: normalizeSnapshot10, presentError: presentError14 });
5184
5411
  }
5185
5412
 
5186
5413
  // plugin-src/client/channels/discord/api.js
@@ -5245,7 +5472,7 @@ function createTokenChannelSettings(definition) {
5245
5472
  AccountSettings = null,
5246
5473
  accountSettingsEndpoint = null
5247
5474
  } = definition;
5248
- function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onModelSave, onAgentPresetSave, onContextEnhancementSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
5475
+ function AccountCard6({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onModelSave, onAgentPresetSave, onContextEnhancementSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
5249
5476
  const state = busy === "reconnect" ? "connecting" : account.state;
5250
5477
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
5251
5478
  const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
@@ -5536,7 +5763,7 @@ function createTokenChannelSettings(definition) {
5536
5763
  title: `\u5DF2\u63A5\u5165\u7684 ${channel4} \u673A\u5668\u4EBA`,
5537
5764
  connectionLabel
5538
5765
  }),
5539
- h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard5, {
5766
+ h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard6, {
5540
5767
  account,
5541
5768
  busy: busyByBot[account.botId],
5542
5769
  testNotice: testNoticeByBot[account.botId],
@@ -5698,7 +5925,7 @@ function createTokenChannelSettings(definition) {
5698
5925
  )
5699
5926
  )));
5700
5927
  }
5701
- return { SettingsTab, AccountCard: AccountCard5 };
5928
+ return { SettingsTab, AccountCard: AccountCard6 };
5702
5929
  }
5703
5930
 
5704
5931
  // plugin-src/client/channels/discord/styles.js
@@ -6344,7 +6571,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
6344
6571
  h2("span", null, repairing ? `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u8865\u5168\u6743\u9650\u4E0E\u56DE\u8C03` : grantingGroupMessages ? `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650` : "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA")
6345
6572
  ),
6346
6573
  h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : repairing ? "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u8865\u5168\u6743\u9650" : grantingGroupMessages ? "\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u7FA4\u6D88\u606F\u6743\u9650" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"),
6347
- h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : grantingGroupMessages ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"),
6574
+ h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u589E\u91CF\u8865\u5145\u5F53\u524D\u7F3A\u5C11\u7684\u5361\u7247\u6309\u94AE\u56DE\u8C03\u3001\u8BFB\u53D6\u7528\u6237\u6D88\u606F\u5185\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:message:readonly\uFF08\u98DE\u4E66\u663E\u793A\u4E3A\u201C\u83B7\u53D6\u5355\u804A\u3001\u7FA4\u7EC4\u6D88\u606F\u201D\uFF09\u3001\u4E0A\u4F20\u673A\u5668\u4EBA\u56FE\u7247\u6216\u6587\u4EF6\u6240\u9700\u7684 im:resource\u3001\u63A5\u6536\u7FA4\u5185\u5176\u4ED6\u673A\u5668\u4EBA @ \u5F53\u524D\u673A\u5668\u4EBA\u6240\u9700\u7684 im:message.group_at_msg.include_bot:readonly\uFF0C\u4EE5\u53CA\u539F\u751F\u547D\u4EE4\u9762\u677F\u6240\u9700\u7684 application:app_slash_command:read / write\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u9875\u53EA\u663E\u793A\u5F53\u524D\u7F3A\u5C11\u9879\uFF0C\u5B8C\u6210\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : grantingGroupMessages ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"),
6348
6575
  h2(
6349
6576
  "ol",
6350
6577
  { className: "bxf-steps dim-steps" },
@@ -6428,14 +6655,14 @@ var HEALTH_LABELS = {
6428
6655
  offline: "\u8FDE\u63A5\u4E2D\u65AD",
6429
6656
  error: "\u9700\u8981\u5904\u7406"
6430
6657
  };
6431
- function formatCheckedTime(timestamp7) {
6432
- if (!timestamp7) return "\u5C1A\u672A\u68C0\u67E5";
6658
+ function formatCheckedTime(timestamp8) {
6659
+ if (!timestamp8) return "\u5C1A\u672A\u68C0\u67E5";
6433
6660
  try {
6434
6661
  return new Intl.DateTimeFormat("zh-CN", {
6435
6662
  hour: "2-digit",
6436
6663
  minute: "2-digit",
6437
6664
  second: "2-digit"
6438
- }).format(new Date(timestamp7));
6665
+ }).format(new Date(timestamp8));
6439
6666
  } catch {
6440
6667
  return "\u521A\u521A";
6441
6668
  }
@@ -6489,6 +6716,7 @@ function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
6489
6716
  }
6490
6717
  function StepPushEditor({ value = false, disabled = false, onSave }) {
6491
6718
  const titleId = React15.useId();
6719
+ const helpId = `${titleId}-help`;
6492
6720
  const current = value === true ? "on" : "off";
6493
6721
  const [saving, setSaving] = React15.useState(false);
6494
6722
  const [error, setError] = React15.useState(null);
@@ -6514,7 +6742,26 @@ function StepPushEditor({ value = false, disabled = false, onSave }) {
6514
6742
  h2(
6515
6743
  "div",
6516
6744
  { className: "dim-feishuGroupControlHeader" },
6517
- h2("h3", { id: titleId }, "\u5206\u6B65\u76F4\u63A8"),
6745
+ h2(
6746
+ "div",
6747
+ { className: "dim-presetTitle" },
6748
+ h2("h3", { id: titleId }, "\u5206\u6B65\u76F4\u63A8"),
6749
+ h2(
6750
+ "span",
6751
+ { className: "dim-presetHelp" },
6752
+ h2("button", {
6753
+ type: "button",
6754
+ className: "dim-presetHelpButton",
6755
+ "aria-label": "\u67E5\u770B\u5206\u6B65\u76F4\u63A8\u8BF4\u660E",
6756
+ "aria-describedby": helpId
6757
+ }, h2("span", { "aria-hidden": "true" }, "?")),
6758
+ h2("span", {
6759
+ id: helpId,
6760
+ className: "dim-presetTooltip",
6761
+ role: "tooltip"
6762
+ }, "\u5F00\u542F\u540E\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E")
6763
+ )
6764
+ ),
6518
6765
  saving ? h2("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "\u4FDD\u5B58\u4E2D\u2026") : null
6519
6766
  ),
6520
6767
  h2(
@@ -6531,11 +6778,6 @@ function StepPushEditor({ value = false, disabled = false, onSave }) {
6531
6778
  h2("option", { value: "off" }, "\u5173\u95ED\uFF08\u4FDD\u6301\u6D41\u5F0F\u5361\u6A21\u5F0F\uFF09"),
6532
6779
  h2("option", { value: "on" }, "\u5F00\u542F\uFF08\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E\uFF09")
6533
6780
  ),
6534
- h2(
6535
- "p",
6536
- { className: "dim-feishuGroupHelp" },
6537
- "\u5F00\u542F\u540E\u9010\u6B65\u63A8\u9001\u5DE5\u5177\u8C03\u7528\u4E0E\u8FC7\u7A0B\u8BF4\u660E"
6538
- ),
6539
6781
  error ? h2("p", {
6540
6782
  className: "dim-feishuGroupError",
6541
6783
  role: "alert"
@@ -6993,15 +7235,15 @@ function FeishuSettingsTab({ rpcCall }) {
6993
7235
  if (targetedUpdate && (provision2.operation !== operation || provision2.botId !== botId)) {
6994
7236
  throw new Error(grantingGroupMessages ? "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801" : "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6743\u9650\u8865\u5168\u4E8C\u7EF4\u7801");
6995
7237
  }
6996
- const timestamp7 = Date.now();
6997
- setNow(timestamp7);
7238
+ const timestamp8 = Date.now();
7239
+ setNow(timestamp8);
6998
7240
  setModel((current) => ({
6999
7241
  ...current,
7000
7242
  provisioning: {
7001
7243
  phase: "qr",
7002
7244
  ...provision2,
7003
7245
  ...botName ? { botName } : {},
7004
- durationMs: Math.max(1, provision2.expiresAt - timestamp7),
7246
+ durationMs: Math.max(1, provision2.expiresAt - timestamp8),
7005
7247
  expired: false
7006
7248
  }
7007
7249
  }));
@@ -7118,9 +7360,9 @@ function FeishuSettingsTab({ rpcCall }) {
7118
7360
  React15.useEffect(() => {
7119
7361
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
7120
7362
  const tick = () => {
7121
- const timestamp7 = Date.now();
7122
- setNow(timestamp7);
7123
- if (timestamp7 >= countdownExpiresAt) {
7363
+ const timestamp8 = Date.now();
7364
+ setNow(timestamp8);
7365
+ if (timestamp8 >= countdownExpiresAt) {
7124
7366
  setModel((current) => current.provisioning?.attemptId === countdownAttemptId ? { ...current, provisioning: { ...current.provisioning, expired: true } } : current);
7125
7367
  }
7126
7368
  };
@@ -10353,17 +10595,13 @@ function WecomSettingsTab({ rpcCall }) {
10353
10595
  )));
10354
10596
  }
10355
10597
 
10356
- // plugin-src/client/channels/weixin/index.js
10357
- var React20 = __toESM(require("react"), 1);
10358
-
10359
- // plugin-src/client/channels/weixin/api.js
10360
- var WEIXIN_RPC_CHANNEL = "/weixin";
10361
- var WEIXIN_ENDPOINTS = Object.freeze({
10598
+ // plugin-src/client/channels/wecom-app/api.js
10599
+ var WECOM_APP_RPC_CHANNEL = "/wecom-app";
10600
+ var WECOM_APP_ENDPOINTS = Object.freeze({
10362
10601
  status: "connection.status",
10363
- beginProvisioning: "provision.begin",
10364
- pollProvisioning: "provision.poll",
10365
- submitVerification: "provision.verify",
10366
- cancelProvisioning: "provision.cancel",
10602
+ bindApp: "bot.bind",
10603
+ updateSettings: "bot.settings.update",
10604
+ resetCallbackSecret: "bot.callback-secret.reset",
10367
10605
  reconnectBot: "bot.reconnect",
10368
10606
  deleteBot: "bot.delete",
10369
10607
  setWorkspace: "bot.workspace.set",
@@ -10373,25 +10611,24 @@ var WEIXIN_ENDPOINTS = Object.freeze({
10373
10611
  setAccessPolicy: "bot.access-policy.set"
10374
10612
  });
10375
10613
  var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
10376
- var PROVISION_STATES4 = /* @__PURE__ */ new Set([
10377
- "starting",
10378
- "pending",
10379
- "scanned",
10380
- "needs_verification",
10381
- "connecting",
10382
- "connected",
10383
- "expired",
10384
- "failed",
10385
- "cancelled"
10386
- ]);
10387
10614
  function isRecord6(value) {
10388
10615
  return value !== null && typeof value === "object" && !Array.isArray(value);
10389
10616
  }
10390
- function string(value, fallback = "") {
10391
- return typeof value === "string" && value.trim() ? value.trim() : fallback;
10617
+ function text5(value, fallback, max = 240) {
10618
+ return typeof value === "string" && value.trim() ? value.trim().slice(0, max) : fallback;
10619
+ }
10620
+ function id4(value) {
10621
+ const result = text5(value, "", 128);
10622
+ return /^[a-z\d_-]+$/i.test(result) ? result : void 0;
10623
+ }
10624
+ function optionalUrl(value) {
10625
+ const raw = text5(value, "", 512);
10626
+ return /^https?:\/\//iu.test(raw) ? raw : null;
10392
10627
  }
10393
10628
  function timestamp5(value) {
10394
- return typeof value === "number" && Number.isFinite(value) ? value : null;
10629
+ if (typeof value === "number" && Number.isFinite(value)) return value;
10630
+ const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
10631
+ return Number.isNaN(parsed) ? void 0 : parsed;
10395
10632
  }
10396
10633
  function normalizeTestMessage4(value) {
10397
10634
  if (!isRecord6(value)) return null;
@@ -10401,154 +10638,993 @@ function normalizeTestMessage4(value) {
10401
10638
  return { sent: false, code };
10402
10639
  }
10403
10640
  function unwrapRpcResult8(result) {
10404
- if (!isRecord6(result) || typeof result.ok !== "boolean") {
10405
- throw new Error("\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
10406
- }
10641
+ if (!isRecord6(result) || typeof result.ok !== "boolean") throw new Error("\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
10407
10642
  if (!result.ok) {
10408
- const error = new Error(string(result.error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25"));
10409
- error.code = string(result.error?.code, "WEIXIN_RPC_ERROR");
10643
+ const error = new Error(text5(result.error?.message, "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u64CD\u4F5C\u5931\u8D25"));
10644
+ error.code = text5(result.error?.code, "WECOM_APP_RPC_ERROR", 80);
10410
10645
  throw error;
10411
10646
  }
10412
10647
  return result.value;
10413
10648
  }
10414
- function safeQrSource5(value) {
10415
- return typeof value === "string" && /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
10416
- }
10417
- function safeVerificationUrl(value) {
10418
- if (typeof value !== "string") return void 0;
10419
- try {
10420
- const url = new URL(value);
10421
- const host = url.hostname.toLowerCase();
10422
- return url.protocol === "https:" && (host === "weixin.qq.com" || host.endsWith(".weixin.qq.com") || host === "wechat.com" || host.endsWith(".wechat.com")) ? url.toString() : void 0;
10423
- } catch {
10424
- return void 0;
10425
- }
10426
- }
10427
- function normalizeProvisioning5(value) {
10428
- if (!isRecord6(value) || !string(value.attemptId)) {
10429
- throw new Error("\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
10430
- }
10431
- const status = PROVISION_STATES4.has(value.status) ? value.status : "failed";
10432
- const result = {
10433
- attemptId: string(value.attemptId),
10434
- status,
10435
- expiresAt: timestamp5(value.expiresAt) ?? Date.now(),
10436
- pollIntervalMs: Math.min(5e3, Math.max(500, Number(value.pollIntervalMs) || 1e3)),
10437
- verificationRequired: value.verificationRequired === true || status === "needs_verification"
10438
- };
10439
- const verificationUrl = safeVerificationUrl(value.verificationUrl);
10440
- const qrCodeDataUrl = safeQrSource5(value.qrCodeDataUrl);
10441
- if (verificationUrl) result.verificationUrl = verificationUrl;
10442
- if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
10443
- if (string(value.botId)) result.botId = string(value.botId);
10444
- if (value.alreadyConnected === true) result.alreadyConnected = true;
10445
- if (isRecord6(value.error)) {
10446
- result.error = {
10447
- code: string(value.error.code, "WEIXIN_PROVISION_FAILED"),
10448
- message: string(value.error.message, "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210")
10449
- };
10450
- }
10451
- return result;
10452
- }
10453
10649
  function normalizeBot5(value) {
10454
- if (!isRecord6(value) || !string(value.botId) || !isRecord6(value.bot)) return null;
10455
- const state = ACCOUNT_STATES5.has(value.state) ? value.state : "error";
10650
+ if (!isRecord6(value) || !id4(value.botId)) return void 0;
10456
10651
  const connected = value.connected === true;
10652
+ const state = ACCOUNT_STATES5.has(value.state) ? value.state : "offline";
10457
10653
  return {
10458
- botId: string(value.botId),
10459
- state: connected ? "connected" : state,
10654
+ botId: id4(value.botId),
10460
10655
  connected,
10461
- configured: value.configured === true,
10462
- workspace: string(value.workspace).slice(0, 4096),
10656
+ state: connected ? "connected" : state,
10657
+ workspace: text5(value.workspace, "", 4096),
10463
10658
  model: normalizeModelSelection(value.model),
10464
10659
  agentPreset: normalizeAgentPresetId(value.agentPreset),
10465
10660
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
10466
10661
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
10467
10662
  bot: {
10468
- name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
10469
- accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
10663
+ name: text5(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528", 100),
10664
+ corpIdMasked: text5(value.bot?.corpIdMasked, "\u4F01\u4E1A ID \u5DF2\u4FDD\u5B58", 140),
10665
+ agentId: text5(value.bot?.agentId, "", 32),
10666
+ apiBaseUrl: optionalUrl(value.bot?.apiBaseUrl) ?? "",
10667
+ callbackBaseUrl: optionalUrl(value.bot?.callbackBaseUrl) ?? "",
10668
+ streamEnabled: value.bot?.streamEnabled !== false,
10669
+ callbackUrl: optionalUrl(value.bot?.callbackUrl) ?? ""
10470
10670
  },
10471
10671
  health: {
10472
- status: string(value.health?.status, connected ? "healthy" : "offline"),
10473
- summary: string(value.health?.summary, connected ? "\u5FAE\u4FE1\u8FDE\u63A5\u6B63\u5E38" : "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA"),
10672
+ summary: text5(value.health?.summary, connected ? "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u56DE\u8C03\u901A\u9053\u5C31\u7EEA" : "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
10474
10673
  lastCheckedAt: timestamp5(value.health?.lastCheckedAt)
10475
10674
  },
10476
- stats: {
10477
- messagesReceived: Math.max(0, Number(value.stats?.messagesReceived) || 0),
10478
- messagesReplied: Math.max(0, Number(value.stats?.messagesReplied) || 0)
10479
- },
10480
10675
  lastMessageError: normalizeLastMessageError(value.lastMessageError),
10481
10676
  error: isRecord6(value.error) ? {
10482
- code: string(value.error.code, "WEIXIN_ACCOUNT_ERROR"),
10483
- message: string(value.error.message, "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA")
10677
+ code: text5(value.error.code, "WECOM_APP_ACCOUNT_ERROR", 80),
10678
+ message: text5(value.error.message, "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA")
10484
10679
  } : null
10485
10680
  };
10486
10681
  }
10487
10682
  function normalizeSnapshot7(value) {
10488
- if (!isRecord6(value) || !Array.isArray(value.bots)) {
10489
- throw new Error("\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u8D26\u53F7\u5217\u8868");
10490
- }
10491
- const bots = value.bots.map(normalizeBot5).filter(Boolean);
10683
+ const source = isRecord6(value?.snapshot) ? value.snapshot : value;
10684
+ if (!isRecord6(source) || !Array.isArray(source.bots)) throw new Error("\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
10685
+ const bots = source.bots.map(normalizeBot5).filter(Boolean);
10492
10686
  return {
10493
- schemaVersion: Number(value.schemaVersion) || 1,
10494
- revision: Number(value.revision) || 0,
10495
- state: string(value.state, "offline"),
10687
+ revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
10496
10688
  bots,
10497
- totals: {
10498
- configured: bots.length,
10499
- connected: bots.filter((bot) => bot.connected).length
10500
- },
10501
- provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
10502
- testMessage: normalizeTestMessage4(value.testMessage),
10503
- agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
10504
- modelCatalog: normalizeModelCatalog(value.modelCatalog)
10689
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
10690
+ testMessage: normalizeTestMessage4(source.testMessage),
10691
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
10692
+ modelCatalog: normalizeModelCatalog(source.modelCatalog)
10505
10693
  };
10506
10694
  }
10507
10695
  function presentError8(error) {
10508
10696
  return {
10509
- code: string(error?.code, "WEIXIN_ERROR"),
10510
- message: string(error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
10697
+ code: text5(error?.code, "WECOM_APP_ERROR", 80),
10698
+ message: text5(error?.message, "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
10511
10699
  };
10512
10700
  }
10513
- function formatRemaining5(milliseconds) {
10514
- const seconds = Math.max(0, Math.ceil(milliseconds / 1e3));
10515
- return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
10701
+
10702
+ // plugin-src/client/channels/wecom-app/index.js
10703
+ var React20 = __toESM(require("react"), 1);
10704
+
10705
+ // plugin-src/client/channels/wecom-app/styles.js
10706
+ var WECOM_APP_STYLE_ID = "xmanrui-dsh-im-wecom-app-settings";
10707
+ var CSS9 = String.raw`
10708
+ .dwecomapp-page { --ddt-accent: #07c160; --ddt-accent-deep: #059a4c; --ddt-accent-wash: #eefaf3; }
10709
+ .dwecomapp-avatar, .dwecomapp-brand { color: #07c160; background: #fff; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); }
10710
+ .dwecomapp-avatar svg, .dwecomapp-brand svg { display: block; }
10711
+ .dim-appFieldGrid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
10712
+ .dim-callbackBox { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border: 1px dashed var(--dsw-alias-border-l2, #e5e6eb); border-radius: 8px; }
10713
+ .dim-callbackBox strong { font-size: 12px; color: var(--dsw-alias-text-secondary, #646a73); }
10714
+ .dim-callbackRow { display: flex; gap: 8px; align-items: center; }
10715
+ .dim-callbackRow input { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
10716
+ .dim-appSwitchRow { display: flex; align-items: center; gap: 8px; font-size: 13px; }
10717
+ `;
10718
+ function installWecomAppStyles() {
10719
+ if (typeof document === "undefined") return () => {
10720
+ };
10721
+ const existing = document.querySelector(`style[data-plugin-css="${WECOM_APP_STYLE_ID}"]`);
10722
+ if (existing) return () => {
10723
+ };
10724
+ const style = document.createElement("style");
10725
+ style.dataset.plugin = "@xmanrui/dsh-im";
10726
+ style.dataset.pluginCss = WECOM_APP_STYLE_ID;
10727
+ style.textContent = CSS9;
10728
+ document.head.appendChild(style);
10729
+ return () => style.remove();
10516
10730
  }
10517
10731
 
10518
- // plugin-src/client/channels/weixin/index.js
10732
+ // plugin-src/client/channels/wecom-app/index.js
10519
10733
  var Button12 = React20.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
10520
10734
  return h2("button", {
10521
10735
  ...props,
10522
10736
  ref,
10523
10737
  type: "button",
10524
- className: `dxw-button ${className}`.trim(),
10738
+ className: `ddt-button ${className}`.trim(),
10525
10739
  "data-kind": kind
10526
10740
  }, children);
10527
10741
  });
10742
+ function checkedTime5(value) {
10743
+ if (!value) return "\u5C1A\u672A\u68C0\u67E5";
10744
+ try {
10745
+ return new Intl.DateTimeFormat("zh-CN", {
10746
+ hour: "2-digit",
10747
+ minute: "2-digit",
10748
+ second: "2-digit"
10749
+ }).format(new Date(value));
10750
+ } catch {
10751
+ return "\u521A\u521A";
10752
+ }
10753
+ }
10528
10754
  function Heading5({ totals, adding, busy, onAdd, addButtonRef }) {
10529
10755
  return h2(
10530
10756
  "div",
10531
- { className: "dxw-heading" },
10757
+ { className: "ddt-heading" },
10532
10758
  h2(
10533
10759
  "div",
10534
- { className: "dxw-tools" },
10535
- h2(Button12, {
10536
- kind: "primary",
10537
- className: "dim-scanButton",
10538
- onClick: onAdd,
10539
- disabled: adding || busy,
10540
- ref: addButtonRef,
10541
- "aria-label": "\u626B\u7801\u63A5\u5165\u5FAE\u4FE1\u673A\u5668\u4EBA"
10542
- }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"),
10543
- totals.configured > 0 ? h2(
10544
- "div",
10760
+ { className: "ddt-tools" },
10761
+ h2(
10762
+ "div",
10763
+ { className: "dim-bindActions" },
10764
+ h2(Button12, {
10765
+ kind: "primary",
10766
+ className: "dim-scanButton",
10767
+ onClick: onAdd,
10768
+ disabled: adding || busy,
10769
+ ref: addButtonRef,
10770
+ "aria-label": "\u6DFB\u52A0\u4F01\u4E1A\u5FAE\u4FE1\u81EA\u5EFA\u5E94\u7528"
10771
+ }, adding ? "\u6536\u8D77\u8868\u5355" : "\u6DFB\u52A0\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528")
10772
+ ),
10773
+ totals.configured > 0 ? h2(
10774
+ "div",
10775
+ { className: "ddt-badge dim-onlineBadge" },
10776
+ h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`)
10777
+ ) : null
10778
+ )
10779
+ );
10780
+ }
10781
+ function LoadingView5() {
10782
+ return h2(
10783
+ "div",
10784
+ { className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" },
10785
+ h2("div", { className: "ddt-spinner dim-spinner" }),
10786
+ h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u72B6\u6001\u2026")
10787
+ );
10788
+ }
10789
+ function EmptyView5({ busy, onStart }) {
10790
+ return h2(
10791
+ "div",
10792
+ { className: "ddt-card dim-surfaceCard" },
10793
+ h2(
10794
+ "div",
10795
+ { className: "ddt-cardBody ddt-empty dim-surfaceBody dim-emptyView" },
10796
+ h2(
10797
+ "div",
10798
+ { className: "dim-emptyCopy" },
10799
+ h2(
10800
+ "div",
10801
+ { className: "ddt-stateLabel dim-stateLabel" },
10802
+ h2("span", { className: "ddt-dot dim-stateDot" }),
10803
+ h2("span", null, "\u5C1A\u672A\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528")
10804
+ ),
10805
+ h2("h3", null, "\u5728\u4F01\u4E1A\u5FAE\u4FE1\u4E2D\u521B\u5EFA\u81EA\u5EFA\u5E94\u7528\uFF0C\u5373\u53EF\u5728\u5FAE\u4FE1\u91CC\u4F7F\u7528"),
10806
+ h2("p", null, "\u5728\u5E94\u7528\u7BA1\u7406\u540E\u53F0\u521B\u5EFA\u81EA\u5EFA\u5E94\u7528\uFF0C\u5F00\u542F\u300C\u63A5\u6536\u6D88\u606F\u300D\u5E76\u586B\u5165\u4E0B\u9762\u7684\u53C2\u6570\u3002\u6210\u5458\u7684\u5FAE\u4FE1\u5173\u6CE8\u8BE5\u4F01\u4E1A\u7684\u5FAE\u4FE1\u63D2\u4EF6\u540E\uFF0C\u5C31\u80FD\u5728\u5FAE\u4FE1\u4E2D\u4E0E\u672C\u5E94\u7528\u53CC\u5411\u5BF9\u8BDD\u3002"),
10807
+ h2(
10808
+ "div",
10809
+ { className: "ddt-actions dim-viewActions" },
10810
+ h2(Button12, { kind: "primary", onClick: onStart, disabled: busy }, "\u586B\u5199\u5E94\u7528\u53C2\u6570")
10811
+ )
10812
+ ),
10813
+ h2(
10814
+ "div",
10815
+ { className: "ddt-brandMark dim-emptyBrand dwecomapp-brand", "aria-hidden": "true" },
10816
+ h2(WecomLogoGlyph, { size: 64 })
10817
+ )
10818
+ )
10819
+ );
10820
+ }
10821
+ function Field({ label, value, onChange, placeholder, busy, required = false, type = "text" }) {
10822
+ return h2(
10823
+ "label",
10824
+ { className: "dim-credentialField" },
10825
+ h2("span", null, label),
10826
+ h2("input", {
10827
+ value,
10828
+ onChange: (event) => onChange(event.target.value),
10829
+ placeholder,
10830
+ type,
10831
+ autoCapitalize: "none",
10832
+ autoCorrect: "off",
10833
+ spellCheck: false,
10834
+ autoComplete: "off",
10835
+ disabled: busy,
10836
+ required
10837
+ })
10838
+ );
10839
+ }
10840
+ function BindForm({ busy, error, onSubmit, onCancel }) {
10841
+ const [corpId, setCorpId] = React20.useState("");
10842
+ const [agentId, setAgentId] = React20.useState("");
10843
+ const [secret, setSecret] = React20.useState("");
10844
+ const [token, setToken] = React20.useState("");
10845
+ const [aesKey, setAesKey] = React20.useState("");
10846
+ const [apiBaseUrl, setApiBaseUrl] = React20.useState("");
10847
+ const [callbackBaseUrl, setCallbackBaseUrl] = React20.useState("");
10848
+ const [streamEnabled, setStreamEnabled] = React20.useState(true);
10849
+ const headingId = React20.useId();
10850
+ const formRef = React20.useRef(null);
10851
+ const submit = (event) => {
10852
+ event.preventDefault();
10853
+ if (busy) return;
10854
+ void onSubmit?.({
10855
+ corpId: corpId.trim(),
10856
+ agentId: agentId.trim(),
10857
+ secret: secret.trim(),
10858
+ token: token.trim(),
10859
+ encodingAESKey: aesKey.trim(),
10860
+ apiBaseUrl: apiBaseUrl.trim() || void 0,
10861
+ callbackBaseUrl: callbackBaseUrl.trim() || void 0,
10862
+ streamEnabled
10863
+ });
10864
+ };
10865
+ return h2(
10866
+ "section",
10867
+ {
10868
+ className: "ddt-card dim-surfaceCard dim-credentialPanel",
10869
+ "aria-labelledby": headingId
10870
+ },
10871
+ h2("h3", { id: headingId, className: "dim-credentialTitle" }, "\u7ED1\u5B9A\u4F01\u4E1A\u5FAE\u4FE1\u81EA\u5EFA\u5E94\u7528"),
10872
+ h2(
10873
+ "form",
10874
+ { ref: formRef, className: "dim-credentialForm", onSubmit: submit },
10875
+ h2(
10876
+ "div",
10877
+ { className: "dim-appFieldGrid" },
10878
+ h2(Field, { label: "\u4F01\u4E1A ID\uFF08CorpID\uFF09", value: corpId, onChange: setCorpId, placeholder: "\u4F8B\u5982 ww1234567890abcdef", busy, required: true }),
10879
+ h2(Field, { label: "\u5E94\u7528 AgentId", value: agentId, onChange: setAgentId, placeholder: "\u4F8B\u5982 1000002", busy, required: true }),
10880
+ h2(Field, { label: "\u5E94\u7528 Secret", value: secret, onChange: setSecret, placeholder: "\u5E94\u7528\u8BE6\u60C5\u9875\u7684 Secret", busy, required: true, type: "password" }),
10881
+ h2(Field, { label: "\u56DE\u8C03 Token", value: token, onChange: setToken, placeholder: "\u63A5\u6536\u6D88\u606F \u2192 API \u63A5\u6536\u4E2D\u7684 Token", busy, required: true, type: "password" }),
10882
+ h2(Field, { label: "EncodingAESKey", value: aesKey, onChange: setAesKey, placeholder: "43 \u4F4D\u5B57\u6BCD\u6216\u6570\u5B57", busy, required: true, type: "password" }),
10883
+ h2(Field, { label: "\u4EE3\u7406\u5730\u5740\uFF08\u53EF\u9009\uFF09", value: apiBaseUrl, onChange: setApiBaseUrl, placeholder: "\u7559\u7A7A\u76F4\u8FDE qyapi.weixin.qq.com", busy })
10884
+ ),
10885
+ h2(
10886
+ "div",
10887
+ { className: "dim-appFieldGrid" },
10888
+ h2(Field, { label: "\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF08\u53EF\u9009\uFF09", value: callbackBaseUrl, onChange: setCallbackBaseUrl, placeholder: "\u4F8B\u5982 https://im.example.com", busy }),
10889
+ h2(
10890
+ "div",
10891
+ { className: "dim-appSwitchRow" },
10892
+ h2("button", {
10893
+ type: "button",
10894
+ className: "ddt-button dim-streamToggle",
10895
+ "aria-pressed": streamEnabled ? "true" : "false",
10896
+ onClick: () => setStreamEnabled((value) => !value),
10897
+ disabled: busy
10898
+ }, streamEnabled ? "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5F00" : "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5173"),
10899
+ h2("span", { className: "dim-switchHint" }, "\u4F01\u4E1A\u5FAE\u4FE1\u5BA2\u6237\u7AEF\u5B9E\u65F6\u51FA\u5B57\uFF1B\u5FAE\u4FE1\u7AEF\u4E0D\u652F\u6301\u65F6\u81EA\u52A8\u6539\u4E3A\u6574\u6BB5\u53D1\u9001")
10900
+ )
10901
+ ),
10902
+ error ? h2("div", { className: "ddt-inlineError dim-inlineError", role: "alert" }, h2("p", null, error.message)) : null,
10903
+ h2(
10904
+ "div",
10905
+ { className: "ddt-actions dim-viewActions" },
10906
+ h2(Button12, { kind: "primary", onClick: () => formRef.current?.requestSubmit(), disabled: busy }, busy ? "\u6B63\u5728\u7ED1\u5B9A\u2026" : "\u4FDD\u5B58\u5E76\u8FDE\u63A5"),
10907
+ h2(Button12, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
10908
+ )
10909
+ )
10910
+ );
10911
+ }
10912
+ function CallbackUrlBox({ url, busy, resetBusy, onCopy, onReset }) {
10913
+ const [copied, setCopied] = React20.useState(false);
10914
+ const copy = async () => {
10915
+ try {
10916
+ await navigator.clipboard.writeText(url);
10917
+ setCopied(true);
10918
+ window.setTimeout(() => setCopied(false), 2e3);
10919
+ } catch {
10920
+ setCopied(false);
10921
+ }
10922
+ onCopy?.();
10923
+ };
10924
+ return h2(
10925
+ "div",
10926
+ { className: "dim-callbackBox" },
10927
+ h2("strong", null, "\u56DE\u8C03 URL\uFF08\u586B\u5165\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u300C\u63A5\u6536\u6D88\u606F \u2192 \u8BBE\u7F6EAPI\u63A5\u6536\u300D\uFF09"),
10928
+ h2(
10929
+ "div",
10930
+ { className: "dim-callbackRow" },
10931
+ h2("input", {
10932
+ value: url || "\u672A\u914D\u7F6E\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF1A\u8BF7\u5728\u672C\u9875\u300C\u8BBE\u7F6E\u300D\u4E2D\u586B\u5199\u56DE\u8C03\u57FA\u5740\uFF0C\u6216\u624B\u52A8\u62FC\u63A5 \u56DE\u8C03\u57FA\u5740 + \u8DEF\u5F84",
10933
+ readOnly: true,
10934
+ onFocus: (event) => event.target.select?.()
10935
+ }),
10936
+ url ? h2(Button12, { onClick: copy, disabled: busy, small: true }, copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236") : null,
10937
+ h2(Button12, { kind: "danger", onClick: onReset, disabled: busy || resetBusy }, resetBusy ? "\u91CD\u7F6E\u4E2D\u2026" : "\u91CD\u7F6E\u5BC6\u94A5")
10938
+ ),
10939
+ h2(
10940
+ "span",
10941
+ { className: "dim-callbackHint" },
10942
+ "\u91CD\u7F6E\u5BC6\u94A5\u540E\u56DE\u8C03 URL \u4F1A\u53D8\u5316\uFF0C\u9700\u8981\u540C\u6B65\u66F4\u65B0\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u3002\u82E5\u516C\u7F51\u53CD\u4EE3\u672A\u5C31\u7EEA\uFF0C\u53EF\u4E34\u65F6\u4F7F\u7528 http://\u670D\u52A1\u5668IP:\u7AEF\u53E3 \u5F62\u5F0F\u7684\u56DE\u8C03\u5730\u5740\u3002"
10943
+ )
10944
+ );
10945
+ }
10946
+ function AppSettingsEditor({ bot, busy, onSave }) {
10947
+ const [apiBaseUrl, setApiBaseUrl] = React20.useState(bot.apiBaseUrl ?? "");
10948
+ const [callbackBaseUrl, setCallbackBaseUrl] = React20.useState(bot.callbackBaseUrl ?? "");
10949
+ const [streamEnabled, setStreamEnabled] = React20.useState(bot.streamEnabled !== false);
10950
+ const [dirty, setDirty] = React20.useState(false);
10951
+ const formRef = React20.useRef(null);
10952
+ React20.useEffect(() => {
10953
+ setApiBaseUrl(bot.apiBaseUrl ?? "");
10954
+ setCallbackBaseUrl(bot.callbackBaseUrl ?? "");
10955
+ setStreamEnabled(bot.streamEnabled !== false);
10956
+ setDirty(false);
10957
+ }, [bot.apiBaseUrl, bot.callbackBaseUrl, bot.streamEnabled]);
10958
+ const touch = () => setDirty(true);
10959
+ const save = (event) => {
10960
+ event.preventDefault();
10961
+ if (!dirty || busy) return;
10962
+ void onSave({
10963
+ apiBaseUrl: apiBaseUrl.trim() || null,
10964
+ callbackBaseUrl: callbackBaseUrl.trim() || null,
10965
+ streamEnabled
10966
+ });
10967
+ setDirty(false);
10968
+ };
10969
+ return h2(
10970
+ "form",
10971
+ { ref: formRef, className: "dim-appFieldGrid", onSubmit: save },
10972
+ h2(Field, { label: "\u4EE3\u7406\u5730\u5740\uFF08\u53EF\u9009\uFF09", value: apiBaseUrl, onChange: (value) => {
10973
+ setApiBaseUrl(value);
10974
+ touch();
10975
+ }, placeholder: "\u7559\u7A7A\u76F4\u8FDE qyapi.weixin.qq.com", busy }),
10976
+ h2(Field, { label: "\u516C\u7F51\u56DE\u8C03\u57FA\u5740\uFF08\u53EF\u9009\uFF09", value: callbackBaseUrl, onChange: (value) => {
10977
+ setCallbackBaseUrl(value);
10978
+ touch();
10979
+ }, placeholder: "\u4F8B\u5982 https://im.example.com", busy }),
10980
+ h2(
10981
+ "div",
10982
+ { className: "dim-appSwitchRow" },
10983
+ h2("button", {
10984
+ type: "button",
10985
+ className: "ddt-button dim-streamToggle",
10986
+ "aria-pressed": streamEnabled ? "true" : "false",
10987
+ onClick: () => {
10988
+ setStreamEnabled((value) => !value);
10989
+ touch();
10990
+ },
10991
+ disabled: busy
10992
+ }, streamEnabled ? "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5F00" : "\u6D41\u5F0F\u56DE\u590D\uFF1A\u5173")
10993
+ ),
10994
+ h2(
10995
+ "div",
10996
+ { className: "ddt-actions dim-viewActions" },
10997
+ h2(Button12, { onClick: () => formRef.current?.requestSubmit(), disabled: busy || !dirty }, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u8BBE\u7F6E")
10998
+ )
10999
+ );
11000
+ }
11001
+ function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
11002
+ return h2(
11003
+ "div",
11004
+ { className: "ddt-confirm dim-confirm", role: "alertdialog" },
11005
+ h2("strong", null, `\u4ECE DeepSeek Harness \u79FB\u9664\u201C${account.bot.name}\u201D\uFF1F`),
11006
+ h2("p", null, "\u8FD9\u4F1A\u5220\u9664\u672C\u673A\u4FDD\u5B58\u7684\u5E94\u7528\u51ED\u636E\u3001\u56DE\u8C03\u914D\u7F6E\u53CA\u4F1A\u8BDD\u6620\u5C04\u3002\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u7684\u5E94\u7528\u4E0D\u4F1A\u88AB\u5220\u9664\u3002"),
11007
+ h2(
11008
+ "div",
11009
+ { className: "ddt-actions dim-viewActions" },
11010
+ h2(Button12, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u5E94\u7528"),
11011
+ h2(Button12, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165")
11012
+ )
11013
+ );
11014
+ }
11015
+ function AccountCard4({
11016
+ account,
11017
+ busy,
11018
+ feedback,
11019
+ removing,
11020
+ onReconnect,
11021
+ onSettingsSave,
11022
+ onSecretReset,
11023
+ onWorkspaceSave,
11024
+ onModelSave,
11025
+ onAgentPresetSave,
11026
+ onContextEnhancementSave,
11027
+ onRequestRemove,
11028
+ onConfirmRemove,
11029
+ onCancelRemove
11030
+ }) {
11031
+ const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
11032
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
11033
+ const summary2 = account.error?.message ?? (account.connected ? null : account.health.summary);
11034
+ const elementId = `wecomapp-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
11035
+ return h2(
11036
+ "article",
11037
+ { className: "ddt-card dim-botCard", "data-bot-id": account.botId },
11038
+ h2(
11039
+ "div",
11040
+ { className: "ddt-cardBody dim-botCardBody" },
11041
+ h2(
11042
+ CollapsibleAccountSection,
11043
+ {
11044
+ id: elementId,
11045
+ header: h2(
11046
+ "div",
11047
+ { className: "ddt-accountTop dim-botCardTop" },
11048
+ h2(
11049
+ "div",
11050
+ { className: "ddt-accountIdentity dim-botIdentity" },
11051
+ h2("div", { className: "ddt-avatar dim-botAvatar dwecomapp-avatar", "aria-hidden": "true" }, h2(WecomLogoGlyph, { size: 29 })),
11052
+ h2(
11053
+ "div",
11054
+ { className: "dim-botName" },
11055
+ h2("h3", null, account.bot.name),
11056
+ h2("p", null, `${account.bot.corpIdMasked} \xB7 AgentId ${account.bot.agentId}`)
11057
+ )
11058
+ ),
11059
+ h2(
11060
+ "div",
11061
+ {
11062
+ className: "dim-botCardTools",
11063
+ onClick: (event) => {
11064
+ event.stopPropagation();
11065
+ },
11066
+ onKeyDown: (event) => {
11067
+ if (event.key === "Enter" || event.key === " ") event.stopPropagation();
11068
+ }
11069
+ },
11070
+ h2(BotStatusMeta, {
11071
+ className: "ddt-health",
11072
+ dotClassName: "ddt-dot",
11073
+ tone,
11074
+ stateLabel: stateLabel2,
11075
+ lastCheckedAt: account.health.lastCheckedAt,
11076
+ formatCheckedTime: checkedTime5
11077
+ }),
11078
+ h2(BotSettingsButton, {
11079
+ channel: "wecomApp",
11080
+ botId: account.botId,
11081
+ botName: account.bot.name,
11082
+ connected: account.connected,
11083
+ accessPolicy: account.accessPolicy
11084
+ })
11085
+ )
11086
+ )
11087
+ },
11088
+ h2(CallbackUrlBox, {
11089
+ url: account.bot.callbackUrl,
11090
+ busy: Boolean(busy),
11091
+ resetBusy: busy === "reset",
11092
+ onReset: onSecretReset
11093
+ }),
11094
+ h2(AppSettingsEditor, {
11095
+ bot: account.bot,
11096
+ busy: Boolean(busy),
11097
+ onSave: onSettingsSave
11098
+ }),
11099
+ h2(WorkspaceEditor, {
11100
+ workspace: account.workspace,
11101
+ disabled: Boolean(busy),
11102
+ onSave: onWorkspaceSave
11103
+ }),
11104
+ h2(ModelEditor, {
11105
+ model: account.model,
11106
+ disabled: Boolean(busy),
11107
+ onSave: onModelSave
11108
+ }),
11109
+ h2(AgentPresetEditor, {
11110
+ agentPreset: account.agentPreset,
11111
+ disabled: Boolean(busy),
11112
+ onSave: onAgentPresetSave
11113
+ }),
11114
+ h2(ContextEnhancementEditor, {
11115
+ config: account.contextEnhancement,
11116
+ disabled: Boolean(busy),
11117
+ onSave: onContextEnhancementSave
11118
+ }),
11119
+ h2(
11120
+ "div",
11121
+ { className: "ddt-accountFooter dim-cardFooter" },
11122
+ h2(
11123
+ "div",
11124
+ { className: "dim-cardFooterLayout" },
11125
+ h2(
11126
+ "div",
11127
+ { className: "ddt-actions dim-cardActions" },
11128
+ h2(Button12, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
11129
+ h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
11130
+ ),
11131
+ summary2 ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary2) : null,
11132
+ account.lastMessageError ? h2(LastMessageErrorSummary, {
11133
+ className: "ddt-summary",
11134
+ error: account.lastMessageError
11135
+ }) : null,
11136
+ feedback ? h2("div", {
11137
+ className: "ddt-summary dim-cardFeedback",
11138
+ role: "status",
11139
+ "aria-live": "polite"
11140
+ }, feedback) : null
11141
+ )
11142
+ )
11143
+ )
11144
+ ),
11145
+ removing ? h2(RemoveConfirmation5, {
11146
+ account,
11147
+ busy: busy === "delete",
11148
+ onConfirm: onConfirmRemove,
11149
+ onCancel: onCancelRemove
11150
+ }) : null
11151
+ );
11152
+ }
11153
+ function WecomAppSettingsTab({ rpcCall }) {
11154
+ const [model, setModel] = React20.useState({
11155
+ phase: "loading",
11156
+ bots: [],
11157
+ totals: { configured: 0, connected: 0 },
11158
+ error: null,
11159
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
11160
+ modelCatalog: EMPTY_MODEL_CATALOG
11161
+ });
11162
+ const [bindOpen, setBindOpen] = React20.useState(false);
11163
+ const [bindError, setBindError] = React20.useState(null);
11164
+ const [busy, setBusy] = React20.useState(false);
11165
+ const [busyByBot, setBusyByBot] = React20.useState({});
11166
+ const [feedbackByBot, setFeedbackByBot] = React20.useState({});
11167
+ const [removeTarget, setRemoveTarget] = React20.useState(null);
11168
+ const [notice, setNotice] = React20.useState("");
11169
+ const mounted = React20.useRef(true);
11170
+ const workspaceFence = useWorkspaceSnapshotFence();
11171
+ const addButtonRef = React20.useRef(null);
11172
+ const noticeFrameRef = React20.useRef(null);
11173
+ const announce = React20.useCallback((message) => {
11174
+ if (!mounted.current) return;
11175
+ if (noticeFrameRef.current !== null) {
11176
+ window.cancelAnimationFrame(noticeFrameRef.current);
11177
+ noticeFrameRef.current = null;
11178
+ }
11179
+ setNotice("");
11180
+ if (message) {
11181
+ noticeFrameRef.current = window.requestAnimationFrame(() => {
11182
+ noticeFrameRef.current = null;
11183
+ if (mounted.current) setNotice(message);
11184
+ });
11185
+ }
11186
+ }, []);
11187
+ React20.useEffect(() => {
11188
+ const disposeDingtalk = installDingtalkStyles();
11189
+ const disposeWecomApp = installWecomAppStyles();
11190
+ mounted.current = true;
11191
+ return () => {
11192
+ mounted.current = false;
11193
+ if (noticeFrameRef.current !== null) {
11194
+ window.cancelAnimationFrame(noticeFrameRef.current);
11195
+ noticeFrameRef.current = null;
11196
+ }
11197
+ disposeWecomApp();
11198
+ disposeDingtalk();
11199
+ };
11200
+ }, []);
11201
+ const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
11202
+ if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
11203
+ return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
11204
+ }, [rpcCall]);
11205
+ const loadStatus = React20.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
11206
+ const workspaceVersion = workspaceFence.beginStatus();
11207
+ if (workspaceVersion === null) return void 0;
11208
+ if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
11209
+ try {
11210
+ const snapshot = normalizeSnapshot7(await invoke(WECOM_APP_ENDPOINTS.status, {}, signal));
11211
+ if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
11212
+ setModel({
11213
+ phase: "ready",
11214
+ bots: snapshot.bots,
11215
+ totals: snapshot.totals,
11216
+ error: null,
11217
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11218
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
11219
+ });
11220
+ if (restore && snapshot.bots.length > 0) setBindOpen(false);
11221
+ return snapshot;
11222
+ } catch (error) {
11223
+ if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
11224
+ setModel((current) => ({ ...current, phase: silent ? current.phase : "error", error: presentError8(error) }));
11225
+ }
11226
+ return void 0;
11227
+ }
11228
+ }, [invoke, workspaceFence]);
11229
+ React20.useEffect(() => {
11230
+ const controller = new AbortController();
11231
+ void loadStatus({ signal: controller.signal, restore: true });
11232
+ return () => controller.abort();
11233
+ }, [loadStatus]);
11234
+ React20.useEffect(() => {
11235
+ if (model.phase !== "ready") return void 0;
11236
+ const controller = new AbortController();
11237
+ const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
11238
+ return () => {
11239
+ controller.abort();
11240
+ window.clearInterval(timer);
11241
+ };
11242
+ }, [loadStatus, model.phase]);
11243
+ const bindApp = React20.useCallback(async (payload) => {
11244
+ const snapshotVersion = workspaceFence.beginMutation();
11245
+ setBusy(true);
11246
+ setBindError(null);
11247
+ try {
11248
+ const snapshot = normalizeSnapshot7(await invoke(WECOM_APP_ENDPOINTS.bindApp, payload));
11249
+ if (!mounted.current) return;
11250
+ if (workspaceFence.canCommitMutation(snapshotVersion)) {
11251
+ setModel({
11252
+ phase: "ready",
11253
+ bots: snapshot.bots,
11254
+ totals: snapshot.totals,
11255
+ error: null,
11256
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11257
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
11258
+ });
11259
+ }
11260
+ setBindOpen(false);
11261
+ announce("\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u5DF2\u7ED1\u5B9A\u3002\u8BF7\u628A\u56DE\u8C03 URL \u586B\u5165\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u5E76\u4FDD\u5B58\u3002");
11262
+ } catch (error) {
11263
+ if (mounted.current) setBindError(presentError8(error));
11264
+ } finally {
11265
+ const shouldRefresh = workspaceFence.endMutation();
11266
+ if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
11267
+ if (mounted.current) setBusy(false);
11268
+ }
11269
+ }, [announce, invoke, loadStatus, workspaceFence]);
11270
+ const botAction = React20.useCallback(async (account, operation, endpoint, payload) => {
11271
+ const snapshotVersion = workspaceFence.beginMutation();
11272
+ setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
11273
+ try {
11274
+ const snapshot = normalizeSnapshot7(await invoke(endpoint, payload));
11275
+ if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
11276
+ setModel({
11277
+ phase: "ready",
11278
+ bots: snapshot.bots,
11279
+ totals: snapshot.totals,
11280
+ error: null,
11281
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
11282
+ modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG
11283
+ });
11284
+ }
11285
+ return snapshot;
11286
+ } finally {
11287
+ const shouldRefresh = workspaceFence.endMutation();
11288
+ if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
11289
+ if (mounted.current) setBusyByBot((current) => {
11290
+ const next = { ...current };
11291
+ delete next[account.botId];
11292
+ return next;
11293
+ });
11294
+ }
11295
+ }, [invoke, loadStatus, workspaceFence]);
11296
+ const reconnect = React20.useCallback(async (account) => {
11297
+ setFeedbackByBot((current) => {
11298
+ const next = { ...current };
11299
+ delete next[account.botId];
11300
+ return next;
11301
+ });
11302
+ try {
11303
+ const snapshot = await botAction(
11304
+ account,
11305
+ "reconnect",
11306
+ WECOM_APP_ENDPOINTS.reconnectBot,
11307
+ { botId: account.botId, sendTest: true }
11308
+ );
11309
+ if (!snapshot) return;
11310
+ const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
11311
+ let feedback;
11312
+ if (!refreshed?.connected) {
11313
+ feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u4ECD\u672A\u5C31\u7EEA\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002";
11314
+ } else if (snapshot.testMessage?.sent) {
11315
+ feedback = "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
11316
+ } else if (snapshot.testMessage?.code === "test-target-unavailable") {
11317
+ feedback = "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u5E94\u7528\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002";
11318
+ } else if (snapshot.testMessage) {
11319
+ feedback = "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002";
11320
+ } else {
11321
+ feedback = "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
11322
+ }
11323
+ if (mounted.current) {
11324
+ setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
11325
+ }
11326
+ announce(feedback);
11327
+ } catch {
11328
+ const feedback = "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
11329
+ if (mounted.current) {
11330
+ setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
11331
+ }
11332
+ announce(feedback);
11333
+ }
11334
+ }, [announce, botAction]);
11335
+ const botList = model.bots.length > 0 ? h2(
11336
+ "section",
11337
+ { className: "dim-listSection" },
11338
+ h2(ChannelListHeading, {
11339
+ className: "ddt-listHeading",
11340
+ title: "\u5DF2\u7ED1\u5B9A\u7684\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528",
11341
+ connectionLabel: "HTTP \u56DE\u8C03\u901A\u9053"
11342
+ }),
11343
+ h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard4, {
11344
+ account,
11345
+ busy: busyByBot[account.botId],
11346
+ feedback: feedbackByBot[account.botId],
11347
+ removing: removeTarget === account.botId,
11348
+ onReconnect: () => void reconnect(account),
11349
+ onSettingsSave: (settings) => botAction(
11350
+ account,
11351
+ "settings",
11352
+ WECOM_APP_ENDPOINTS.updateSettings,
11353
+ { botId: account.botId, ...settings }
11354
+ ),
11355
+ onSecretReset: async () => {
11356
+ try {
11357
+ await botAction(account, "reset", WECOM_APP_ENDPOINTS.resetCallbackSecret, { botId: account.botId });
11358
+ announce("\u56DE\u8C03\u5BC6\u94A5\u5DF2\u91CD\u7F6E\uFF0C\u8BF7\u628A\u65B0\u7684\u56DE\u8C03 URL \u66F4\u65B0\u5230\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u3002");
11359
+ } catch {
11360
+ announce("\u56DE\u8C03\u5BC6\u94A5\u91CD\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
11361
+ }
11362
+ },
11363
+ onWorkspaceSave: (workspace) => botAction(
11364
+ account,
11365
+ "workspace",
11366
+ WECOM_APP_ENDPOINTS.setWorkspace,
11367
+ { botId: account.botId, workspace }
11368
+ ),
11369
+ onModelSave: (selectedModel) => botAction(
11370
+ account,
11371
+ "model",
11372
+ WECOM_APP_ENDPOINTS.setModel,
11373
+ { botId: account.botId, model: selectedModel }
11374
+ ),
11375
+ onAgentPresetSave: (agentPreset) => botAction(
11376
+ account,
11377
+ "preset",
11378
+ WECOM_APP_ENDPOINTS.setAgentPreset,
11379
+ { botId: account.botId, agentPreset }
11380
+ ),
11381
+ onContextEnhancementSave: (config) => botAction(
11382
+ account,
11383
+ "context-enhancement",
11384
+ WECOM_APP_ENDPOINTS.setContextEnhancement,
11385
+ { botId: account.botId, config }
11386
+ ),
11387
+ onRequestRemove: () => setRemoveTarget(account.botId),
11388
+ onCancelRemove: () => setRemoveTarget(null),
11389
+ onConfirmRemove: async () => {
11390
+ await botAction(account, "delete", WECOM_APP_ENDPOINTS.deleteBot, { botId: account.botId, confirm: true });
11391
+ if (mounted.current) setRemoveTarget(null);
11392
+ }
11393
+ }))))
11394
+ ) : null;
11395
+ const bindView = bindOpen ? h2(BindForm, {
11396
+ busy,
11397
+ error: bindError,
11398
+ onSubmit: bindApp,
11399
+ onCancel: () => {
11400
+ setBindOpen(false);
11401
+ setBindError(null);
11402
+ }
11403
+ }) : null;
11404
+ return h2(ModelCatalogContext.Provider, {
11405
+ value: model.modelCatalog ?? EMPTY_MODEL_CATALOG
11406
+ }, h2(AgentPresetCatalogContext.Provider, {
11407
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
11408
+ }, h2(
11409
+ "section",
11410
+ { className: "ddt-page dwecomapp-page dim-channelPage", "aria-label": "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528\u8BBE\u7F6E" },
11411
+ h2(Heading5, {
11412
+ totals: model.totals,
11413
+ adding: bindOpen,
11414
+ busy,
11415
+ onAdd: () => {
11416
+ setBindOpen((value) => !value);
11417
+ setBindError(null);
11418
+ },
11419
+ addButtonRef
11420
+ }),
11421
+ h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
11422
+ model.phase === "loading" ? h2(LoadingView5) : 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\u5E94\u7528\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
11423
+ React20.Fragment,
11424
+ null,
11425
+ bindView,
11426
+ model.bots.length === 0 && !bindOpen ? h2(EmptyView5, { busy, onStart: () => setBindOpen(true) }) : null,
11427
+ botList
11428
+ )
11429
+ )));
11430
+ }
11431
+
11432
+ // plugin-src/client/channels/weixin/index.js
11433
+ var React21 = __toESM(require("react"), 1);
11434
+
11435
+ // plugin-src/client/channels/weixin/api.js
11436
+ var WEIXIN_RPC_CHANNEL = "/weixin";
11437
+ var WEIXIN_ENDPOINTS = Object.freeze({
11438
+ status: "connection.status",
11439
+ beginProvisioning: "provision.begin",
11440
+ pollProvisioning: "provision.poll",
11441
+ submitVerification: "provision.verify",
11442
+ cancelProvisioning: "provision.cancel",
11443
+ reconnectBot: "bot.reconnect",
11444
+ deleteBot: "bot.delete",
11445
+ setWorkspace: "bot.workspace.set",
11446
+ setModel: SET_MODEL_ENDPOINT,
11447
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
11448
+ setContextEnhancement: "bot.context-enhancement.set",
11449
+ setAccessPolicy: "bot.access-policy.set"
11450
+ });
11451
+ var ACCOUNT_STATES6 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
11452
+ var PROVISION_STATES4 = /* @__PURE__ */ new Set([
11453
+ "starting",
11454
+ "pending",
11455
+ "scanned",
11456
+ "needs_verification",
11457
+ "connecting",
11458
+ "connected",
11459
+ "expired",
11460
+ "failed",
11461
+ "cancelled"
11462
+ ]);
11463
+ function isRecord7(value) {
11464
+ return value !== null && typeof value === "object" && !Array.isArray(value);
11465
+ }
11466
+ function string(value, fallback = "") {
11467
+ return typeof value === "string" && value.trim() ? value.trim() : fallback;
11468
+ }
11469
+ function timestamp6(value) {
11470
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
11471
+ }
11472
+ function normalizeTestMessage5(value) {
11473
+ if (!isRecord7(value)) return null;
11474
+ if (value.sent === true) return { sent: true };
11475
+ if (value.sent !== false) return null;
11476
+ const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
11477
+ return { sent: false, code };
11478
+ }
11479
+ function unwrapRpcResult9(result) {
11480
+ if (!isRecord7(result) || typeof result.ok !== "boolean") {
11481
+ throw new Error("\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
11482
+ }
11483
+ if (!result.ok) {
11484
+ const error = new Error(string(result.error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25"));
11485
+ error.code = string(result.error?.code, "WEIXIN_RPC_ERROR");
11486
+ throw error;
11487
+ }
11488
+ return result.value;
11489
+ }
11490
+ function safeQrSource5(value) {
11491
+ return typeof value === "string" && /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
11492
+ }
11493
+ function safeVerificationUrl(value) {
11494
+ if (typeof value !== "string") return void 0;
11495
+ try {
11496
+ const url = new URL(value);
11497
+ const host = url.hostname.toLowerCase();
11498
+ return url.protocol === "https:" && (host === "weixin.qq.com" || host.endsWith(".weixin.qq.com") || host === "wechat.com" || host.endsWith(".wechat.com")) ? url.toString() : void 0;
11499
+ } catch {
11500
+ return void 0;
11501
+ }
11502
+ }
11503
+ function normalizeProvisioning5(value) {
11504
+ if (!isRecord7(value) || !string(value.attemptId)) {
11505
+ throw new Error("\u5FAE\u4FE1\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
11506
+ }
11507
+ const status = PROVISION_STATES4.has(value.status) ? value.status : "failed";
11508
+ const result = {
11509
+ attemptId: string(value.attemptId),
11510
+ status,
11511
+ expiresAt: timestamp6(value.expiresAt) ?? Date.now(),
11512
+ pollIntervalMs: Math.min(5e3, Math.max(500, Number(value.pollIntervalMs) || 1e3)),
11513
+ verificationRequired: value.verificationRequired === true || status === "needs_verification"
11514
+ };
11515
+ const verificationUrl = safeVerificationUrl(value.verificationUrl);
11516
+ const qrCodeDataUrl = safeQrSource5(value.qrCodeDataUrl);
11517
+ if (verificationUrl) result.verificationUrl = verificationUrl;
11518
+ if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
11519
+ if (string(value.botId)) result.botId = string(value.botId);
11520
+ if (value.alreadyConnected === true) result.alreadyConnected = true;
11521
+ if (isRecord7(value.error)) {
11522
+ result.error = {
11523
+ code: string(value.error.code, "WEIXIN_PROVISION_FAILED"),
11524
+ message: string(value.error.message, "\u5FAE\u4FE1\u7ED1\u5B9A\u6CA1\u6709\u5B8C\u6210")
11525
+ };
11526
+ }
11527
+ return result;
11528
+ }
11529
+ function normalizeBot6(value) {
11530
+ if (!isRecord7(value) || !string(value.botId) || !isRecord7(value.bot)) return null;
11531
+ const state = ACCOUNT_STATES6.has(value.state) ? value.state : "error";
11532
+ const connected = value.connected === true;
11533
+ return {
11534
+ botId: string(value.botId),
11535
+ state: connected ? "connected" : state,
11536
+ connected,
11537
+ configured: value.configured === true,
11538
+ workspace: string(value.workspace).slice(0, 4096),
11539
+ model: normalizeModelSelection(value.model),
11540
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
11541
+ contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
11542
+ ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
11543
+ bot: {
11544
+ name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
11545
+ accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
11546
+ },
11547
+ health: {
11548
+ status: string(value.health?.status, connected ? "healthy" : "offline"),
11549
+ summary: string(value.health?.summary, connected ? "\u5FAE\u4FE1\u8FDE\u63A5\u6B63\u5E38" : "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA"),
11550
+ lastCheckedAt: timestamp6(value.health?.lastCheckedAt)
11551
+ },
11552
+ stats: {
11553
+ messagesReceived: Math.max(0, Number(value.stats?.messagesReceived) || 0),
11554
+ messagesReplied: Math.max(0, Number(value.stats?.messagesReplied) || 0)
11555
+ },
11556
+ lastMessageError: normalizeLastMessageError(value.lastMessageError),
11557
+ error: isRecord7(value.error) ? {
11558
+ code: string(value.error.code, "WEIXIN_ACCOUNT_ERROR"),
11559
+ message: string(value.error.message, "\u5FAE\u4FE1\u8FDE\u63A5\u672A\u5C31\u7EEA")
11560
+ } : null
11561
+ };
11562
+ }
11563
+ function normalizeSnapshot8(value) {
11564
+ if (!isRecord7(value) || !Array.isArray(value.bots)) {
11565
+ throw new Error("\u5FAE\u4FE1\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u8D26\u53F7\u5217\u8868");
11566
+ }
11567
+ const bots = value.bots.map(normalizeBot6).filter(Boolean);
11568
+ return {
11569
+ schemaVersion: Number(value.schemaVersion) || 1,
11570
+ revision: Number(value.revision) || 0,
11571
+ state: string(value.state, "offline"),
11572
+ bots,
11573
+ totals: {
11574
+ configured: bots.length,
11575
+ connected: bots.filter((bot) => bot.connected).length
11576
+ },
11577
+ provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
11578
+ testMessage: normalizeTestMessage5(value.testMessage),
11579
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
11580
+ modelCatalog: normalizeModelCatalog(value.modelCatalog)
11581
+ };
11582
+ }
11583
+ function presentError9(error) {
11584
+ return {
11585
+ code: string(error?.code, "WEIXIN_ERROR"),
11586
+ message: string(error?.message, "\u5FAE\u4FE1\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
11587
+ };
11588
+ }
11589
+ function formatRemaining5(milliseconds) {
11590
+ const seconds = Math.max(0, Math.ceil(milliseconds / 1e3));
11591
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
11592
+ }
11593
+
11594
+ // plugin-src/client/channels/weixin/index.js
11595
+ var Button14 = React21.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
11596
+ return h2("button", {
11597
+ ...props,
11598
+ ref,
11599
+ type: "button",
11600
+ className: `dxw-button ${className}`.trim(),
11601
+ "data-kind": kind
11602
+ }, children);
11603
+ });
11604
+ function Heading6({ totals, adding, busy, onAdd, addButtonRef }) {
11605
+ return h2(
11606
+ "div",
11607
+ { className: "dxw-heading" },
11608
+ h2(
11609
+ "div",
11610
+ { className: "dxw-tools" },
11611
+ h2(Button14, {
11612
+ kind: "primary",
11613
+ className: "dim-scanButton",
11614
+ onClick: onAdd,
11615
+ disabled: adding || busy,
11616
+ ref: addButtonRef,
11617
+ "aria-label": "\u626B\u7801\u63A5\u5165\u5FAE\u4FE1\u673A\u5668\u4EBA"
11618
+ }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"),
11619
+ totals.configured > 0 ? h2(
11620
+ "div",
10545
11621
  { className: "dxw-badge dim-onlineBadge" },
10546
11622
  h2("span", null, `${totals.connected} / ${totals.configured} \u5728\u7EBF`)
10547
11623
  ) : null
10548
11624
  )
10549
11625
  );
10550
11626
  }
10551
- function LoadingView5() {
11627
+ function LoadingView6() {
10552
11628
  return h2(
10553
11629
  "div",
10554
11630
  { className: "dxw-card dxw-loading dim-surfaceCard dim-loadingView", "aria-busy": "true" },
@@ -10556,7 +11632,7 @@ function LoadingView5() {
10556
11632
  h2("span", null, "\u6B63\u5728\u8BFB\u53D6\u5FAE\u4FE1\u8FDE\u63A5\u72B6\u6001\u2026")
10557
11633
  );
10558
11634
  }
10559
- function EmptyView5({ onStart, busy }) {
11635
+ function EmptyView6({ onStart, busy }) {
10560
11636
  return h2(
10561
11637
  "div",
10562
11638
  { className: "dxw-card dim-surfaceCard" },
@@ -10578,7 +11654,7 @@ function EmptyView5({ onStart, busy }) {
10578
11654
  "div",
10579
11655
  { className: "dxw-actions dim-viewActions" },
10580
11656
  h2(
10581
- Button12,
11657
+ Button14,
10582
11658
  { kind: "primary", onClick: onStart, disabled: busy },
10583
11659
  busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801"
10584
11660
  )
@@ -10589,14 +11665,14 @@ function EmptyView5({ onStart, busy }) {
10589
11665
  );
10590
11666
  }
10591
11667
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10592
- const [imageFailed, setImageFailed] = React20.useState(false);
11668
+ const [imageFailed, setImageFailed] = React21.useState(false);
10593
11669
  const source = safeQrSource5(provision.qrCodeDataUrl);
10594
11670
  const href = safeVerificationUrl(provision.verificationUrl);
10595
11671
  const remaining = Math.max(0, provision.expiresAt - now);
10596
11672
  const expired = remaining === 0 || provision.status === "expired";
10597
11673
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
10598
11674
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
10599
- React20.useEffect(() => setImageFailed(false), [source]);
11675
+ React21.useEffect(() => setImageFailed(false), [source]);
10600
11676
  return h2(
10601
11677
  "div",
10602
11678
  { className: "dxw-card dim-surfaceCard" },
@@ -10648,24 +11724,24 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
10648
11724
  h2(
10649
11725
  "div",
10650
11726
  { className: "dxw-actions dim-viewActions" },
10651
- expired ? h2(Button12, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null,
11727
+ expired ? h2(Button14, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null,
10652
11728
  href ? h2("a", {
10653
11729
  className: "dxw-button",
10654
11730
  href,
10655
11731
  target: "_blank",
10656
11732
  rel: "noopener noreferrer"
10657
11733
  }, "\u6253\u5F00\u5907\u7528\u94FE\u63A5") : null,
10658
- !expired ? h2(Button12, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
10659
- h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
11734
+ !expired ? h2(Button14, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
11735
+ h2(Button14, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
10660
11736
  )
10661
11737
  )
10662
11738
  )
10663
11739
  );
10664
11740
  }
10665
11741
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
10666
- const [code, setCode] = React20.useState("");
11742
+ const [code, setCode] = React21.useState("");
10667
11743
  const valid = /^\d{4,8}$/.test(code);
10668
- React20.useEffect(() => setCode(""), [provision.attemptId]);
11744
+ React21.useEffect(() => setCode(""), [provision.attemptId]);
10669
11745
  return h2(
10670
11746
  "div",
10671
11747
  { className: "dxw-card dim-surfaceCard" },
@@ -10706,7 +11782,7 @@ function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
10706
11782
  disabled: !valid || busy
10707
11783
  }, busy ? "\u6B63\u5728\u9A8C\u8BC1\u2026" : "\u7EE7\u7EED\u8FDE\u63A5")
10708
11784
  ),
10709
- h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A")
11785
+ h2(Button14, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A")
10710
11786
  )
10711
11787
  );
10712
11788
  }
@@ -10720,7 +11796,7 @@ function ProgressPanel2({ scanned, onCancel, busy }) {
10720
11796
  onCancel ? h2(
10721
11797
  "div",
10722
11798
  { className: "dxw-actions dim-viewActions", style: { justifyContent: "center", marginTop: 14 } },
10723
- h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
11799
+ h2(Button14, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
10724
11800
  ) : null
10725
11801
  );
10726
11802
  }
@@ -10738,25 +11814,25 @@ function ProvisionError3({ provision, busy, onRetry, onClose }) {
10738
11814
  h2(
10739
11815
  "div",
10740
11816
  { className: "dxw-actions dim-viewActions" },
10741
- h2(Button12, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
10742
- h2(Button12, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
11817
+ h2(Button14, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
11818
+ h2(Button14, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
10743
11819
  )
10744
11820
  )
10745
11821
  );
10746
11822
  }
10747
- function checkedTime5(timestamp7) {
10748
- if (!timestamp7) return "\u5C1A\u672A\u68C0\u67E5";
11823
+ function checkedTime6(timestamp8) {
11824
+ if (!timestamp8) return "\u5C1A\u672A\u68C0\u67E5";
10749
11825
  try {
10750
11826
  return new Intl.DateTimeFormat("zh-CN", {
10751
11827
  hour: "2-digit",
10752
11828
  minute: "2-digit",
10753
11829
  second: "2-digit"
10754
- }).format(new Date(timestamp7));
11830
+ }).format(new Date(timestamp8));
10755
11831
  } catch {
10756
11832
  return "\u521A\u521A";
10757
11833
  }
10758
11834
  }
10759
- function AccountCard4({
11835
+ function AccountCard5({
10760
11836
  account,
10761
11837
  busy,
10762
11838
  feedback,
@@ -10810,7 +11886,7 @@ function AccountCard4({
10810
11886
  tone,
10811
11887
  stateLabel: account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA",
10812
11888
  lastCheckedAt: account.health.lastCheckedAt,
10813
- formatCheckedTime: checkedTime5
11889
+ formatCheckedTime: checkedTime6
10814
11890
  }),
10815
11891
  h2(BotSettingsButton, {
10816
11892
  channel: "weixin",
@@ -10853,11 +11929,11 @@ function AccountCard4({
10853
11929
  "div",
10854
11930
  { className: "dxw-actions dim-cardActions" },
10855
11931
  h2(
10856
- Button12,
11932
+ Button14,
10857
11933
  { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
10858
11934
  busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
10859
11935
  ),
10860
- h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
11936
+ h2(Button14, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
10861
11937
  ),
10862
11938
  summary2 ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary2) : null,
10863
11939
  account.lastMessageError ? h2(LastMessageErrorSummary, {
@@ -10881,9 +11957,9 @@ function AccountCard4({
10881
11957
  h2(
10882
11958
  "div",
10883
11959
  { className: "dxw-actions dim-viewActions" },
10884
- h2(Button12, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"),
11960
+ h2(Button14, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"),
10885
11961
  h2(
10886
- Button12,
11962
+ Button14,
10887
11963
  { kind: "danger", onClick: onConfirmRemove, disabled: busy === "delete" },
10888
11964
  busy === "delete" ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664"
10889
11965
  )
@@ -10903,7 +11979,7 @@ function AccountList2(props) {
10903
11979
  h2("ul", { className: "dxw-list dim-botList" }, props.bots.map((account) => h2(
10904
11980
  "li",
10905
11981
  { key: account.botId },
10906
- h2(AccountCard4, {
11982
+ h2(AccountCard5, {
10907
11983
  account,
10908
11984
  busy: props.busyByBot[account.botId],
10909
11985
  feedback: props.feedbackByBot[account.botId],
@@ -10931,7 +12007,7 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
10931
12007
  };
10932
12008
  }
10933
12009
  function WeixinSettingsTab({ rpcCall }) {
10934
- const [model, setModel] = React20.useState({
12010
+ const [model, setModel] = React21.useState({
10935
12011
  phase: "loading",
10936
12012
  bots: [],
10937
12013
  totals: EMPTY_TOTALS3,
@@ -10940,33 +12016,33 @@ function WeixinSettingsTab({ rpcCall }) {
10940
12016
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
10941
12017
  modelCatalog: EMPTY_MODEL_CATALOG
10942
12018
  });
10943
- const [provision, setProvision] = React20.useState(null);
10944
- const [busy, setBusy] = React20.useState(false);
10945
- const [busyByBot, setBusyByBot] = React20.useState({});
10946
- const [feedbackByBot, setFeedbackByBot] = React20.useState({});
10947
- const [removeTarget, setRemoveTarget] = React20.useState(null);
10948
- const [notice, setNotice] = React20.useState("");
10949
- const [now, setNow] = React20.useState(() => Date.now());
10950
- const addButtonRef = React20.useRef(null);
10951
- const mountedRef = React20.useRef(true);
12019
+ const [provision, setProvision] = React21.useState(null);
12020
+ const [busy, setBusy] = React21.useState(false);
12021
+ const [busyByBot, setBusyByBot] = React21.useState({});
12022
+ const [feedbackByBot, setFeedbackByBot] = React21.useState({});
12023
+ const [removeTarget, setRemoveTarget] = React21.useState(null);
12024
+ const [notice, setNotice] = React21.useState("");
12025
+ const [now, setNow] = React21.useState(() => Date.now());
12026
+ const addButtonRef = React21.useRef(null);
12027
+ const mountedRef = React21.useRef(true);
10952
12028
  const workspaceFence = useWorkspaceSnapshotFence();
10953
12029
  const scheduleAnimationFrame = useAnimationFrameScheduler();
10954
- React20.useEffect(() => {
12030
+ React21.useEffect(() => {
10955
12031
  mountedRef.current = true;
10956
12032
  return () => {
10957
12033
  mountedRef.current = false;
10958
12034
  };
10959
12035
  }, []);
10960
- const announce = React20.useCallback((value) => {
12036
+ const announce = React21.useCallback((value) => {
10961
12037
  setNotice("");
10962
12038
  scheduleAnimationFrame(() => {
10963
12039
  if (value) setNotice(value);
10964
12040
  }, "announcement");
10965
12041
  }, [scheduleAnimationFrame]);
10966
- const invoke = React20.useCallback(async (endpoint, payload = {}, signal) => {
10967
- return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
12042
+ const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
12043
+ return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
10968
12044
  }, [rpcCall]);
10969
- const loadStatus = React20.useCallback(async ({
12045
+ const loadStatus = React21.useCallback(async ({
10970
12046
  signal,
10971
12047
  silent = false,
10972
12048
  restoreProvisioning = false
@@ -10975,7 +12051,7 @@ function WeixinSettingsTab({ rpcCall }) {
10975
12051
  if (workspaceVersion === null || !mountedRef.current) return void 0;
10976
12052
  if (!silent) setModel((current) => ({ ...current, phase: "loading", error: null }));
10977
12053
  try {
10978
- const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.status, {}, signal));
12054
+ const snapshot = normalizeSnapshot8(await invoke(WEIXIN_ENDPOINTS.status, {}, signal));
10979
12055
  if (signal?.aborted || !mountedRef.current || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
10980
12056
  setModel({
10981
12057
  phase: "ready",
@@ -10999,17 +12075,17 @@ function WeixinSettingsTab({ rpcCall }) {
10999
12075
  setModel((current) => ({
11000
12076
  ...current,
11001
12077
  phase: silent && current.phase === "ready" ? "ready" : "error",
11002
- error: presentError8(error)
12078
+ error: presentError9(error)
11003
12079
  }));
11004
12080
  return void 0;
11005
12081
  }
11006
12082
  }, [invoke, workspaceFence]);
11007
- React20.useEffect(() => {
12083
+ React21.useEffect(() => {
11008
12084
  const controller = new AbortController();
11009
12085
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
11010
12086
  return () => controller.abort();
11011
12087
  }, [loadStatus]);
11012
- React20.useEffect(() => {
12088
+ React21.useEffect(() => {
11013
12089
  if (model.phase !== "ready") return void 0;
11014
12090
  const controller = new AbortController();
11015
12091
  let running = false;
@@ -11028,12 +12104,12 @@ function WeixinSettingsTab({ rpcCall }) {
11028
12104
  window.clearInterval(timer);
11029
12105
  };
11030
12106
  }, [loadStatus, model.phase]);
11031
- React20.useEffect(() => {
12107
+ React21.useEffect(() => {
11032
12108
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
11033
12109
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
11034
12110
  return () => window.clearInterval(timer);
11035
12111
  }, [provision?.attemptId, provision?.status]);
11036
- const startProvisioning = React20.useCallback(async ({ replace = false } = {}) => {
12112
+ const startProvisioning = React21.useCallback(async ({ replace = false } = {}) => {
11037
12113
  setBusy(true);
11038
12114
  try {
11039
12115
  if (replace && provision?.attemptId) {
@@ -11047,14 +12123,14 @@ function WeixinSettingsTab({ rpcCall }) {
11047
12123
  } catch (error) {
11048
12124
  setProvision({
11049
12125
  status: "failed",
11050
- error: presentError8(error),
12126
+ error: presentError9(error),
11051
12127
  ...provision?.attemptId ? { attemptId: provision.attemptId } : {}
11052
12128
  });
11053
12129
  } finally {
11054
12130
  setBusy(false);
11055
12131
  }
11056
12132
  }, [announce, invoke, provision?.attemptId]);
11057
- const cancelProvisioning = React20.useCallback(async () => {
12133
+ const cancelProvisioning = React21.useCallback(async () => {
11058
12134
  setBusy(true);
11059
12135
  try {
11060
12136
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -11064,12 +12140,12 @@ function WeixinSettingsTab({ rpcCall }) {
11064
12140
  announce("\u5DF2\u53D6\u6D88\u5FAE\u4FE1\u7ED1\u5B9A\u3002");
11065
12141
  scheduleAnimationFrame(() => addButtonRef.current?.focus(), "focus");
11066
12142
  } catch (error) {
11067
- setProvision((current) => ({ ...current, status: "failed", error: presentError8(error) }));
12143
+ setProvision((current) => ({ ...current, status: "failed", error: presentError9(error) }));
11068
12144
  } finally {
11069
12145
  setBusy(false);
11070
12146
  }
11071
12147
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
11072
- const submitVerification = React20.useCallback(async (verifyCode) => {
12148
+ const submitVerification = React21.useCallback(async (verifyCode) => {
11073
12149
  if (!provision?.attemptId) return;
11074
12150
  setBusy(true);
11075
12151
  try {
@@ -11080,12 +12156,12 @@ function WeixinSettingsTab({ rpcCall }) {
11080
12156
  setProvision((current) => ({ ...current, ...next }));
11081
12157
  announce("\u914D\u5BF9\u7801\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u7B49\u5F85\u5FAE\u4FE1\u786E\u8BA4\u3002");
11082
12158
  } catch (error) {
11083
- setProvision((current) => ({ ...current, status: "failed", error: presentError8(error) }));
12159
+ setProvision((current) => ({ ...current, status: "failed", error: presentError9(error) }));
11084
12160
  } finally {
11085
12161
  setBusy(false);
11086
12162
  }
11087
12163
  }, [announce, invoke, provision?.attemptId]);
11088
- React20.useEffect(() => {
12164
+ React21.useEffect(() => {
11089
12165
  const attemptId = provision?.attemptId;
11090
12166
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
11091
12167
  const controller = new AbortController();
@@ -11124,7 +12200,7 @@ function WeixinSettingsTab({ rpcCall }) {
11124
12200
  }
11125
12201
  } catch (error) {
11126
12202
  if (scheduler.disposed || error?.name === "AbortError") return;
11127
- setProvision((current) => current?.attemptId === attemptId ? { ...current, status: "failed", error: presentError8(error) } : current);
12203
+ setProvision((current) => current?.attemptId === attemptId ? { ...current, status: "failed", error: presentError9(error) } : current);
11128
12204
  }
11129
12205
  };
11130
12206
  scheduler.schedule(poll, provision.pollIntervalMs ?? 1e3);
@@ -11133,7 +12209,7 @@ function WeixinSettingsTab({ rpcCall }) {
11133
12209
  controller.abort();
11134
12210
  };
11135
12211
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
11136
- const setBotBusy = React20.useCallback((botId, value) => {
12212
+ const setBotBusy = React21.useCallback((botId, value) => {
11137
12213
  setBusyByBot((current) => {
11138
12214
  const next = { ...current };
11139
12215
  if (value) next[botId] = value;
@@ -11141,7 +12217,7 @@ function WeixinSettingsTab({ rpcCall }) {
11141
12217
  return next;
11142
12218
  });
11143
12219
  }, []);
11144
- const reconnect = React20.useCallback(async (account) => {
12220
+ const reconnect = React21.useCallback(async (account) => {
11145
12221
  const snapshotVersion = workspaceFence.beginMutation();
11146
12222
  setBotBusy(account.botId, "reconnect");
11147
12223
  setFeedbackByBot((current) => {
@@ -11150,7 +12226,7 @@ function WeixinSettingsTab({ rpcCall }) {
11150
12226
  return next;
11151
12227
  });
11152
12228
  try {
11153
- const snapshot = normalizeSnapshot7(await invoke(
12229
+ const snapshot = normalizeSnapshot8(await invoke(
11154
12230
  WEIXIN_ENDPOINTS.reconnectBot,
11155
12231
  { botId: account.botId, sendTest: true }
11156
12232
  ));
@@ -11193,11 +12269,11 @@ function WeixinSettingsTab({ rpcCall }) {
11193
12269
  setBotBusy(account.botId, null);
11194
12270
  }
11195
12271
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
11196
- const saveWorkspace = React20.useCallback(async (account, workspace) => {
12272
+ const saveWorkspace = React21.useCallback(async (account, workspace) => {
11197
12273
  const workspaceVersion = workspaceFence.beginMutation();
11198
12274
  setBotBusy(account.botId, "workspace");
11199
12275
  try {
11200
- const snapshot = normalizeSnapshot7(await invoke(
12276
+ const snapshot = normalizeSnapshot8(await invoke(
11201
12277
  WEIXIN_ENDPOINTS.setWorkspace,
11202
12278
  { botId: account.botId, workspace }
11203
12279
  ));
@@ -11218,11 +12294,11 @@ function WeixinSettingsTab({ rpcCall }) {
11218
12294
  if (mountedRef.current) setBotBusy(account.botId, null);
11219
12295
  }
11220
12296
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
11221
- const saveBotSetting = React20.useCallback(async (account, operation, endpoint, payload) => {
12297
+ const saveBotSetting = React21.useCallback(async (account, operation, endpoint, payload) => {
11222
12298
  const snapshotVersion = workspaceFence.beginMutation();
11223
12299
  setBotBusy(account.botId, operation);
11224
12300
  try {
11225
- const snapshot = normalizeSnapshot7(await invoke(
12301
+ const snapshot = normalizeSnapshot8(await invoke(
11226
12302
  endpoint,
11227
12303
  { botId: account.botId, ...payload }
11228
12304
  ));
@@ -11243,11 +12319,11 @@ function WeixinSettingsTab({ rpcCall }) {
11243
12319
  if (mountedRef.current) setBotBusy(account.botId, null);
11244
12320
  }
11245
12321
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
11246
- const remove = React20.useCallback(async (account) => {
12322
+ const remove = React21.useCallback(async (account) => {
11247
12323
  const snapshotVersion = workspaceFence.beginMutation();
11248
12324
  setBotBusy(account.botId, "delete");
11249
12325
  try {
11250
- const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.deleteBot, {
12326
+ const snapshot = normalizeSnapshot8(await invoke(WEIXIN_ENDPOINTS.deleteBot, {
11251
12327
  botId: account.botId,
11252
12328
  confirm: true
11253
12329
  }));
@@ -11264,7 +12340,7 @@ function WeixinSettingsTab({ rpcCall }) {
11264
12340
  setRemoveTarget(null);
11265
12341
  announce("\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002");
11266
12342
  } catch (error) {
11267
- announce(`\u79FB\u9664\u5931\u8D25\uFF1A${presentError8(error).message}`);
12343
+ announce(`\u79FB\u9664\u5931\u8D25\uFF1A${presentError9(error).message}`);
11268
12344
  } finally {
11269
12345
  const shouldRefresh = workspaceFence.endMutation();
11270
12346
  if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
@@ -11310,7 +12386,7 @@ function WeixinSettingsTab({ rpcCall }) {
11310
12386
  }, h2(
11311
12387
  "section",
11312
12388
  { className: "dxw-page dim-channelPage", "aria-label": "\u5FAE\u4FE1\u8BBE\u7F6E" },
11313
- h2(Heading5, {
12389
+ h2(Heading6, {
11314
12390
  totals: model.totals,
11315
12391
  adding: Boolean(provision),
11316
12392
  busy,
@@ -11319,7 +12395,7 @@ function WeixinSettingsTab({ rpcCall }) {
11319
12395
  }),
11320
12396
  h2("div", { className: "dxw-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
11321
12397
  model.error && model.phase === "ready" ? h2("div", { className: "dxw-statusNotice dim-statusNotice" }, `\u72B6\u6001\u5237\u65B0\u5931\u8D25\uFF1A${model.error.message}`) : null,
11322
- model.phase === "loading" ? h2(LoadingView5) : model.phase === "error" ? h2(
12398
+ model.phase === "loading" ? h2(LoadingView6) : model.phase === "error" ? h2(
11323
12399
  "div",
11324
12400
  { className: "dxw-card dim-surfaceCard" },
11325
12401
  h2(
@@ -11327,13 +12403,13 @@ function WeixinSettingsTab({ rpcCall }) {
11327
12403
  { className: "dxw-error dim-inlineError" },
11328
12404
  h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001"),
11329
12405
  h2("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"),
11330
- h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
12406
+ h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
11331
12407
  )
11332
12408
  ) : h2(
11333
- React20.Fragment,
12409
+ React21.Fragment,
11334
12410
  null,
11335
12411
  provisionView,
11336
- model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
12412
+ model.bots.length === 0 && !provision ? h2(EmptyView6, { onStart: () => void startProvisioning(), busy }) : null,
11337
12413
  model.bots.length > 0 ? h2(AccountList2, {
11338
12414
  bots: model.bots,
11339
12415
  busyByBot,
@@ -11369,7 +12445,7 @@ function WeixinSettingsTab({ rpcCall }) {
11369
12445
 
11370
12446
  // plugin-src/client/channels/weixin/styles.js
11371
12447
  var WEIXIN_STYLE_ID = "xmanrui-dsh-im-weixin-settings";
11372
- var CSS9 = String.raw`
12448
+ var CSS10 = String.raw`
11373
12449
  .dxw-page {
11374
12450
  --dxw-accent: #07c160;
11375
12451
  --dxw-accent-dark: #05994c;
@@ -11479,7 +12555,7 @@ function installWeixinStyles() {
11479
12555
  const style = document.createElement("style");
11480
12556
  style.dataset.plugin = "@xmanrui/dsh-im";
11481
12557
  style.dataset.pluginCss = WEIXIN_STYLE_ID;
11482
- style.textContent = CSS9;
12558
+ style.textContent = CSS10;
11483
12559
  document.head.appendChild(style);
11484
12560
  return () => style.remove();
11485
12561
  }
@@ -11502,28 +12578,28 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
11502
12578
  var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
11503
12579
  var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
11504
12580
  var QR_DATA_URL4 = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
11505
- function isRecord7(value) {
12581
+ function isRecord8(value) {
11506
12582
  return value !== null && typeof value === "object" && !Array.isArray(value);
11507
12583
  }
11508
- function text5(value, fallback, max = 240) {
12584
+ function text6(value, fallback, max = 240) {
11509
12585
  return typeof value === "string" && value.trim() ? value.trim().slice(0, max) : fallback;
11510
12586
  }
11511
- function id4(value) {
11512
- const result = text5(value, "", 128);
12587
+ function id5(value) {
12588
+ const result = text6(value, "", 128);
11513
12589
  return /^[a-z\d_-]+$/i.test(result) ? result : void 0;
11514
12590
  }
11515
- function timestamp6(value) {
12591
+ function timestamp7(value) {
11516
12592
  if (typeof value === "number" && Number.isFinite(value)) return value;
11517
12593
  const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
11518
12594
  return Number.isNaN(parsed) ? void 0 : parsed;
11519
12595
  }
11520
- function unwrapRpcResult9(result) {
11521
- if (!isRecord7(result) || typeof result.ok !== "boolean") {
12596
+ function unwrapRpcResult10(result) {
12597
+ if (!isRecord8(result) || typeof result.ok !== "boolean") {
11522
12598
  throw new Error("WhatsApp \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
11523
12599
  }
11524
12600
  if (!result.ok) {
11525
- const error = new Error(text5(result.error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25"));
11526
- error.code = text5(result.error?.code, "WHATSAPP_RPC_ERROR", 80);
12601
+ const error = new Error(text6(result.error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25"));
12602
+ error.code = text6(result.error?.code, "WHATSAPP_RPC_ERROR", 80);
11527
12603
  throw error;
11528
12604
  }
11529
12605
  return result.value;
@@ -11532,61 +12608,61 @@ function safeQrSource6(value) {
11532
12608
  return typeof value === "string" && value.length <= 2 * 1024 * 1024 && QR_DATA_URL4.test(value) ? value : void 0;
11533
12609
  }
11534
12610
  function normalizeProvisioning6(value, now = Date.now()) {
11535
- const source = isRecord7(value?.provisioning) ? value.provisioning : value;
11536
- if (!isRecord7(source)) throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u8FDB\u5EA6");
11537
- const attemptId = id4(source.attemptId);
12611
+ const source = isRecord8(value?.provisioning) ? value.provisioning : value;
12612
+ if (!isRecord8(source)) throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u8FDB\u5EA6");
12613
+ const attemptId = id5(source.attemptId);
11538
12614
  if (!attemptId) throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u626B\u7801\u4EFB\u52A1");
11539
- const reported = text5(source.status, "failed", 32);
12615
+ const reported = text6(source.status, "failed", 32);
11540
12616
  const result = {
11541
12617
  attemptId,
11542
12618
  status: PROVISION_STATES5.has(reported) ? reported : "failed",
11543
- expiresAt: timestamp6(source.expiresAt) ?? now + 6e4,
12619
+ expiresAt: timestamp7(source.expiresAt) ?? now + 6e4,
11544
12620
  pollIntervalMs: Math.min(5e3, Math.max(500, Number(source.pollIntervalMs) || 1e3)),
11545
12621
  qrRevision: Number.isSafeInteger(source.qrRevision) ? source.qrRevision : 0
11546
12622
  };
11547
12623
  const qrCodeDataUrl = safeQrSource6(source.qrCodeDataUrl);
11548
12624
  if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
11549
- if (id4(source.botId)) result.botId = id4(source.botId);
11550
- if (isRecord7(source.error)) result.error = {
11551
- code: text5(source.error.code, "WHATSAPP_PROVISION_FAILED", 80),
11552
- message: text5(source.error.message, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210")
12625
+ if (id5(source.botId)) result.botId = id5(source.botId);
12626
+ if (isRecord8(source.error)) result.error = {
12627
+ code: text6(source.error.code, "WHATSAPP_PROVISION_FAILED", 80),
12628
+ message: text6(source.error.message, "WhatsApp \u6CA1\u6709\u63A5\u5165\u5B8C\u6210")
11553
12629
  };
11554
12630
  return result;
11555
12631
  }
11556
- function normalizeBot6(value) {
11557
- if (!isRecord7(value) || !id4(value.botId)) return void 0;
12632
+ function normalizeBot7(value) {
12633
+ if (!isRecord8(value) || !id5(value.botId)) return void 0;
11558
12634
  const connected = value.connected === true;
11559
12635
  const state = BOT_STATES.has(value.state) ? value.state : "offline";
11560
12636
  return {
11561
- botId: id4(value.botId),
12637
+ botId: id5(value.botId),
11562
12638
  connected,
11563
12639
  state: connected ? "connected" : state,
11564
- workspace: text5(value.workspace, "", 4096),
12640
+ workspace: text6(value.workspace, "", 4096),
11565
12641
  model: normalizeModelSelection(value.model),
11566
12642
  agentPreset: normalizeAgentPresetId(value.agentPreset),
11567
12643
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
11568
12644
  ...Object.hasOwn(value, "accessPolicy") ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) } : {},
11569
12645
  bot: {
11570
- name: text5(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
11571
- idMasked: text5(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
12646
+ name: text6(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
12647
+ idMasked: text6(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
11572
12648
  },
11573
12649
  health: {
11574
- summary: text5(value.health?.summary, connected ? "WhatsApp Web \u5173\u8054\u8BBE\u5907\u8FD0\u884C\u6B63\u5E38" : "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
11575
- lastCheckedAt: timestamp6(value.health?.lastCheckedAt)
12650
+ summary: text6(value.health?.summary, connected ? "WhatsApp Web \u5173\u8054\u8BBE\u5907\u8FD0\u884C\u6B63\u5E38" : "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
12651
+ lastCheckedAt: timestamp7(value.health?.lastCheckedAt)
11576
12652
  },
11577
12653
  lastMessageError: normalizeLastMessageError(value.lastMessageError),
11578
- error: isRecord7(value.error) ? {
11579
- code: text5(value.error.code, "WHATSAPP_ACCOUNT_ERROR", 80),
11580
- message: text5(value.error.message, "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA")
12654
+ error: isRecord8(value.error) ? {
12655
+ code: text6(value.error.code, "WHATSAPP_ACCOUNT_ERROR", 80),
12656
+ message: text6(value.error.message, "WhatsApp \u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA")
11581
12657
  } : null
11582
12658
  };
11583
12659
  }
11584
- function normalizeSnapshot8(value) {
11585
- const source = isRecord7(value?.snapshot) ? value.snapshot : value;
11586
- if (!isRecord7(source) || !Array.isArray(source.bots)) {
12660
+ function normalizeSnapshot9(value) {
12661
+ const source = isRecord8(value?.snapshot) ? value.snapshot : value;
12662
+ if (!isRecord8(source) || !Array.isArray(source.bots)) {
11587
12663
  throw new Error("WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
11588
12664
  }
11589
- const bots = source.bots.map(normalizeBot6).filter(Boolean);
12665
+ const bots = source.bots.map(normalizeBot7).filter(Boolean);
11590
12666
  return {
11591
12667
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
11592
12668
  bots,
@@ -11596,10 +12672,10 @@ function normalizeSnapshot8(value) {
11596
12672
  modelCatalog: normalizeModelCatalog(source.modelCatalog)
11597
12673
  };
11598
12674
  }
11599
- function presentError9(error) {
12675
+ function presentError10(error) {
11600
12676
  return {
11601
- code: text5(error?.code, "WHATSAPP_ERROR", 80),
11602
- message: text5(error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
12677
+ code: text6(error?.code, "WHATSAPP_ERROR", 80),
12678
+ message: text6(error?.message, "WhatsApp \u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
11603
12679
  };
11604
12680
  }
11605
12681
  function formatRemaining6(milliseconds) {
@@ -11608,11 +12684,11 @@ function formatRemaining6(milliseconds) {
11608
12684
  }
11609
12685
 
11610
12686
  // plugin-src/client/channels/whatsapp/index.js
11611
- var React21 = __toESM(require("react"), 1);
12687
+ var React22 = __toESM(require("react"), 1);
11612
12688
 
11613
12689
  // plugin-src/client/channels/whatsapp/styles.js
11614
12690
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
11615
- var CSS10 = String.raw`
12691
+ var CSS11 = String.raw`
11616
12692
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
11617
12693
  .dwa-avatar { color: #fff; background: #25d366; }
11618
12694
  .dwa-avatar svg { display: block; }
@@ -11626,14 +12702,14 @@ function installWhatsappStyles() {
11626
12702
  const style = document.createElement("style");
11627
12703
  style.dataset.plugin = "@xmanrui/dsh-im";
11628
12704
  style.dataset.pluginCss = WHATSAPP_STYLE_ID;
11629
- style.textContent = CSS10;
12705
+ style.textContent = CSS11;
11630
12706
  document.head.appendChild(style);
11631
12707
  return () => style.remove();
11632
12708
  }
11633
12709
 
11634
12710
  // plugin-src/client/channels/whatsapp/index.js
11635
12711
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
11636
- var Button14 = React21.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
12712
+ var Button16 = React22.forwardRef(function Button17({ children, kind = "secondary", className = "", ...props }, ref) {
11637
12713
  return h2("button", {
11638
12714
  ...props,
11639
12715
  ref,
@@ -11642,7 +12718,7 @@ var Button14 = React21.forwardRef(function Button15({ children, kind = "secondar
11642
12718
  "data-kind": kind
11643
12719
  }, children);
11644
12720
  });
11645
- function checkedTime6(value) {
12721
+ function checkedTime7(value) {
11646
12722
  if (!value) return "\u5C1A\u672A\u68C0\u67E5";
11647
12723
  try {
11648
12724
  return new Intl.DateTimeFormat("zh-CN", {
@@ -11663,7 +12739,7 @@ function connectionTestNotice3(value) {
11663
12739
  }
11664
12740
  return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
11665
12741
  }
11666
- function Heading6({ totals, busy, onAdd, addButtonRef }) {
12742
+ function Heading7({ totals, busy, onAdd, addButtonRef }) {
11667
12743
  return h2(
11668
12744
  "div",
11669
12745
  { className: "ddt-heading" },
@@ -11673,7 +12749,7 @@ function Heading6({ totals, busy, onAdd, addButtonRef }) {
11673
12749
  h2(
11674
12750
  "div",
11675
12751
  { className: "dim-bindActions" },
11676
- h2(Button14, {
12752
+ h2(Button16, {
11677
12753
  kind: "primary",
11678
12754
  className: "dim-scanButton",
11679
12755
  onClick: onAdd,
@@ -11690,13 +12766,13 @@ function Heading6({ totals, busy, onAdd, addButtonRef }) {
11690
12766
  )
11691
12767
  );
11692
12768
  }
11693
- function LoadingView6() {
12769
+ function LoadingView7() {
11694
12770
  return h2("div", {
11695
12771
  className: "ddt-card ddt-loading dim-surfaceCard dim-loadingView",
11696
12772
  "aria-busy": "true"
11697
12773
  }, h2("div", { className: "ddt-spinner dim-spinner" }), "\u6B63\u5728\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001\u2026");
11698
12774
  }
11699
- function EmptyView6({ busy, onStart }) {
12775
+ function EmptyView7({ busy, onStart }) {
11700
12776
  return h2(
11701
12777
  "div",
11702
12778
  { className: "ddt-card dim-surfaceCard" },
@@ -11718,7 +12794,7 @@ function EmptyView6({ busy, onStart }) {
11718
12794
  "div",
11719
12795
  { className: "ddt-actions dim-viewActions" },
11720
12796
  h2(
11721
- Button14,
12797
+ Button16,
11722
12798
  { kind: "primary", onClick: onStart, disabled: busy },
11723
12799
  busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4E8C\u7EF4\u7801"
11724
12800
  )
@@ -11787,8 +12863,8 @@ function QrPanel5({ provision, now, busy, onRefresh, onCancel }) {
11787
12863
  h2(
11788
12864
  "div",
11789
12865
  { className: "ddt-actions dim-viewActions" },
11790
- h2(Button14, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
11791
- h2(Button14, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
12866
+ h2(Button16, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
12867
+ h2(Button16, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
11792
12868
  )
11793
12869
  )
11794
12870
  )
@@ -11824,13 +12900,13 @@ function ProvisionView3({ provision, busy, onRetry, onClose }) {
11824
12900
  h2(
11825
12901
  "div",
11826
12902
  { className: "ddt-actions dim-viewActions" },
11827
- h2(Button14, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
11828
- h2(Button14, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
12903
+ h2(Button16, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
12904
+ h2(Button16, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
11829
12905
  )
11830
12906
  )
11831
12907
  );
11832
12908
  }
11833
- function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
12909
+ function RemoveConfirmation6({ account, busy, onConfirm, onCancel }) {
11834
12910
  return h2(
11835
12911
  "div",
11836
12912
  { className: "ddt-confirm dim-confirm", role: "alertdialog" },
@@ -11839,9 +12915,9 @@ function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
11839
12915
  h2(
11840
12916
  "div",
11841
12917
  { className: "ddt-actions dim-viewActions" },
11842
- h2(Button14, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
12918
+ h2(Button16, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
11843
12919
  h2(
11844
- Button14,
12920
+ Button16,
11845
12921
  { kind: "danger", onClick: onConfirm, disabled: busy },
11846
12922
  busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165"
11847
12923
  )
@@ -11911,7 +12987,7 @@ function WhatsappAccountCard({
11911
12987
  tone,
11912
12988
  stateLabel: stateLabel2,
11913
12989
  lastCheckedAt: account.health.lastCheckedAt,
11914
- formatCheckedTime: checkedTime6
12990
+ formatCheckedTime: checkedTime7
11915
12991
  }),
11916
12992
  h2(BotSettingsButton, {
11917
12993
  channel: "whatsapp",
@@ -11952,12 +13028,12 @@ function WhatsappAccountCard({
11952
13028
  h2(
11953
13029
  "div",
11954
13030
  { className: "ddt-actions dim-cardActions" },
11955
- h2(Button14, {
13031
+ h2(Button16, {
11956
13032
  className: "dim-cardAction",
11957
13033
  onClick: onReconnect,
11958
13034
  disabled: Boolean(busy)
11959
13035
  }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
11960
- h2(Button14, {
13036
+ h2(Button16, {
11961
13037
  className: "dim-cardAction",
11962
13038
  kind: "danger",
11963
13039
  onClick: onRequestRemove,
@@ -11977,7 +13053,7 @@ function WhatsappAccountCard({
11977
13053
  )
11978
13054
  )
11979
13055
  ),
11980
- removing ? h2(RemoveConfirmation5, {
13056
+ removing ? h2(RemoveConfirmation6, {
11981
13057
  account,
11982
13058
  busy: busy === "delete",
11983
13059
  onConfirm: onConfirmRemove,
@@ -11986,7 +13062,7 @@ function WhatsappAccountCard({
11986
13062
  );
11987
13063
  }
11988
13064
  function WhatsappSettingsTab({ rpcCall }) {
11989
- const [model, setModel] = React21.useState({
13065
+ const [model, setModel] = React22.useState({
11990
13066
  phase: "loading",
11991
13067
  bots: [],
11992
13068
  totals: { configured: 0, connected: 0 },
@@ -11994,16 +13070,16 @@ function WhatsappSettingsTab({ rpcCall }) {
11994
13070
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
11995
13071
  modelCatalog: EMPTY_MODEL_CATALOG
11996
13072
  });
11997
- const [provision, setProvision] = React21.useState(null);
11998
- const [busy, setBusy] = React21.useState(false);
11999
- const [busyByBot, setBusyByBot] = React21.useState({});
12000
- const [testNoticeByBot, setTestNoticeByBot] = React21.useState({});
12001
- const [removeTarget, setRemoveTarget] = React21.useState(null);
12002
- const [now, setNow] = React21.useState(Date.now());
12003
- const mounted = React21.useRef(true);
13073
+ const [provision, setProvision] = React22.useState(null);
13074
+ const [busy, setBusy] = React22.useState(false);
13075
+ const [busyByBot, setBusyByBot] = React22.useState({});
13076
+ const [testNoticeByBot, setTestNoticeByBot] = React22.useState({});
13077
+ const [removeTarget, setRemoveTarget] = React22.useState(null);
13078
+ const [now, setNow] = React22.useState(Date.now());
13079
+ const mounted = React22.useRef(true);
12004
13080
  const workspaceFence = useWorkspaceSnapshotFence();
12005
- const addButtonRef = React21.useRef(null);
12006
- React21.useEffect(() => {
13081
+ const addButtonRef = React22.useRef(null);
13082
+ React22.useEffect(() => {
12007
13083
  const disposeDingtalk = installDingtalkStyles();
12008
13084
  const disposeWhatsapp = installWhatsappStyles();
12009
13085
  mounted.current = true;
@@ -12013,16 +13089,16 @@ function WhatsappSettingsTab({ rpcCall }) {
12013
13089
  disposeDingtalk();
12014
13090
  };
12015
13091
  }, []);
12016
- const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
13092
+ const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
12017
13093
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
12018
- return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
13094
+ return unwrapRpcResult10(await rpcCall(endpoint, payload, signal));
12019
13095
  }, [rpcCall]);
12020
- const loadStatus = React21.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
13096
+ const loadStatus = React22.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
12021
13097
  const workspaceVersion = workspaceFence.beginStatus();
12022
13098
  if (workspaceVersion === null) return void 0;
12023
13099
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
12024
13100
  try {
12025
- const snapshot = normalizeSnapshot8(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
13101
+ const snapshot = normalizeSnapshot9(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
12026
13102
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
12027
13103
  setModel({
12028
13104
  phase: "ready",
@@ -12042,18 +13118,18 @@ function WhatsappSettingsTab({ rpcCall }) {
12042
13118
  setModel((current) => ({
12043
13119
  ...current,
12044
13120
  phase: silent ? current.phase : "error",
12045
- error: presentError9(error)
13121
+ error: presentError10(error)
12046
13122
  }));
12047
13123
  }
12048
13124
  return void 0;
12049
13125
  }
12050
13126
  }, [invoke, workspaceFence]);
12051
- React21.useEffect(() => {
13127
+ React22.useEffect(() => {
12052
13128
  const controller = new AbortController();
12053
13129
  void loadStatus({ signal: controller.signal, restore: true });
12054
13130
  return () => controller.abort();
12055
13131
  }, [loadStatus]);
12056
- React21.useEffect(() => {
13132
+ React22.useEffect(() => {
12057
13133
  if (model.phase !== "ready") return void 0;
12058
13134
  const controller = new AbortController();
12059
13135
  const timer = window.setInterval(
@@ -12065,12 +13141,12 @@ function WhatsappSettingsTab({ rpcCall }) {
12065
13141
  window.clearInterval(timer);
12066
13142
  };
12067
13143
  }, [loadStatus, model.phase]);
12068
- React21.useEffect(() => {
13144
+ React22.useEffect(() => {
12069
13145
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
12070
13146
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
12071
13147
  return () => window.clearInterval(timer);
12072
13148
  }, [provision?.attemptId, provision?.status]);
12073
- const startProvisioning = React21.useCallback(async (replace = false) => {
13149
+ const startProvisioning = React22.useCallback(async (replace = false) => {
12074
13150
  setBusy(true);
12075
13151
  try {
12076
13152
  if (replace && provision?.attemptId) {
@@ -12083,12 +13159,12 @@ function WhatsappSettingsTab({ rpcCall }) {
12083
13159
  setNow(Date.now());
12084
13160
  setProvision({ ...started, durationMs: Math.max(1, started.expiresAt - Date.now()) });
12085
13161
  } catch (error) {
12086
- if (mounted.current) setProvision({ status: "failed", error: presentError9(error) });
13162
+ if (mounted.current) setProvision({ status: "failed", error: presentError10(error) });
12087
13163
  } finally {
12088
13164
  if (mounted.current) setBusy(false);
12089
13165
  }
12090
13166
  }, [invoke, provision?.attemptId]);
12091
- const closeProvision = React21.useCallback(async () => {
13167
+ const closeProvision = React22.useCallback(async () => {
12092
13168
  setBusy(true);
12093
13169
  try {
12094
13170
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -12099,7 +13175,7 @@ function WhatsappSettingsTab({ rpcCall }) {
12099
13175
  if (mounted.current) setBusy(false);
12100
13176
  }
12101
13177
  }, [invoke, provision?.attemptId, provision?.status]);
12102
- React21.useEffect(() => {
13178
+ React22.useEffect(() => {
12103
13179
  const attemptId = provision?.attemptId;
12104
13180
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
12105
13181
  const controller = new AbortController();
@@ -12129,7 +13205,7 @@ function WhatsappSettingsTab({ rpcCall }) {
12129
13205
  if (ACTIVE_STATES3.has(current.status)) schedule(current.pollIntervalMs);
12130
13206
  } catch (error) {
12131
13207
  if (!disposed && !controller.signal.aborted && mounted.current) {
12132
- setProvision({ status: "failed", error: presentError9(error) });
13208
+ setProvision({ status: "failed", error: presentError10(error) });
12133
13209
  }
12134
13210
  }
12135
13211
  };
@@ -12140,7 +13216,7 @@ function WhatsappSettingsTab({ rpcCall }) {
12140
13216
  if (timer) window.clearTimeout(timer);
12141
13217
  };
12142
13218
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
12143
- const botAction = React21.useCallback(async (account, operation, endpoint, payload) => {
13219
+ const botAction = React22.useCallback(async (account, operation, endpoint, payload) => {
12144
13220
  const snapshotVersion = workspaceFence.beginMutation();
12145
13221
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
12146
13222
  if (operation === "reconnect") {
@@ -12152,7 +13228,7 @@ function WhatsappSettingsTab({ rpcCall }) {
12152
13228
  }
12153
13229
  try {
12154
13230
  const value = await invoke(endpoint, payload);
12155
- const snapshot = normalizeSnapshot8(value);
13231
+ const snapshot = normalizeSnapshot9(value);
12156
13232
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
12157
13233
  setModel({
12158
13234
  phase: "ready",
@@ -12251,13 +13327,13 @@ function WhatsappSettingsTab({ rpcCall }) {
12251
13327
  className: "ddt-page dwa-page dim-channelPage",
12252
13328
  "aria-label": "WhatsApp \u8BBE\u7F6E"
12253
13329
  },
12254
- h2(Heading6, {
13330
+ h2(Heading7, {
12255
13331
  totals: model.totals,
12256
13332
  busy,
12257
13333
  onAdd: () => void startProvisioning(false),
12258
13334
  addButtonRef
12259
13335
  }),
12260
- model.phase === "loading" ? h2(LoadingView6) : model.phase === "error" ? h2(
13336
+ model.phase === "loading" ? h2(LoadingView7) : model.phase === "error" ? h2(
12261
13337
  "div",
12262
13338
  { className: "ddt-card dim-surfaceCard" },
12263
13339
  h2(
@@ -12265,10 +13341,10 @@ function WhatsappSettingsTab({ rpcCall }) {
12265
13341
  { className: "ddt-inlineError dim-inlineError" },
12266
13342
  h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001"),
12267
13343
  h2("p", null, model.error?.message),
12268
- h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
13344
+ h2(Button16, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
12269
13345
  )
12270
13346
  ) : h2(
12271
- React21.Fragment,
13347
+ React22.Fragment,
12272
13348
  null,
12273
13349
  provision?.status === "pending" ? h2(QrPanel5, {
12274
13350
  provision,
@@ -12281,17 +13357,17 @@ function WhatsappSettingsTab({ rpcCall }) {
12281
13357
  busy,
12282
13358
  onRetry: () => void startProvisioning(true),
12283
13359
  onClose: () => void closeProvision()
12284
- }) : model.bots.length === 0 ? h2(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null,
13360
+ }) : model.bots.length === 0 ? h2(EmptyView7, { busy, onStart: () => void startProvisioning(false) }) : null,
12285
13361
  botList
12286
13362
  )
12287
13363
  )));
12288
13364
  }
12289
13365
 
12290
13366
  // plugin-src/client/delivery-settings.js
12291
- var React24 = __toESM(require("react"), 1);
13367
+ var React25 = __toESM(require("react"), 1);
12292
13368
 
12293
13369
  // plugin-src/client/access-policy-settings.js
12294
- var React22 = __toESM(require("react"), 1);
13370
+ var React23 = __toESM(require("react"), 1);
12295
13371
  var ACCESS_POLICY_ENDPOINT = "bot.access-policy.set";
12296
13372
  var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
12297
13373
  weixin: Object.freeze({
@@ -12317,6 +13393,12 @@ var ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
12317
13393
  groupUserLabel: "\u7FA4\u6210\u5458\u7528\u6237 ID",
12318
13394
  groupPlaceholder: "\u586B\u5199 userid"
12319
13395
  }),
13396
+ wecomApp: Object.freeze({
13397
+ directUserLabel: "\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID",
13398
+ directPlaceholder: "\u586B\u5199 userid",
13399
+ groupUserLabel: "\u7FA4\u6210\u5458\u7528\u6237 ID",
13400
+ groupPlaceholder: "\u586B\u5199 userid"
13401
+ }),
12320
13402
  qq: Object.freeze({
12321
13403
  directUserLabel: "QQ User Open ID",
12322
13404
  directPlaceholder: "\u586B\u5199 user_openid",
@@ -12366,7 +13448,7 @@ function clonePolicy(policy) {
12366
13448
  group: cloneScope(policy.group)
12367
13449
  };
12368
13450
  }
12369
- function unwrapRpcResult10(result) {
13451
+ function unwrapRpcResult11(result) {
12370
13452
  if (result?.ok === true) return result.value;
12371
13453
  if (result?.ok === false) {
12372
13454
  const error = new Error(result.error?.message || "\u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
@@ -12393,8 +13475,8 @@ function ScenePolicyEditor({
12393
13475
  unsupported = false,
12394
13476
  onChange
12395
13477
  }) {
12396
- const ownerHelpId = React22.useId();
12397
- const emptyAllowlistHelpId = React22.useId();
13478
+ const ownerHelpId = React23.useId();
13479
+ const emptyAllowlistHelpId = React23.useId();
12398
13480
  const allowlist = policy.mode === "allowlist";
12399
13481
  const collectionKey = allowlist ? "users" : "commandPermissionOverrides";
12400
13482
  const branchKey = allowlist ? "allowlist" : "open";
@@ -12446,7 +13528,7 @@ function ScenePolicyEditor({
12446
13528
  h2("strong", null, "\u5F53\u524D\u6E20\u9053\u4E0D\u652F\u6301\u7FA4\u804A"),
12447
13529
  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")
12448
13530
  ) : h2(
12449
- React22.Fragment,
13531
+ React23.Fragment,
12450
13532
  null,
12451
13533
  h2(
12452
13534
  "div",
@@ -12587,16 +13669,16 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
12587
13669
  const definition = ACCESS_CHANNEL_DEFINITIONS[channel4];
12588
13670
  const initialPolicy = normalizeAccessPolicy(account?.accessPolicy);
12589
13671
  const initialKey = JSON.stringify(initialPolicy);
12590
- const [draft, setDraft] = React22.useState(() => clonePolicy(
13672
+ const [draft, setDraft] = React23.useState(() => clonePolicy(
12591
13673
  initialPolicy ?? DEFAULT_ACCESS_POLICY
12592
13674
  ));
12593
- const [saving, setSaving] = React22.useState(false);
12594
- const [feedback, setFeedback] = React22.useState(null);
12595
- React22.useEffect(() => {
13675
+ const [saving, setSaving] = React23.useState(false);
13676
+ const [feedback, setFeedback] = React23.useState(null);
13677
+ React23.useEffect(() => {
12596
13678
  const next = normalizeAccessPolicy(account?.accessPolicy);
12597
13679
  setDraft(clonePolicy(next ?? DEFAULT_ACCESS_POLICY));
12598
13680
  }, [account?.botId, initialKey]);
12599
- React22.useEffect(() => {
13681
+ React23.useEffect(() => {
12600
13682
  setFeedback(null);
12601
13683
  }, [account?.botId]);
12602
13684
  if (!definition) {
@@ -12613,7 +13695,7 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
12613
13695
  try {
12614
13696
  const policy = validateAccessPolicy(draft);
12615
13697
  if (typeof rpcCall !== "function") throw new Error("\u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12616
- const value = unwrapRpcResult10(await rpcCall(ACCESS_POLICY_ENDPOINT, {
13698
+ const value = unwrapRpcResult11(await rpcCall(ACCESS_POLICY_ENDPOINT, {
12617
13699
  botId: account.botId,
12618
13700
  policy
12619
13701
  }));
@@ -12687,7 +13769,7 @@ function AccessPolicySettingsPage({ channel: channel4, account, rpcCall, onSaved
12687
13769
  }
12688
13770
 
12689
13771
  // plugin-src/client/channels/feishu/group-settings.js
12690
- var React23 = __toESM(require("react"), 1);
13772
+ var React24 = __toESM(require("react"), 1);
12691
13773
  var GROUP_MESSAGE_PERMISSION_OPERATION2 = FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
12692
13774
  function SettingsButton({ children, kind = "secondary", className = "", ...props }) {
12693
13775
  return h2("button", {
@@ -12748,9 +13830,9 @@ function GroupResponseModeEditor({
12748
13830
  onAuthorize
12749
13831
  }) {
12750
13832
  const current = normalizeGroupResponseMode(value);
12751
- const [saving, setSaving] = React23.useState(false);
12752
- const [authorizing, setAuthorizing] = React23.useState(false);
12753
- const [error, setError] = React23.useState(null);
13833
+ const [saving, setSaving] = React24.useState(false);
13834
+ const [authorizing, setAuthorizing] = React24.useState(false);
13835
+ const [error, setError] = React24.useState(null);
12754
13836
  const change = async (event) => {
12755
13837
  const next = normalizeGroupResponseMode(event.target.value);
12756
13838
  if (next === current || saving || disabled) return;
@@ -12831,8 +13913,8 @@ function GroupResponseModeEditor({
12831
13913
  }
12832
13914
  function GroupTopicReplyEditor({ value = false, disabled = false, onSave }) {
12833
13915
  const current = value === true ? "on" : "off";
12834
- const [saving, setSaving] = React23.useState(false);
12835
- const [error, setError] = React23.useState(null);
13916
+ const [saving, setSaving] = React24.useState(false);
13917
+ const [error, setError] = React24.useState(null);
12836
13918
  const change = async (event) => {
12837
13919
  const next = event.target.value === "on";
12838
13920
  if ((next ? "on" : "off") === current || saving || disabled) return;
@@ -13019,14 +14101,14 @@ function PermissionFlow({ provision, now, busy, botName, onRetry, onCancel, onCl
13019
14101
  );
13020
14102
  }
13021
14103
  function FeishuGroupSettingsPage({ account, rpcCall }) {
13022
- const [settings, setSettings] = React23.useState(() => groupSettingsFrom(account));
13023
- const [refreshing, setRefreshing] = React23.useState(false);
13024
- const [refreshError, setRefreshError] = React23.useState(null);
13025
- const [provision, setProvision] = React23.useState(null);
13026
- const [provisionBusy, setProvisionBusy] = React23.useState(false);
13027
- const [now, setNow] = React23.useState(() => Date.now());
13028
- const mounted = React23.useRef(true);
13029
- React23.useEffect(() => {
14104
+ const [settings, setSettings] = React24.useState(() => groupSettingsFrom(account));
14105
+ const [refreshing, setRefreshing] = React24.useState(false);
14106
+ const [refreshError, setRefreshError] = React24.useState(null);
14107
+ const [provision, setProvision] = React24.useState(null);
14108
+ const [provisionBusy, setProvisionBusy] = React24.useState(false);
14109
+ const [now, setNow] = React24.useState(() => Date.now());
14110
+ const mounted = React24.useRef(true);
14111
+ React24.useEffect(() => {
13030
14112
  setSettings(groupSettingsFrom(account));
13031
14113
  }, [
13032
14114
  account.botId,
@@ -13034,22 +14116,22 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13034
14116
  account.groupTopicReply,
13035
14117
  account.groupMessagePermissionGranted
13036
14118
  ]);
13037
- React23.useEffect(() => {
14119
+ React24.useEffect(() => {
13038
14120
  mounted.current = true;
13039
14121
  return () => {
13040
14122
  mounted.current = false;
13041
14123
  };
13042
14124
  }, []);
13043
- const invoke = React23.useCallback(async (endpoint, payload = {}, signal) => {
14125
+ const invoke = React24.useCallback(async (endpoint, payload = {}, signal) => {
13044
14126
  if (typeof rpcCall !== "function") throw new Error("\u98DE\u4E66\u7FA4\u804A\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
13045
14127
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
13046
14128
  }, [rpcCall]);
13047
- const applySnapshot = React23.useCallback((value) => {
14129
+ const applySnapshot = React24.useCallback((value) => {
13048
14130
  const result = targetBotFromSnapshot(value, account.botId);
13049
14131
  if (mounted.current) setSettings(groupSettingsFrom(result.bot));
13050
14132
  return result;
13051
14133
  }, [account.botId]);
13052
- const loadSettings = React23.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
14134
+ const loadSettings = React24.useCallback(async ({ signal, restoreProvisioning = false } = {}) => {
13053
14135
  setRefreshing(true);
13054
14136
  setRefreshError(null);
13055
14137
  try {
@@ -13059,13 +14141,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13059
14141
  if (restoreProvisioning) {
13060
14142
  const active = result.snapshot.provisioning;
13061
14143
  if (active?.operation === GROUP_MESSAGE_PERMISSION_OPERATION2 && active.botId === account.botId) {
13062
- const timestamp7 = Date.now();
13063
- setNow(timestamp7);
14144
+ const timestamp8 = Date.now();
14145
+ setNow(timestamp8);
13064
14146
  setProvision({
13065
14147
  phase: active.submitted ? "connecting" : "qr",
13066
14148
  ...active,
13067
- durationMs: Math.max(1, active.expiresAt - timestamp7),
13068
- expired: !active.submitted && active.expiresAt <= timestamp7
14149
+ durationMs: Math.max(1, active.expiresAt - timestamp8),
14150
+ expired: !active.submitted && active.expiresAt <= timestamp8
13069
14151
  });
13070
14152
  }
13071
14153
  }
@@ -13078,16 +14160,16 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13078
14160
  if (!signal?.aborted && mounted.current) setRefreshing(false);
13079
14161
  }
13080
14162
  }, [account.botId, applySnapshot, invoke]);
13081
- React23.useEffect(() => {
14163
+ React24.useEffect(() => {
13082
14164
  const controller = new AbortController();
13083
14165
  void loadSettings({ signal: controller.signal, restoreProvisioning: true });
13084
14166
  return () => controller.abort();
13085
14167
  }, [loadSettings]);
13086
- const saveSetting = React23.useCallback(async (endpoint, payload) => {
14168
+ const saveSetting = React24.useCallback(async (endpoint, payload) => {
13087
14169
  const value = await invoke(endpoint, { botId: account.botId, ...payload });
13088
14170
  applySnapshot(value);
13089
14171
  }, [account.botId, applySnapshot, invoke]);
13090
- const startAuthorization = React23.useCallback(async ({ replace = false } = {}) => {
14172
+ const startAuthorization = React24.useCallback(async ({ replace = false } = {}) => {
13091
14173
  if (provisionBusy) return;
13092
14174
  const previousAttemptId = provision?.attemptId;
13093
14175
  setProvisionBusy(true);
@@ -13106,13 +14188,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13106
14188
  if (normalized.operation !== GROUP_MESSAGE_PERMISSION_OPERATION2 || normalized.botId !== account.botId) {
13107
14189
  throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801");
13108
14190
  }
13109
- const timestamp7 = Date.now();
14191
+ const timestamp8 = Date.now();
13110
14192
  if (!mounted.current) return;
13111
- setNow(timestamp7);
14193
+ setNow(timestamp8);
13112
14194
  setProvision({
13113
14195
  phase: normalized.submitted ? "connecting" : "qr",
13114
14196
  ...normalized,
13115
- durationMs: Math.max(1, normalized.expiresAt - timestamp7),
14197
+ durationMs: Math.max(1, normalized.expiresAt - timestamp8),
13116
14198
  expired: false
13117
14199
  });
13118
14200
  } catch (cause) {
@@ -13128,13 +14210,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13128
14210
  if (mounted.current) setProvisionBusy(false);
13129
14211
  }
13130
14212
  }, [account.botId, invoke, provision?.attemptId, provisionBusy]);
13131
- const finishAuthorization = React23.useCallback(async (signal) => {
14213
+ const finishAuthorization = React24.useCallback(async (signal) => {
13132
14214
  const bot = await loadSettings({ signal, restoreProvisioning: false });
13133
14215
  if (signal?.aborted || !mounted.current) return;
13134
14216
  if (!bot) throw new Error("\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001");
13135
14217
  setProvision(null);
13136
14218
  }, [loadSettings]);
13137
- React23.useEffect(() => {
14219
+ React24.useEffect(() => {
13138
14220
  if (!provision?.attemptId || !["qr", "connecting"].includes(provision.phase) || provision.expired) return void 0;
13139
14221
  const timerHost = globalThis.window ?? globalThis;
13140
14222
  const controller = new AbortController();
@@ -13178,13 +14260,13 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13178
14260
  timerHost.clearTimeout(timer);
13179
14261
  };
13180
14262
  }, [account.botId, finishAuthorization, invoke, provision]);
13181
- React23.useEffect(() => {
14263
+ React24.useEffect(() => {
13182
14264
  if (!provision?.attemptId || provision.phase !== "qr" || provision.expired) return void 0;
13183
14265
  const timerHost = globalThis.window ?? globalThis;
13184
14266
  const tick = () => {
13185
- const timestamp7 = Date.now();
13186
- setNow(timestamp7);
13187
- if (timestamp7 >= provision.expiresAt) {
14267
+ const timestamp8 = Date.now();
14268
+ setNow(timestamp8);
14269
+ if (timestamp8 >= provision.expiresAt) {
13188
14270
  setProvision((current) => current?.attemptId === provision.attemptId ? { ...current, expired: true } : current);
13189
14271
  }
13190
14272
  };
@@ -13192,7 +14274,7 @@ function FeishuGroupSettingsPage({ account, rpcCall }) {
13192
14274
  const timer = timerHost.setInterval(tick, 1e3);
13193
14275
  return () => timerHost.clearInterval(timer);
13194
14276
  }, [provision?.attemptId, provision?.expired, provision?.expiresAt, provision?.phase]);
13195
- const cancelAuthorization = React23.useCallback(async () => {
14277
+ const cancelAuthorization = React24.useCallback(async () => {
13196
14278
  if (!provision?.attemptId || provisionBusy) {
13197
14279
  setProvision(null);
13198
14280
  return;
@@ -13336,6 +14418,13 @@ var CHANNEL_DEFINITIONS = Object.freeze({
13336
14418
  group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "\u586B\u5199\u7FA4 chatid" }]
13337
14419
  }
13338
14420
  },
14421
+ wecomApp: {
14422
+ label: "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528",
14423
+ kinds: [{ value: "user", label: "\u79C1\u804A" }],
14424
+ fields: {
14425
+ user: [{ key: "chatId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID" }]
14426
+ }
14427
+ },
13339
14428
  qq: {
13340
14429
  label: "QQ",
13341
14430
  kinds: [{ value: "user", label: "\u5355\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
@@ -13380,10 +14469,10 @@ var CHANNEL_DEFINITIONS = Object.freeze({
13380
14469
  }
13381
14470
  }
13382
14471
  });
13383
- function presentError10(error, fallback) {
14472
+ function presentError11(error, fallback) {
13384
14473
  return error?.message || fallback;
13385
14474
  }
13386
- function unwrapRpcResult11(result) {
14475
+ function unwrapRpcResult12(result) {
13387
14476
  if (result?.ok === true) return result.value;
13388
14477
  if (result?.ok === false) {
13389
14478
  const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
@@ -13435,18 +14524,18 @@ function validSuggestions(definition, value) {
13435
14524
  });
13436
14525
  }
13437
14526
  function maskRouteValue(value) {
13438
- const text6 = String(value ?? "").trim();
13439
- if (!text6) return "";
13440
- const at = text6.indexOf("@");
14527
+ const text7 = String(value ?? "").trim();
14528
+ if (!text7) return "";
14529
+ const at = text7.indexOf("@");
13441
14530
  if (at > 0) {
13442
- const local = text6.slice(0, at);
13443
- const domain = text6.slice(at);
14531
+ const local = text7.slice(0, at);
14532
+ const domain = text7.slice(at);
13444
14533
  const visible = local.length <= 4 ? `${local.slice(0, 1)}\u2022\u2022${local.slice(-1)}` : `${local.slice(0, 3)}\u2026${local.slice(-2)}`;
13445
14534
  return `${visible}${domain}`;
13446
14535
  }
13447
- if (text6.length <= 6) return `${text6.slice(0, 2)}\u2026${text6.slice(-1)}`;
13448
- if (text6.length <= 12) return `${text6.slice(0, 3)}\u2026${text6.slice(-3)}`;
13449
- return `${text6.slice(0, 5)}\u2026${text6.slice(-4)}`;
14536
+ if (text7.length <= 6) return `${text7.slice(0, 2)}\u2026${text7.slice(-1)}`;
14537
+ if (text7.length <= 12) return `${text7.slice(0, 3)}\u2026${text7.slice(-3)}`;
14538
+ return `${text7.slice(0, 5)}\u2026${text7.slice(-4)}`;
13450
14539
  }
13451
14540
  function suggestionFallbackName(definition, suggestion) {
13452
14541
  const displayName = suggestionDisplayName(suggestion);
@@ -13501,13 +14590,13 @@ function TargetForm({
13501
14590
  }) {
13502
14591
  const editing = mode === "edit";
13503
14592
  const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
13504
- const [targetId, setTargetId] = React24.useState(initialValue?.targetId ?? "");
13505
- const [name2, setName] = React24.useState(initialValue?.name ?? "");
13506
- const [kind, setKind] = React24.useState(initialKind);
13507
- const [route, setRoute] = React24.useState(initialValue?.route ?? {});
13508
- const [error, setError] = React24.useState(null);
13509
- const [testing, setTesting] = React24.useState(false);
13510
- const [testState, setTestState] = React24.useState(null);
14593
+ const [targetId, setTargetId] = React25.useState(initialValue?.targetId ?? "");
14594
+ const [name2, setName] = React25.useState(initialValue?.name ?? "");
14595
+ const [kind, setKind] = React25.useState(initialKind);
14596
+ const [route, setRoute] = React25.useState(initialValue?.route ?? {});
14597
+ const [error, setError] = React25.useState(null);
14598
+ const [testing, setTesting] = React25.useState(false);
14599
+ const [testState, setTestState] = React25.useState(null);
13511
14600
  const currentTarget = () => {
13512
14601
  const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field) => {
13513
14602
  const raw = String(route[field.key] ?? "").trim();
@@ -13528,7 +14617,7 @@ function TargetForm({
13528
14617
  ...currentTarget()
13529
14618
  });
13530
14619
  } catch (caught) {
13531
- setError(presentError10(caught, "\u76EE\u6807\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002"));
14620
+ setError(presentError11(caught, "\u76EE\u6807\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002"));
13532
14621
  }
13533
14622
  };
13534
14623
  const testTarget = async () => {
@@ -13540,7 +14629,7 @@ function TargetForm({
13540
14629
  await onTest({ kind: target.kind, route: target.route });
13541
14630
  setTestState({ tone: "success", message: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002" });
13542
14631
  } catch (caught) {
13543
- setTestState({ tone: "error", message: presentError10(caught, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
14632
+ setTestState({ tone: "error", message: presentError11(caught, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
13544
14633
  } finally {
13545
14634
  setTesting(false);
13546
14635
  }
@@ -13724,7 +14813,7 @@ function TargetSuggestionPicker({
13724
14813
  suggestions.map((suggestion, index) => {
13725
14814
  const identity = routeIdentity(definition, suggestion);
13726
14815
  const added = configured.has(identity);
13727
- return React24.createElement("option", {
14816
+ return React25.createElement("option", {
13728
14817
  key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
13729
14818
  value: String(index),
13730
14819
  disabled: added
@@ -13741,11 +14830,11 @@ function TargetSuggestionPicker({
13741
14830
  );
13742
14831
  }
13743
14832
  function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
13744
- const [action, setAction] = React24.useState(null);
13745
- const [testState, setTestState] = React24.useState(null);
13746
- const [syncFeedback, setSyncFeedback] = React24.useState(null);
13747
- const [copyState, setCopyState] = React24.useState(null);
13748
- const [confirmDelete, setConfirmDelete] = React24.useState(false);
14833
+ const [action, setAction] = React25.useState(null);
14834
+ const [testState, setTestState] = React25.useState(null);
14835
+ const [syncFeedback, setSyncFeedback] = React25.useState(null);
14836
+ const [copyState, setCopyState] = React25.useState(null);
14837
+ const [confirmDelete, setConfirmDelete] = React25.useState(false);
13749
14838
  const sessionSync = target.sessionSync ?? { enabled: false, state: "unavailable" };
13750
14839
  const syncDescription = sessionSync.state === "active" ? "\u81EA\u52A8\u8DDF\u968F\u8BE5\u79C1\u804A\u7684\u5F53\u524D\u4F1A\u8BDD" : sessionSync.state === "waiting" ? "\u7B49\u5F85\u8BE5\u79C1\u804A\u5EFA\u7ACB\u65B0\u4F1A\u8BDD" : sessionSync.state === "unavailable" ? "\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807" : "\u5173\u95ED\u65F6\u4E0D\u53D1\u9001 DSH \u4F1A\u8BDD\u6D88\u606F";
13751
14840
  const testTarget = async () => {
@@ -13755,7 +14844,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
13755
14844
  await rpcCall(DELIVERY_ENDPOINTS.test, { botId, targetId: target.targetId });
13756
14845
  setTestState({ tone: "success", message: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002" });
13757
14846
  } catch (error) {
13758
- setTestState({ tone: "error", message: presentError10(error, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
14847
+ setTestState({ tone: "error", message: presentError11(error, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
13759
14848
  } finally {
13760
14849
  setAction(null);
13761
14850
  }
@@ -13766,7 +14855,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
13766
14855
  await copyText(JSON.stringify({ botId, targetId: target.targetId }));
13767
14856
  setCopyState("\u5DF2\u590D\u5236\u8C03\u7528\u53C2\u6570");
13768
14857
  } catch (error) {
13769
- setCopyState(presentError10(error, "\u590D\u5236\u5931\u8D25\u3002"));
14858
+ setCopyState(presentError11(error, "\u590D\u5236\u5931\u8D25\u3002"));
13770
14859
  }
13771
14860
  };
13772
14861
  const deleteTarget = async () => {
@@ -13775,7 +14864,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
13775
14864
  await rpcCall(DELIVERY_ENDPOINTS.delete, { botId, targetId: target.targetId });
13776
14865
  await onChanged();
13777
14866
  } catch (error) {
13778
- setTestState({ tone: "error", message: presentError10(error, "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
14867
+ setTestState({ tone: "error", message: presentError11(error, "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
13779
14868
  setAction(null);
13780
14869
  setConfirmDelete(false);
13781
14870
  }
@@ -13793,7 +14882,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
13793
14882
  } catch (error) {
13794
14883
  setSyncFeedback({
13795
14884
  tone: "error",
13796
- message: error?.code === "session-sync-unavailable" ? "\u4F1A\u8BDD\u540C\u6B65\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807\u3002" : presentError10(error, "\u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")
14885
+ message: error?.code === "session-sync-unavailable" ? "\u4F1A\u8BDD\u540C\u6B65\u4EC5\u652F\u6301\u5DF2\u7ECF\u804A\u8FC7\u3001\u5DF2\u5EFA\u7ACB\u5F53\u524D\u4F1A\u8BDD\u4E14\u4F7F\u7528\u5F53\u524D Host Harness \u7684\u79C1\u804A\u76EE\u6807\u3002" : presentError11(error, "\u4F1A\u8BDD\u540C\u6B65\u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002")
13797
14886
  });
13798
14887
  } finally {
13799
14888
  setAction(null);
@@ -13808,7 +14897,7 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
13808
14897
  h2(
13809
14898
  "div",
13810
14899
  { className: "dim-targetTitle" },
13811
- React24.createElement("strong", null, target.name || target.targetId),
14900
+ React25.createElement("strong", null, target.name || target.targetId),
13812
14901
  h2("span", null, kindLabel(definition, target.kind))
13813
14902
  ),
13814
14903
  h2("code", null, `targetId: ${target.targetId}`)
@@ -13894,26 +14983,26 @@ function DeliveryTargetSettingsPage({
13894
14983
  onBack
13895
14984
  }) {
13896
14985
  const definition = CHANNEL_DEFINITIONS[channel4];
13897
- const [activeTabId, setActiveTabId] = React24.useState(BOT_SETTINGS_TABS[0].id);
13898
- const [phase, setPhase] = React24.useState("loading");
13899
- const [targets, setTargets] = React24.useState([]);
13900
- const [suggestionPhase, setSuggestionPhase] = React24.useState("idle");
13901
- const [suggestions, setSuggestions] = React24.useState([]);
13902
- const [suggestionError, setSuggestionError] = React24.useState(null);
13903
- const [error, setError] = React24.useState(null);
13904
- const [editor, setEditor] = React24.useState(null);
13905
- const [saving, setSaving] = React24.useState(false);
13906
- const [botCopyState, setBotCopyState] = React24.useState(null);
13907
- const [accessPolicy, setAccessPolicy] = React24.useState(account.accessPolicy);
13908
- const mounted = React24.useRef(true);
13909
- React24.useEffect(() => {
14986
+ const [activeTabId, setActiveTabId] = React25.useState(BOT_SETTINGS_TABS[0].id);
14987
+ const [phase, setPhase] = React25.useState("loading");
14988
+ const [targets, setTargets] = React25.useState([]);
14989
+ const [suggestionPhase, setSuggestionPhase] = React25.useState("idle");
14990
+ const [suggestions, setSuggestions] = React25.useState([]);
14991
+ const [suggestionError, setSuggestionError] = React25.useState(null);
14992
+ const [error, setError] = React25.useState(null);
14993
+ const [editor, setEditor] = React25.useState(null);
14994
+ const [saving, setSaving] = React25.useState(false);
14995
+ const [botCopyState, setBotCopyState] = React25.useState(null);
14996
+ const [accessPolicy, setAccessPolicy] = React25.useState(account.accessPolicy);
14997
+ const mounted = React25.useRef(true);
14998
+ React25.useEffect(() => {
13910
14999
  setAccessPolicy(account.accessPolicy);
13911
15000
  }, [account.botId, account.accessPolicy]);
13912
- const invoke = React24.useCallback(async (endpoint, payload = {}, signal) => {
15001
+ const invoke = React25.useCallback(async (endpoint, payload = {}, signal) => {
13913
15002
  if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
13914
- return unwrapRpcResult11(await rpcCall(endpoint, payload, signal));
15003
+ return unwrapRpcResult12(await rpcCall(endpoint, payload, signal));
13915
15004
  }, [rpcCall]);
13916
- const loadTargets = React24.useCallback(async ({ signal, silent = false } = {}) => {
15005
+ const loadTargets = React25.useCallback(async ({ signal, silent = false } = {}) => {
13917
15006
  if (!silent) setPhase("loading");
13918
15007
  setError(null);
13919
15008
  try {
@@ -13923,11 +15012,11 @@ function DeliveryTargetSettingsPage({
13923
15012
  setPhase("ready");
13924
15013
  } catch (caught) {
13925
15014
  if (signal?.aborted || caught?.name === "AbortError" || !mounted.current) return;
13926
- setError(presentError10(caught, "\u65E0\u6CD5\u8BFB\u53D6\u6295\u9012\u76EE\u6807\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
15015
+ setError(presentError11(caught, "\u65E0\u6CD5\u8BFB\u53D6\u6295\u9012\u76EE\u6807\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
13927
15016
  setPhase("error");
13928
15017
  }
13929
15018
  }, [account.botId, invoke]);
13930
- const loadSuggestions = React24.useCallback(async () => {
15019
+ const loadSuggestions = React25.useCallback(async () => {
13931
15020
  setSuggestionPhase("loading");
13932
15021
  setSuggestionError(null);
13933
15022
  try {
@@ -13937,11 +15026,11 @@ function DeliveryTargetSettingsPage({
13937
15026
  setSuggestionPhase("ready");
13938
15027
  } catch (caught) {
13939
15028
  if (caught?.name === "AbortError" || !mounted.current) return;
13940
- setSuggestionError(presentError10(caught, "\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
15029
+ setSuggestionError(presentError11(caught, "\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
13941
15030
  setSuggestionPhase("error");
13942
15031
  }
13943
15032
  }, [account.botId, definition, invoke]);
13944
- React24.useEffect(() => {
15033
+ React25.useEffect(() => {
13945
15034
  mounted.current = true;
13946
15035
  const controller = new AbortController();
13947
15036
  void loadTargets({ signal: controller.signal });
@@ -14009,7 +15098,7 @@ function DeliveryTargetSettingsPage({
14009
15098
  await copyText(account.botId);
14010
15099
  setBotCopyState("\u5DF2\u590D\u5236 Bot ID");
14011
15100
  } catch (caught) {
14012
- setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002"));
15101
+ setBotCopyState(presentError11(caught, "\u590D\u5236\u5931\u8D25\u3002"));
14013
15102
  }
14014
15103
  };
14015
15104
  const settingsTabs = botSettingsTabsForChannel(channel4);
@@ -14063,7 +15152,7 @@ function DeliveryTargetSettingsPage({
14063
15152
  account,
14064
15153
  rpcCall: accessRpcCall
14065
15154
  }) : h2(
14066
- React24.Fragment,
15155
+ React25.Fragment,
14067
15156
  null,
14068
15157
  h2(
14069
15158
  "section",
@@ -14174,7 +15263,7 @@ function DeliveryTargetSettingsPage({
14174
15263
  }
14175
15264
 
14176
15265
  // plugin-src/client/global-settings.js
14177
- var React25 = __toESM(require("react"), 1);
15266
+ var React26 = __toESM(require("react"), 1);
14178
15267
 
14179
15268
  // src/channels/shared/inbound-ttl.mjs
14180
15269
  var DEFAULT_INBOUND_TTL_HOURS = 168;
@@ -14203,10 +15292,10 @@ var GLOBAL_SETTINGS_ENDPOINTS = Object.freeze({
14203
15292
  setTtl: "settings.inbound-ttl.set",
14204
15293
  sweep: "settings.inbound-ttl.sweep"
14205
15294
  });
14206
- function presentError11(error, fallback) {
15295
+ function presentError12(error, fallback) {
14207
15296
  return error?.message || fallback;
14208
15297
  }
14209
- function unwrapRpcResult12(result) {
15298
+ function unwrapRpcResult13(result) {
14210
15299
  if (result?.ok === true) return result.value;
14211
15300
  if (result?.ok === false) {
14212
15301
  const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
@@ -14236,29 +15325,29 @@ function GlobalButton({ children, kind = "secondary", className = "", ...props }
14236
15325
  }, children);
14237
15326
  }
14238
15327
  function GlobalSettingsPanel({ rpcCall }) {
14239
- const [phase, setPhase] = React25.useState("loading");
14240
- const [loadError, setLoadError] = React25.useState(null);
14241
- const [ttlInput, setTtlInput] = React25.useState("");
14242
- const [savedTtl, setSavedTtl] = React25.useState(null);
14243
- const [ttlError, setTtlError] = React25.useState(false);
14244
- const [saveError, setSaveError] = React25.useState(null);
14245
- const [saveSucceeded, setSaveSucceeded] = React25.useState(false);
14246
- const [isSaving, setIsSaving] = React25.useState(false);
14247
- const [sweepConfirming, setSweepConfirming] = React25.useState(false);
14248
- const [sweeping, setSweeping] = React25.useState(false);
14249
- const ttlErrorId = React25.useId();
14250
- const ttlHintsId = React25.useId();
14251
- const sweepTriggerId = React25.useId();
14252
- const sweepConfirmId = React25.useId();
14253
- const sweepConfirmTextId = React25.useId();
14254
- const sweepConfirmButtonId = React25.useId();
14255
- const mounted = React25.useRef(true);
14256
- const saving = React25.useRef(false);
14257
- const invoke = React25.useCallback(async (endpoint, payload = {}, signal) => {
15328
+ const [phase, setPhase] = React26.useState("loading");
15329
+ const [loadError, setLoadError] = React26.useState(null);
15330
+ const [ttlInput, setTtlInput] = React26.useState("");
15331
+ const [savedTtl, setSavedTtl] = React26.useState(null);
15332
+ const [ttlError, setTtlError] = React26.useState(false);
15333
+ const [saveError, setSaveError] = React26.useState(null);
15334
+ const [saveSucceeded, setSaveSucceeded] = React26.useState(false);
15335
+ const [isSaving, setIsSaving] = React26.useState(false);
15336
+ const [sweepConfirming, setSweepConfirming] = React26.useState(false);
15337
+ const [sweeping, setSweeping] = React26.useState(false);
15338
+ const ttlErrorId = React26.useId();
15339
+ const ttlHintsId = React26.useId();
15340
+ const sweepTriggerId = React26.useId();
15341
+ const sweepConfirmId = React26.useId();
15342
+ const sweepConfirmTextId = React26.useId();
15343
+ const sweepConfirmButtonId = React26.useId();
15344
+ const mounted = React26.useRef(true);
15345
+ const saving = React26.useRef(false);
15346
+ const invoke = React26.useCallback(async (endpoint, payload = {}, signal) => {
14258
15347
  if (typeof rpcCall !== "function") throw new Error("\u901A\u7528\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
14259
- return unwrapRpcResult12(await rpcCall(endpoint, payload, signal));
15348
+ return unwrapRpcResult13(await rpcCall(endpoint, payload, signal));
14260
15349
  }, [rpcCall]);
14261
- const loadSettings = React25.useCallback(async ({ signal } = {}) => {
15350
+ const loadSettings = React26.useCallback(async ({ signal } = {}) => {
14262
15351
  setPhase("loading");
14263
15352
  setLoadError(null);
14264
15353
  try {
@@ -14278,11 +15367,11 @@ function GlobalSettingsPanel({ rpcCall }) {
14278
15367
  setPhase("ready");
14279
15368
  } catch (caught) {
14280
15369
  if (signal?.aborted || caught?.name === "AbortError" || !mounted.current) return;
14281
- setLoadError(presentError11(caught, "\u65E0\u6CD5\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
15370
+ setLoadError(presentError12(caught, "\u65E0\u6CD5\u8BFB\u53D6\u901A\u7528\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
14282
15371
  setPhase("error");
14283
15372
  }
14284
15373
  }, [invoke]);
14285
- React25.useEffect(() => {
15374
+ React26.useEffect(() => {
14286
15375
  mounted.current = true;
14287
15376
  const controller = new AbortController();
14288
15377
  void loadSettings({ signal: controller.signal });
@@ -14291,7 +15380,7 @@ function GlobalSettingsPanel({ rpcCall }) {
14291
15380
  controller.abort();
14292
15381
  };
14293
15382
  }, [loadSettings]);
14294
- React25.useEffect(() => {
15383
+ React26.useEffect(() => {
14295
15384
  if (!sweepConfirming) return;
14296
15385
  globalThis.document?.getElementById(sweepConfirmButtonId)?.focus();
14297
15386
  }, [sweepConfirmButtonId, sweepConfirming]);
@@ -14322,7 +15411,7 @@ function GlobalSettingsPanel({ rpcCall }) {
14322
15411
  setPhase("ready");
14323
15412
  } catch (caught) {
14324
15413
  if (mounted.current) {
14325
- setSaveError(presentError11(caught, "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
15414
+ setSaveError(presentError12(caught, "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
14326
15415
  }
14327
15416
  } finally {
14328
15417
  saving.current = false;
@@ -14598,7 +15687,7 @@ function replacePageLocation(url, location = globalThis.location) {
14598
15687
 
14599
15688
  // plugin-src/client/styles.js
14600
15689
  var IM_STYLE_ID = "xmanrui-dsh-im-settings";
14601
- var CSS11 = String.raw`
15690
+ var CSS12 = String.raw`
14602
15691
  .dim-page {
14603
15692
  --dim-blue: var(--dsw-alias-state-business-primary, #3370ff);
14604
15693
  --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent);
@@ -14669,9 +15758,7 @@ var CSS11 = String.raw`
14669
15758
  .dim-generalSettingsAction:hover .dim-generalSettingsTooltip, .dim-generalSettingsButton:focus-visible + .dim-generalSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
14670
15759
  .dim-generalSettingsButton[aria-current="page"] + .dim-generalSettingsTooltip { opacity: 0; visibility: hidden; transform: translateY(-3px); }
14671
15760
  .dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
14672
- .dim-rail { max-height: 520px; display: grid; align-content: start; gap: 8px; overflow-y: auto; padding: 0 4px 1px 1px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-border-l2, #dfe1e5) transparent; }
14673
- .dim-rail::-webkit-scrollbar { width: 4px; }
14674
- .dim-rail::-webkit-scrollbar-thumb { border-radius: 99px; background: var(--dsw-alias-border-l2, #dfe1e5); }
15761
+ .dim-rail { display: grid; align-content: start; gap: 8px; padding: 0 4px 1px 1px; }
14675
15762
  .dim-channel { width: 100%; min-height: 48px; display: grid; grid-template-columns: 30px minmax(0, 1fr); align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--dsw-alias-border-l2, #eef0f3); border-radius: 14px; color: inherit; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 2px 8px rgb(31 35 41 / 3%); font: inherit; text-align: left; cursor: pointer; transition: border-color .16s ease, background .16s ease, box-shadow .16s ease; }
14676
15763
  .dim-channel:hover { border-color: color-mix(in srgb, var(--dim-blue) 25%, var(--dsw-alias-border-l2, #eef0f3)); background: color-mix(in srgb, var(--dim-blue) 2%, var(--dsw-alias-bg-layer-3, #fff)); box-shadow: 0 5px 16px rgb(31 35 41 / 5%); }
14677
15764
  .dim-channel[aria-selected="true"] { border-color: color-mix(in srgb, var(--dim-blue) 43%, var(--dsw-alias-border-l2, #dfe1e5)); color: var(--dim-blue); background: color-mix(in srgb, var(--dim-blue) 12%, var(--dsw-alias-bg-layer-3, #fff)); box-shadow: 0 3px 12px rgb(51 112 255 / 7%); }
@@ -14701,7 +15788,7 @@ var CSS11 = String.raw`
14701
15788
  .dim-channelCopy { min-width: 0; display: grid; }
14702
15789
  .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
14703
15790
  .dim-channelNote { overflow: hidden; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 10px; line-height: 13px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
14704
- .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-border-l1, #eef0f3); }
15791
+ .dim-divider { width: 1px; align-self: stretch; background: var(--dsw-alias-border-l1, #eef0f3); }
14705
15792
  .dim-panel { min-width: 0; container-type: inline-size; }
14706
15793
  .dim-loopbackRecovery { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 14px; padding: 14px 16px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 30%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 12px; color: var(--dsw-alias-label-primary, #1f2329); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 8%, var(--dsw-alias-bg-layer-1, #fff)); }
14707
15794
  .dim-loopbackRecoveryCopy { min-width: 0; }
@@ -14817,6 +15904,24 @@ var CSS11 = String.raw`
14817
15904
  .dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
14818
15905
  .dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; 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; }
14819
15906
  .dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
15907
+ .dim-panel .dim-modelSetting { display: block; padding: 8px 4px 4px; }
15908
+ .dim-modelSetting > .dim-presetHeader { padding: 0 6px 6px; }
15909
+ .dim-modelRow { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 40px; padding: 6px 10px; border: 0; border-radius: 10px; background: transparent; color: var(--dsw-alias-label-primary, #1f2329); font: inherit; font-size: 14px; line-height: 22px; text-align: left; cursor: pointer; }
15910
+ .dim-modelRowLabel { flex: none; }
15911
+ .dim-modelValue { flex: 1; min-width: 0; overflow: hidden; color: var(--dsw-alias-label-tertiary, #81858c); text-overflow: ellipsis; white-space: nowrap; text-align: right; }
15912
+ .dim-modelChevron { flex: none; color: var(--dsw-alias-label-tertiary, #81858c); }
15913
+ .dim-modelRow:hover:not(:disabled), .dim-modelOption:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eceef1); }
15914
+ .dim-modelRow:focus-visible, .dim-modelOption:focus-visible { outline: 2px solid var(--dsw-alias-border-l3, #1677ff); outline-offset: -2px; }
15915
+ .dim-modelRow:disabled, .dim-modelOption:disabled { opacity: .55; cursor: default; }
15916
+ /* In-flow lists avoid clipping by collapsed-card and settings scroll containers. */
15917
+ .dim-modelMenu { max-height: 280px; overflow-y: auto; margin: 4px; padding: 4px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 12px; background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3, #fff)); color: var(--dsw-alias-label-primary, #1f2329); scrollbar-width: thin; }
15918
+ .dim-modelGroupTitle { padding: 7px 8px 3px; color: var(--dsw-alias-label-tertiary, #81858c); font-size: 12px; line-height: 18px; font-weight: 500; }
15919
+ .dim-modelOption { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 38px; padding: 6px 8px; border: 0; border-radius: 10px; background: transparent; color: inherit; text-align: left; font: inherit; cursor: pointer; }
15920
+ .dim-modelOptionCopy { display: flex; flex: 1; min-width: 0; flex-direction: column; gap: 2px; }
15921
+ .dim-modelOptionName { font-size: 14px; line-height: 20px; font-weight: 500; overflow-wrap: anywhere; }
15922
+ .dim-modelDescription { color: var(--dsw-alias-label-tertiary, #81858c); font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
15923
+ .dim-modelCheck { flex: 0 0 18px; text-align: center; }
15924
+ .dim-modelHint { margin: 4px 10px 6px; color: var(--dsw-alias-label-tertiary, #81858c); font-size: 11px; line-height: 17px; }
14820
15925
  .dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 3; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
14821
15926
  .dim-contextEntry { width: 100%; min-height: 40px; display: grid; grid-template-columns: 16px minmax(0, 1fr) max-content 16px; align-items: center; gap: 9px; margin: 10px 0; padding: 8px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font: inherit; font-size: 13px; line-height: 20px; text-align: left; cursor: pointer; }
14822
15927
  .dim-contextEntry:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
@@ -15004,7 +16109,7 @@ var CSS11 = String.raw`
15004
16109
  .dim-feishuGroupSettings { min-width: 0; display: grid; gap: 14px; }
15005
16110
  .dim-feishuGroupControls { min-width: 0; display: grid; gap: 12px; }
15006
16111
  .dim-feishuGroupControl { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
15007
- .dim-feishuGroupControlHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
16112
+ .dim-feishuGroupControlHeader { position: relative; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
15008
16113
  .dim-feishuGroupControlHeader h3 { min-width: 0; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 21px; font-weight: 650; }
15009
16114
  .dim-feishuGroupControlStatus { flex: none; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
15010
16115
  .dim-feishuGroupSelect { min-width: 0; width: 100%; height: 38px; padding: 0 34px 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 38px; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
@@ -15206,9 +16311,8 @@ var CSS11 = String.raw`
15206
16311
  @media (max-width: 840px) {
15207
16312
  .dim-title { align-items: flex-start; }
15208
16313
  .dim-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
15209
- .dim-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
16314
+ .dim-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); padding-right: 1px; }
15210
16315
  .dim-divider { display: none; }
15211
- .dim-rail { max-height: none; overflow: visible; padding-right: 1px; }
15212
16316
  .dim-channel { min-height: 48px; }
15213
16317
  }
15214
16318
  @media (max-width: 560px) {
@@ -15256,13 +16360,261 @@ function installImStyles() {
15256
16360
  const style = document.createElement("style");
15257
16361
  style.dataset.plugin = "@xmanrui/dsh-im";
15258
16362
  style.dataset.pluginCss = IM_STYLE_ID;
15259
- style.textContent = CSS11;
16363
+ style.textContent = CSS12;
15260
16364
  document.head.appendChild(style);
15261
16365
  return () => style.remove();
15262
16366
  }
15263
16367
 
16368
+ // src/channels/shared/session-channel-labels.mjs
16369
+ var SESSION_CHANNEL_LABELS = Object.freeze(Object.fromEntries(Object.entries({
16370
+ weixin: ["\u5FAE\u4FE1", "WeChat"],
16371
+ feishu: ["\u98DE\u4E66", "Feishu"],
16372
+ dingtalk: ["\u9489\u9489", "DingTalk"],
16373
+ wecom: ["\u4F01\u4E1A\u5FAE\u4FE1", "WeCom"],
16374
+ qq: ["QQ", "QQ"],
16375
+ slack: ["Slack", "Slack"],
16376
+ telegram: ["Telegram", "Telegram"],
16377
+ discord: ["Discord", "Discord"],
16378
+ whatsapp: ["WhatsApp", "WhatsApp"],
16379
+ office: ["AI Office", "AI Office"]
16380
+ }).map(([channel4, labels]) => [channel4, Object.freeze(labels)])));
16381
+ function parseSessionChannelTitle(title) {
16382
+ if (typeof title !== "string") return null;
16383
+ for (const [channel4, labels] of Object.entries(SESSION_CHANNEL_LABELS)) {
16384
+ for (const label of labels) {
16385
+ const prefix = `${label} \xB7 `;
16386
+ if (title.startsWith(prefix) && title.slice(prefix.length).trim()) {
16387
+ return { channel: channel4, title: title.slice(prefix.length) };
16388
+ }
16389
+ }
16390
+ }
16391
+ return null;
16392
+ }
16393
+
16394
+ // plugin-src/client/session-channel-logos.js
16395
+ var GLYPHS = Object.freeze({
16396
+ weixin: [WeixinLogoGlyph, "#07c160"],
16397
+ feishu: [FeishuLogoGlyph, "#3370ff"],
16398
+ dingtalk: [DingtalkLogoGlyph, "#1677ff"],
16399
+ wecom: [WecomLogoGlyph, "#3370ff"],
16400
+ qq: [QqLogoGlyph, "#12b7f5"],
16401
+ slack: [SlackLogoGlyph, "#e01e5a"],
16402
+ telegram: [TelegramLogoGlyph, "#229ed9"],
16403
+ discord: [DiscordLogoGlyph, "#5865f2"],
16404
+ whatsapp: [WhatsappLogoGlyph, "#25d366"],
16405
+ office: [OfficeLogoGlyph, "#f97316"]
16406
+ });
16407
+ var CHANNEL_ATTR = "data-dsh-im-session-channel";
16408
+ var TITLE_ATTR = "data-dsh-im-session-text";
16409
+ var MARKED = `[${CHANNEL_ATTR}]`;
16410
+ var ROW = '[role="treeitem"][aria-selected]';
16411
+ var SVG_NS = "http://www.w3.org/2000/svg";
16412
+ var installations = /* @__PURE__ */ new WeakMap();
16413
+ function hasClassPart(element, part) {
16414
+ const match = new RegExp(`(?:^|[_-])${part}(?:$|[_-])`, "u");
16415
+ return [...element.classList].some((token) => match.test(token));
16416
+ }
16417
+ function titleOf(row) {
16418
+ if (!row.matches(ROW) || row.closest('input, textarea, [contenteditable]:not([contenteditable="false"])')) return null;
16419
+ let title;
16420
+ if (row.tagName === "DIV" && hasClassPart(row, "sessionRow")) {
16421
+ title = [...row.children].find((child) => child.tagName === "SPAN" && hasClassPart(child, "title"));
16422
+ } else if (row.tagName === "BUTTON" && hasClassPart(row, "searchResultRow")) {
16423
+ const heading = [...row.children].find((child) => hasClassPart(child, "searchResultHeading"));
16424
+ title = heading && [...heading.children].find((child) => child.tagName === "SPAN" && hasClassPart(child, "searchResultTitle"));
16425
+ }
16426
+ return title?.childNodes.length === 1 && title.firstChild.nodeType === 3 ? title : null;
16427
+ }
16428
+ function logoUri(document2, channel4) {
16429
+ const [Glyph, color] = GLYPHS[channel4];
16430
+ const render = (node) => {
16431
+ if (!["svg", "path"].includes(node.type)) throw new Error("Unsupported channel logo element");
16432
+ const element = document2.createElementNS(SVG_NS, node.type);
16433
+ for (const [name2, value] of Object.entries(node.props)) {
16434
+ if (name2 === "children") continue;
16435
+ const attribute = name2.startsWith("stroke") ? name2.replace(/[A-Z]/gu, (letter) => `-${letter.toLowerCase()}`) : name2;
16436
+ element.setAttribute(attribute, String(value));
16437
+ }
16438
+ for (const child of [node.props.children].flat().filter(Boolean)) element.appendChild(render(child));
16439
+ return element;
16440
+ };
16441
+ const svg = render(Glyph({ size: 16 }));
16442
+ svg.setAttribute("xmlns", SVG_NS);
16443
+ svg.setAttribute("color", color);
16444
+ return `data:image/svg+xml,${encodeURIComponent(svg.outerHTML)}`;
16445
+ }
16446
+ function createInstallation(document2) {
16447
+ const view = document2.defaultView;
16448
+ const ready = /* @__PURE__ */ new Set();
16449
+ const owned = /* @__PURE__ */ new Map();
16450
+ const queued = /* @__PURE__ */ new Set();
16451
+ const images = [];
16452
+ let closed = false;
16453
+ let scheduled = false;
16454
+ const style = document2.createElement("style");
16455
+ style.dataset.pluginCss = "dsh-im-session-channel-logos";
16456
+ const uris = Object.fromEntries(Object.keys(GLYPHS).map((channel4) => [channel4, logoUri(document2, channel4)]));
16457
+ style.textContent = `
16458
+ ${MARKED}[${TITLE_ATTR}] {
16459
+ position: relative;
16460
+ -webkit-text-fill-color: transparent;
16461
+ text-overflow: clip !important;
16462
+ overflow: hidden;
16463
+ }
16464
+ ${MARKED}[${TITLE_ATTR}]::before {
16465
+ content: "";
16466
+ position: absolute;
16467
+ inset-inline-start: 0;
16468
+ top: 50%;
16469
+ transform: translateY(-50%);
16470
+ width: 16px;
16471
+ height: 16px;
16472
+ background: var(--dsh-im-session-logo) center / contain no-repeat;
16473
+ text-indent: 0;
16474
+ pointer-events: none;
16475
+ }
16476
+ ${MARKED}[${TITLE_ATTR}]::after {
16477
+ content: attr(${TITLE_ATTR}) / "";
16478
+ position: absolute;
16479
+ inset: 0;
16480
+ inset-inline-start: 22px;
16481
+ -webkit-text-fill-color: currentColor;
16482
+ text-indent: 0;
16483
+ overflow: hidden;
16484
+ white-space: nowrap;
16485
+ text-overflow: ellipsis;
16486
+ pointer-events: none;
16487
+ }
16488
+ ${Object.entries(uris).map(([channel4, uri]) => `[${CHANNEL_ATTR}="${channel4}"] { --dsh-im-session-logo: url("${uri}"); }`).join("\n")}
16489
+ `;
16490
+ document2.head.appendChild(style);
16491
+ const restore = (title) => {
16492
+ const previous = owned.get(title);
16493
+ if (!previous) return;
16494
+ owned.delete(title);
16495
+ for (const [attribute, value] of previous) {
16496
+ if (value === null) title.removeAttribute(attribute);
16497
+ else title.setAttribute(attribute, value);
16498
+ }
16499
+ };
16500
+ const update = (row) => {
16501
+ const title = row.isConnected ? titleOf(row) : null;
16502
+ for (const marked of row.querySelectorAll(MARKED)) {
16503
+ if (marked !== title) restore(marked);
16504
+ }
16505
+ if (!title) return;
16506
+ const parsed = parseSessionChannelTitle(title.textContent);
16507
+ if (!parsed || !ready.has(parsed.channel)) {
16508
+ restore(title);
16509
+ return;
16510
+ }
16511
+ if (!owned.has(title)) {
16512
+ owned.set(title, [CHANNEL_ATTR, TITLE_ATTR].map((attribute) => [attribute, title.getAttribute(attribute)]));
16513
+ }
16514
+ title.setAttribute(TITLE_ATTR, parsed.title);
16515
+ title.setAttribute(CHANNEL_ATTR, parsed.channel);
16516
+ };
16517
+ const schedule = () => {
16518
+ if (closed || scheduled || !queued.size) return;
16519
+ scheduled = true;
16520
+ queueMicrotask(() => {
16521
+ scheduled = false;
16522
+ if (closed) return;
16523
+ const rows = [...queued];
16524
+ queued.clear();
16525
+ for (const row of rows) {
16526
+ try {
16527
+ update(row);
16528
+ } catch {
16529
+ for (const marked of row.querySelectorAll(MARKED)) restore(marked);
16530
+ }
16531
+ }
16532
+ });
16533
+ };
16534
+ const collect = (node, descendants = false) => {
16535
+ const element = node.nodeType === 1 ? node : node.parentElement;
16536
+ if (!element) return;
16537
+ const row = element.closest(ROW);
16538
+ if (row) queued.add(row);
16539
+ if (descendants) {
16540
+ for (const child of element.querySelectorAll(ROW)) queued.add(child);
16541
+ const marked = [...element.querySelectorAll(MARKED)];
16542
+ if (owned.has(element)) marked.push(element);
16543
+ for (const title of marked) {
16544
+ const owner = title.closest(ROW);
16545
+ if (owner) queued.add(owner);
16546
+ else restore(title);
16547
+ }
16548
+ }
16549
+ schedule();
16550
+ };
16551
+ const observer = new view.MutationObserver((records) => {
16552
+ if (closed) return;
16553
+ for (const record2 of records) {
16554
+ collect(record2.target, record2.type === "attributes");
16555
+ if (record2.type !== "childList") continue;
16556
+ for (const node of record2.addedNodes) collect(node, true);
16557
+ for (const node of record2.removedNodes) {
16558
+ if (node.nodeType !== 1 || node.isConnected) continue;
16559
+ if (owned.has(node)) restore(node);
16560
+ for (const title of node.querySelectorAll(MARKED)) restore(title);
16561
+ }
16562
+ }
16563
+ });
16564
+ observer.observe(document2.body, {
16565
+ subtree: true,
16566
+ childList: true,
16567
+ characterData: true,
16568
+ attributes: true,
16569
+ attributeFilter: ["class", "role", "aria-selected", "contenteditable"]
16570
+ });
16571
+ for (const [channel4, uri] of Object.entries(uris)) {
16572
+ const image = new view.Image();
16573
+ images.push(image);
16574
+ image.onload = () => {
16575
+ if (closed) return;
16576
+ ready.add(channel4);
16577
+ collect(document2.body, true);
16578
+ };
16579
+ image.src = uri;
16580
+ }
16581
+ return () => {
16582
+ closed = true;
16583
+ observer.disconnect();
16584
+ queued.clear();
16585
+ for (const image of images) image.onload = null;
16586
+ for (const title of [...owned.keys()]) restore(title);
16587
+ style.remove();
16588
+ };
16589
+ }
16590
+ function installSessionChannelLogos(document2 = globalThis.document) {
16591
+ const view = document2?.defaultView;
16592
+ if (!document2?.body || !document2?.head || !view?.MutationObserver || !view?.Image || !view.CSS?.supports?.("content", `attr(${TITLE_ATTR}) / ""`) || !view.CSS.supports("-webkit-text-fill-color", "transparent")) return () => {
16593
+ };
16594
+ let entry = installations.get(document2);
16595
+ if (!entry) {
16596
+ try {
16597
+ entry = { references: 0, close: createInstallation(document2) };
16598
+ } catch {
16599
+ return () => {
16600
+ };
16601
+ }
16602
+ installations.set(document2, entry);
16603
+ }
16604
+ entry.references += 1;
16605
+ let released = false;
16606
+ return () => {
16607
+ if (released) return;
16608
+ released = true;
16609
+ entry.references -= 1;
16610
+ if (entry.references > 0) return;
16611
+ entry.close();
16612
+ installations.delete(document2);
16613
+ };
16614
+ }
16615
+
15264
16616
  // plugin-src/client/update-panel.js
15265
- var React26 = __toESM(require("react"), 1);
16617
+ var React27 = __toESM(require("react"), 1);
15266
16618
  var import_react_dom3 = require("react-dom");
15267
16619
  var import_valid = __toESM(require_valid(), 1);
15268
16620
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -15309,7 +16661,7 @@ function unwrapSnapshot(result) {
15309
16661
  }
15310
16662
  return value;
15311
16663
  }
15312
- function presentError12(error) {
16664
+ function presentError13(error) {
15313
16665
  const code = error?.rpcError?.code ?? error?.code ?? "";
15314
16666
  const message = error?.rpcError?.message ?? error?.message ?? "";
15315
16667
  if (code === "update-unavailable" || /(?:not[-_ ]found|unimplemented|unknown[-_ ](?:endpoint|channel)|no[-_ ]handler)/i.test(code) || /(?:not found|unknown (?:endpoint|channel)|not registered|no .*handler|HTTP 404)/i.test(message)) {
@@ -15352,11 +16704,11 @@ function manualUpdateCommand(snapshot) {
15352
16704
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
15353
16705
  }
15354
16706
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
15355
- const [copyState, setCopyState] = React26.useState("idle");
15356
- const commandRef = React26.useRef(null);
15357
- const mounted = React26.useRef(false);
15358
- const copying = React26.useRef(false);
15359
- React26.useEffect(() => {
16707
+ const [copyState, setCopyState] = React27.useState("idle");
16708
+ const commandRef = React27.useRef(null);
16709
+ const mounted = React27.useRef(false);
16710
+ const copying = React27.useRef(false);
16711
+ React27.useEffect(() => {
15360
16712
  mounted.current = true;
15361
16713
  return () => {
15362
16714
  mounted.current = false;
@@ -15388,7 +16740,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
15388
16740
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
15389
16741
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
15390
16742
  command ? h2(
15391
- React26.Fragment,
16743
+ React27.Fragment,
15392
16744
  null,
15393
16745
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
15394
16746
  h2(
@@ -15424,7 +16776,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
15424
16776
  "aria-hidden": "true",
15425
16777
  focusable: "false"
15426
16778
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
15427
- React26.Fragment,
16779
+ React27.Fragment,
15428
16780
  null,
15429
16781
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
15430
16782
  h2("path", { d: "M5 13H3.5A1.5 1.5 0 0 1 2 11.5v-8A1.5 1.5 0 0 1 3.5 2h8A1.5 1.5 0 0 1 13 3.5V5" })
@@ -15449,10 +16801,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
15449
16801
  );
15450
16802
  }
15451
16803
  function UpdateDialog({ children, onClose }) {
15452
- const dialogRef = React26.useRef(null);
15453
- const titleId = React26.useId();
15454
- const descriptionId = React26.useId();
15455
- React26.useEffect(() => {
16804
+ const dialogRef = React27.useRef(null);
16805
+ const titleId = React27.useId();
16806
+ const descriptionId = React27.useId();
16807
+ React27.useEffect(() => {
15456
16808
  const previous = globalThis.document?.activeElement;
15457
16809
  dialogRef.current?.focus?.();
15458
16810
  return () => {
@@ -15510,19 +16862,19 @@ function UpdateDialog({ children, onClose }) {
15510
16862
  return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
15511
16863
  }
15512
16864
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15513
- const [snapshot, setSnapshot] = React26.useState(null);
15514
- const [action, setAction] = React26.useState("status");
15515
- const [error, setError] = React26.useState(null);
15516
- const [open, setOpen] = React26.useState(false);
15517
- const [uncertainInstall, setUncertainInstall] = React26.useState(false);
15518
- const mounted = React26.useRef(false);
15519
- const busy = React26.useRef(false);
15520
- const readController = React26.useRef(null);
15521
- const pollReadController = React26.useRef(null);
15522
- const installRequest = React26.useRef(null);
15523
- const onStatusRef = React26.useRef(onStatus);
16865
+ const [snapshot, setSnapshot] = React27.useState(null);
16866
+ const [action, setAction] = React27.useState("status");
16867
+ const [error, setError] = React27.useState(null);
16868
+ const [open, setOpen] = React27.useState(false);
16869
+ const [uncertainInstall, setUncertainInstall] = React27.useState(false);
16870
+ const mounted = React27.useRef(false);
16871
+ const busy = React27.useRef(false);
16872
+ const readController = React27.useRef(null);
16873
+ const pollReadController = React27.useRef(null);
16874
+ const installRequest = React27.useRef(null);
16875
+ const onStatusRef = React27.useRef(onStatus);
15524
16876
  onStatusRef.current = onStatus;
15525
- const accept = React26.useCallback((next) => {
16877
+ const accept = React27.useCallback((next) => {
15526
16878
  setSnapshot(next);
15527
16879
  onStatusRef.current?.(next);
15528
16880
  }, []);
@@ -15531,7 +16883,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15531
16883
  setUncertainInstall(false);
15532
16884
  accept(next);
15533
16885
  };
15534
- const invoke = React26.useCallback(async (endpoint, payload = {}, signal) => {
16886
+ const invoke = React27.useCallback(async (endpoint, payload = {}, signal) => {
15535
16887
  if (typeof rpcCall !== "function") {
15536
16888
  const unavailable = new Error("\u5F53\u524D Host \u4E0D\u652F\u6301\u66F4\u65B0\u63A5\u53E3\uFF0C\u8BF7\u5148\u624B\u52A8\u66F4\u65B0\u63D2\u4EF6\u5E76\u91CD\u542F\u3002");
15537
16889
  unavailable.code = "update-unavailable";
@@ -15539,14 +16891,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15539
16891
  }
15540
16892
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
15541
16893
  }, [rpcCall]);
15542
- React26.useEffect(() => {
16894
+ React27.useEffect(() => {
15543
16895
  mounted.current = true;
15544
16896
  busy.current = true;
15545
16897
  const controller = new AbortController();
15546
16898
  void invoke("update.status", {}, controller.signal).then((next) => {
15547
16899
  if (!controller.signal.aborted) accept(next);
15548
16900
  }).catch((cause) => {
15549
- if (!controller.signal.aborted) setError(presentError12(cause));
16901
+ if (!controller.signal.aborted) setError(presentError13(cause));
15550
16902
  }).finally(() => {
15551
16903
  if (!controller.signal.aborted) {
15552
16904
  busy.current = false;
@@ -15562,7 +16914,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15562
16914
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
15563
16915
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
15564
16916
  const shouldPoll = activeJob || uncertainInstall;
15565
- React26.useEffect(() => {
16917
+ React27.useEffect(() => {
15566
16918
  if (!shouldPoll) return void 0;
15567
16919
  let controller;
15568
16920
  const scheduler = createPollScheduler({
@@ -15583,7 +16935,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15583
16935
  keepPolling = ACTIVE_STATES4.has(next.job?.state);
15584
16936
  }
15585
16937
  } catch (cause) {
15586
- if (!pendingController.signal.aborted) setError(presentError12(cause));
16938
+ if (!pendingController.signal.aborted) setError(presentError13(cause));
15587
16939
  } finally {
15588
16940
  if (pollReadController.current === pendingController) pollReadController.current = null;
15589
16941
  }
@@ -15614,7 +16966,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15614
16966
  else acceptAuthoritative(next);
15615
16967
  }
15616
16968
  } catch (cause) {
15617
- if (!controller.signal.aborted && mounted.current) setError(presentError12(cause));
16969
+ if (!controller.signal.aborted && mounted.current) setError(presentError13(cause));
15618
16970
  } finally {
15619
16971
  if (!controller.signal.aborted && mounted.current) {
15620
16972
  busy.current = false;
@@ -15640,7 +16992,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15640
16992
  if (mounted.current) acceptAuthoritative(next);
15641
16993
  } catch (cause) {
15642
16994
  if (mounted.current) {
15643
- setError(presentError12(cause));
16995
+ setError(presentError13(cause));
15644
16996
  setUncertainInstall(true);
15645
16997
  }
15646
16998
  } finally {
@@ -15660,7 +17012,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15660
17012
  const manualCommand = manualUpdateCommand(snapshot);
15661
17013
  const buttonLabel = action === "checking" ? "\u68C0\u67E5\u4E2D\u2026" : action === "starting" || activeJob ? "\u6B63\u5728\u66F4\u65B0\u2026" : restartRequired ? "\u5F85\u624B\u52A8\u91CD\u542F" : snapshot?.canInstall ? "\u66F4\u65B0\u81F3" : "\u68C0\u67E5\u66F4\u65B0";
15662
17014
  return h2(
15663
- React26.Fragment,
17015
+ React27.Fragment,
15664
17016
  null,
15665
17017
  h2("button", {
15666
17018
  type: "button",
@@ -15685,13 +17037,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
15685
17037
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
15686
17038
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
15687
17039
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
15688
- React26.Fragment,
17040
+ React27.Fragment,
15689
17041
  null,
15690
17042
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
15691
17043
  h2("dd", null, `v${snapshot.installedVersion}`)
15692
17044
  ) : null,
15693
17045
  targetVersion ? h2(
15694
- React26.Fragment,
17046
+ React27.Fragment,
15695
17047
  null,
15696
17048
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
15697
17049
  h2("dd", null, `v${targetVersion}`)
@@ -15781,6 +17133,7 @@ var CHANNELS = Object.freeze([
15781
17133
  { id: "telegram", label: "Telegram" },
15782
17134
  { id: "discord", label: "Discord" },
15783
17135
  { id: "whatsapp", label: "WhatsApp" },
17136
+ { id: "wecomApp", label: "\u4F01\u4E1A\u5FAE\u4FE1\u5E94\u7528", note: "\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09" },
15784
17137
  { id: "office", label: "AI Office", note: "\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09" }
15785
17138
  ]);
15786
17139
  function WeixinLogo() {
@@ -15810,6 +17163,9 @@ function QqLogo() {
15810
17163
  function WecomLogo() {
15811
17164
  return h2("span", { className: "dim-logo dim-logoWecom", "aria-hidden": "true" }, h2(WecomLogoGlyph));
15812
17165
  }
17166
+ function WecomAppLogo() {
17167
+ return h2("span", { className: "dim-logo dim-logoWecomApp", "aria-hidden": "true" }, h2(WecomLogoGlyph));
17168
+ }
15813
17169
  function TelegramLogo() {
15814
17170
  return h2(
15815
17171
  "span",
@@ -15850,6 +17206,7 @@ function ChannelLogo({ channel: channel4 }) {
15850
17206
  if (channel4 === "feishu") return h2(FeishuLogo);
15851
17207
  if (channel4 === "dingtalk") return h2(DingtalkLogo);
15852
17208
  if (channel4 === "wecom") return h2(WecomLogo);
17209
+ if (channel4 === "wecomApp") return h2(WecomAppLogo);
15853
17210
  if (channel4 === "qq") return h2(QqLogo);
15854
17211
  if (channel4 === "slack") return h2(SlackLogo);
15855
17212
  if (channel4 === "telegram") return h2(TelegramLogo);
@@ -15886,6 +17243,7 @@ function IMSettingsTab({
15886
17243
  slackRpcCall,
15887
17244
  telegramRpcCall,
15888
17245
  wecomRpcCall,
17246
+ wecomAppRpcCall,
15889
17247
  weixinRpcCall,
15890
17248
  whatsappRpcCall,
15891
17249
  officeRpcCall,
@@ -15896,23 +17254,23 @@ function IMSettingsTab({
15896
17254
  browserLocation = globalThis.location,
15897
17255
  navigateToRecoveryUrl = replacePageLocation
15898
17256
  }) {
15899
- const [selected, setSelected] = React27.useState("weixin");
15900
- const [loopbackRecovery, setLoopbackRecovery] = React27.useState(null);
15901
- const [runningVersion, setRunningVersion] = React27.useState(IM_PLUGIN_VERSION);
15902
- const [deliverySettings, setDeliverySettings] = React27.useState(null);
15903
- const githubTooltipId = React27.useId();
15904
- const generalSettingsTooltipId = React27.useId();
17257
+ const [selected, setSelected] = React28.useState("weixin");
17258
+ const [loopbackRecovery, setLoopbackRecovery] = React28.useState(null);
17259
+ const [runningVersion, setRunningVersion] = React28.useState(IM_PLUGIN_VERSION);
17260
+ const [deliverySettings, setDeliverySettings] = React28.useState(null);
17261
+ const githubTooltipId = React28.useId();
17262
+ const generalSettingsTooltipId = React28.useId();
15905
17263
  const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
15906
17264
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
15907
17265
  const activeTabId = globalSettingsSelected ? "dim-general-settings-trigger" : `dim-tab-${active.id}`;
15908
17266
  const activePanelId = globalSettingsSelected ? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}` : `dim-panel-${active.id}`;
15909
- const reportLoopbackRecovery = React27.useCallback((recovery) => {
17267
+ const reportLoopbackRecovery = React28.useCallback((recovery) => {
15910
17268
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
15911
17269
  }, []);
15912
- const reportUpdateStatus = React27.useCallback((snapshot) => {
17270
+ const reportUpdateStatus = React28.useCallback((snapshot) => {
15913
17271
  setRunningVersion(snapshot.runningVersion);
15914
17272
  }, []);
15915
- const rpcCalls = React27.useMemo(() => createLoopbackAwareRpcCalls({
17273
+ const rpcCalls = React28.useMemo(() => createLoopbackAwareRpcCalls({
15916
17274
  dingtalkRpcCall,
15917
17275
  discordRpcCall,
15918
17276
  feishuRpcCall,
@@ -15920,6 +17278,7 @@ function IMSettingsTab({
15920
17278
  slackRpcCall,
15921
17279
  telegramRpcCall,
15922
17280
  wecomRpcCall,
17281
+ wecomAppRpcCall,
15923
17282
  weixinRpcCall,
15924
17283
  whatsappRpcCall,
15925
17284
  officeRpcCall,
@@ -15943,10 +17302,11 @@ function IMSettingsTab({
15943
17302
  telegramRpcCall,
15944
17303
  updateRpcCall,
15945
17304
  wecomRpcCall,
17305
+ wecomAppRpcCall,
15946
17306
  weixinRpcCall,
15947
17307
  whatsappRpcCall
15948
17308
  ]);
15949
- const botSettingsContext = React27.useMemo(() => Object.freeze({
17309
+ const botSettingsContext = React28.useMemo(() => Object.freeze({
15950
17310
  openBotSettings: setDeliverySettings
15951
17311
  }), []);
15952
17312
  return h2(
@@ -16075,7 +17435,7 @@ function IMSettingsTab({
16075
17435
  rpcCall: rpcCalls.deliveryRpcCall,
16076
17436
  accessRpcCall: rpcCalls[`${active.id}RpcCall`],
16077
17437
  onBack: () => setDeliverySettings(null)
16078
- }) : 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 })
17438
+ }) : 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 === "wecomApp" ? h2(WecomAppSettingsTab, { rpcCall: rpcCalls.wecomAppRpcCall }) : 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 })
16079
17439
  )
16080
17440
  )
16081
17441
  )
@@ -16089,11 +17449,13 @@ function apply(ctx) {
16089
17449
  );
16090
17450
  const t = ctx.locale.bind(IM_LOCALE_NAMESPACE);
16091
17451
  setImTranslator(t);
17452
+ ctx.effect(() => installSessionChannelLogos(), "im-settings: Session channel logos");
16092
17453
  ctx.effect(() => {
16093
17454
  const disposers = [
16094
17455
  installFeishuStyles(),
16095
17456
  installWeixinStyles(),
16096
17457
  installWecomStyles(),
17458
+ installWecomAppStyles(),
16097
17459
  installQqStyles(),
16098
17460
  installSlackStyles(),
16099
17461
  installTelegramStyles(),
@@ -16111,6 +17473,7 @@ function apply(ctx) {
16111
17473
  const dingtalkRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DINGTALK_RPC_CHANNEL, endpoint, payload, signal);
16112
17474
  const qqRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(QQ_RPC_CHANNEL, endpoint, payload, signal);
16113
17475
  const wecomRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WECOM_RPC_CHANNEL, endpoint, payload, signal);
17476
+ const wecomAppRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WECOM_APP_RPC_CHANNEL, endpoint, payload, signal);
16114
17477
  const telegramRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
16115
17478
  const discordRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DISCORD_RPC_CHANNEL, endpoint, payload, signal);
16116
17479
  const whatsappRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
@@ -16137,6 +17500,7 @@ function apply(ctx) {
16137
17500
  slackRpcCall,
16138
17501
  telegramRpcCall,
16139
17502
  wecomRpcCall,
17503
+ wecomAppRpcCall,
16140
17504
  weixinRpcCall,
16141
17505
  whatsappRpcCall,
16142
17506
  officeRpcCall,