@xmanrui/dsh-im 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/PROACTIVE_DELIVERY.en.md +321 -0
  2. package/PROACTIVE_DELIVERY.md +321 -0
  3. package/README.en.md +11 -5
  4. package/README.md +11 -5
  5. package/lib/client.js +1200 -110
  6. package/lib/index.js +229 -229
  7. package/package.json +4 -1
  8. package/plugin-src/client/channel-card-meta.js +44 -0
  9. package/plugin-src/client/channels/dingtalk/index.js +16 -8
  10. package/plugin-src/client/channels/feishu/index.js +17 -9
  11. package/plugin-src/client/channels/qq/index.js +16 -8
  12. package/plugin-src/client/channels/shared/token-channel.js +16 -8
  13. package/plugin-src/client/channels/wecom/index.js +16 -8
  14. package/plugin-src/client/channels/weixin/index.js +16 -8
  15. package/plugin-src/client/channels/whatsapp/index.js +16 -8
  16. package/plugin-src/client/delivery-settings.js +749 -0
  17. package/plugin-src/client/i18n.js +82 -0
  18. package/plugin-src/client/index.js +59 -22
  19. package/plugin-src/client/styles.js +72 -0
  20. package/plugin-src/host/channels/dingtalk/index.mjs +3 -0
  21. package/plugin-src/host/channels/dingtalk/production.mjs +4 -0
  22. package/plugin-src/host/channels/discord/index.mjs +6 -1
  23. package/plugin-src/host/channels/feishu/index.mjs +3 -0
  24. package/plugin-src/host/channels/feishu/production.mjs +4 -0
  25. package/plugin-src/host/channels/qq/index.mjs +6 -1
  26. package/plugin-src/host/channels/qq/production.mjs +2 -0
  27. package/plugin-src/host/channels/shared/production.mjs +7 -0
  28. package/plugin-src/host/channels/slack/index.mjs +6 -1
  29. package/plugin-src/host/channels/slack/production.mjs +4 -0
  30. package/plugin-src/host/channels/telegram/index.mjs +6 -1
  31. package/plugin-src/host/channels/wecom/index.mjs +6 -1
  32. package/plugin-src/host/channels/wecom/production.mjs +4 -0
  33. package/plugin-src/host/channels/weixin/index.mjs +3 -0
  34. package/plugin-src/host/channels/weixin/production.mjs +4 -0
  35. package/plugin-src/host/channels/whatsapp/index.mjs +6 -1
  36. package/plugin-src/host/channels/whatsapp/production.mjs +4 -0
  37. package/plugin-src/host/delivery-adapter.mjs +179 -0
  38. package/plugin-src/host/delivery-http.mjs +132 -0
  39. package/plugin-src/host/delivery-rpc.mjs +158 -0
  40. package/plugin-src/host/delivery-service.mjs +224 -0
  41. package/plugin-src/host/delivery-suggestions.mjs +135 -0
  42. package/plugin-src/host/index.mjs +32 -5
  43. package/scripts/verify-package.mjs +3 -0
  44. package/src/channels/dingtalk/dingtalk-api.mjs +33 -0
  45. package/src/channels/dingtalk/dingtalk-bridge.mjs +2 -2
  46. package/src/channels/dingtalk/dingtalk-controller.mjs +14 -0
  47. package/src/channels/dingtalk/dingtalk-runtime.mjs +39 -0
  48. package/src/channels/discord/discord-runtime.mjs +16 -0
  49. package/src/channels/feishu/bridge.mjs +4 -4
  50. package/src/channels/feishu/feishu-cards.mjs +4 -4
  51. package/src/channels/feishu/feishu-runtime.mjs +35 -0
  52. package/src/channels/feishu/multi-bot-controller.mjs +15 -0
  53. package/src/channels/qq/qq-bridge.mjs +2 -2
  54. package/src/channels/qq/qq-controller.mjs +14 -0
  55. package/src/channels/qq/qq-runtime.mjs +23 -0
  56. package/src/channels/shared/bot-workspace-store.mjs +192 -6
  57. package/src/channels/shared/i18n-en/feishu.mjs +6 -2
  58. package/src/channels/shared/i18n-en/shared-a.mjs +4 -0
  59. package/src/channels/shared/preset-command.mjs +2 -2
  60. package/src/channels/shared/text-harness-bridge.mjs +7 -2
  61. package/src/channels/shared/token-bot-controller.mjs +16 -0
  62. package/src/channels/shared/workspace-command.mjs +1 -1
  63. package/src/channels/slack/slack-controller.mjs +14 -0
  64. package/src/channels/slack/slack-runtime.mjs +24 -0
  65. package/src/channels/telegram/telegram-runtime.mjs +26 -0
  66. package/src/channels/wecom/state-store.mjs +4 -0
  67. package/src/channels/wecom/wecom-bridge.mjs +2 -2
  68. package/src/channels/wecom/wecom-controller.mjs +14 -0
  69. package/src/channels/wecom/wecom-runtime.mjs +21 -0
  70. package/src/channels/weixin/weixin-bridge.mjs +2 -2
  71. package/src/channels/weixin/weixin-controller.mjs +14 -0
  72. package/src/channels/weixin/weixin-runtime.mjs +24 -0
  73. package/src/channels/whatsapp/whatsapp-controller.mjs +14 -0
  74. package/src/channels/whatsapp/whatsapp-runtime.mjs +19 -0
package/lib/client.js CHANGED
@@ -566,12 +566,12 @@ __export(index_exports, {
566
566
  name: () => name
567
567
  });
568
568
  module.exports = __toCommonJS(index_exports);
569
- var React22 = __toESM(require("react"), 1);
569
+ var React23 = __toESM(require("react"), 1);
570
570
 
571
571
  // package.json
572
572
  var package_default = {
573
573
  name: "@xmanrui/dsh-im",
574
- version: "4.0.0",
574
+ version: "4.1.0",
575
575
  description: "\u628A\u4E5D\u79CD IM \u673A\u5668\u4EBA\u548C\u516C\u7F51 AI Office \u63A5\u5165\u672C\u673A DeepSeek Harness\u3002 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
576
576
  keywords: [
577
577
  "deepseek-harness",
@@ -635,6 +635,8 @@ var package_default = {
635
635
  "plugin-src",
636
636
  "scripts",
637
637
  "src",
638
+ "PROACTIVE_DELIVERY.md",
639
+ "PROACTIVE_DELIVERY.en.md",
638
640
  "LICENSE",
639
641
  "README.md",
640
642
  "README.en.md",
@@ -671,6 +673,7 @@ var package_default = {
671
673
  qrcode: "1.5.4"
672
674
  },
673
675
  devDependencies: {
676
+ "@deepseek-ai/cordis": "4.0.1",
674
677
  "@larksuiteoapi/node-sdk": "1.73.0",
675
678
  "@whiskeysockets/baileys": "7.0.0-rc14",
676
679
  esbuild: "0.25.9",
@@ -836,6 +839,7 @@ var EN = Object.freeze({
836
839
  "$locale": "en",
837
840
  "IM\u673A\u5668\u4EBA": "IM bots",
838
841
  "IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings",
842
+ "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E": "More bot settings",
839
843
  "IM \u6E20\u9053": "IM channels",
840
844
  "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA": "Connecting DeepSeek Harness",
841
845
  "\u5F53\u524D\u7248\u672C": "Current version",
@@ -1502,7 +1506,88 @@ var EN = Object.freeze({
1502
1506
  "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u8FDB\u5EA6": "WhatsApp did not return QR setup progress",
1503
1507
  "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u626B\u7801\u4EFB\u52A1": "WhatsApp did not return a valid setup attempt",
1504
1508
  "WhatsApp \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868": "WhatsApp did not return a valid account list",
1505
- "\u7528\u4E8E\u5173\u8054 WhatsApp \u8BBE\u5907\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for linking a WhatsApp device"
1509
+ "\u7528\u4E8E\u5173\u8054 WhatsApp \u8BBE\u5907\u7684\u4E00\u6B21\u6027\u4E8C\u7EF4\u7801": "One-time QR code for linking a WhatsApp device",
1510
+ "\u7528\u6237": "User",
1511
+ "\u5FAE\u4FE1\u7528\u6237 ID": "WeChat user ID",
1512
+ "\u586B\u5199\u63A5\u6536\u6D88\u606F\u7684\u7528\u6237 ID": "Enter the recipient user ID",
1513
+ "\u79C1\u804A": "Direct message",
1514
+ "\u7FA4\u804A": "Group",
1515
+ "\u7FA4 Chat ID": "Group Chat ID",
1516
+ "\u7528\u6237 ID": "User ID",
1517
+ "\u586B\u5199\u9489\u9489\u7528\u6237 ID": "Enter the DingTalk user ID",
1518
+ "\u7FA4 Open Conversation ID": "Group Open Conversation ID",
1519
+ "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID": "Enter the WeCom user ID",
1520
+ "\u586B\u5199\u7FA4 chatid": "Enter the group chatid",
1521
+ "\u5355\u804A": "Direct message",
1522
+ "\u7528\u6237 Open ID": "User Open ID",
1523
+ "\u586B\u5199 user_openid": "Enter the user_openid",
1524
+ "\u7FA4 Open ID": "Group Open ID",
1525
+ "\u586B\u5199 group_openid": "Enter the group_openid",
1526
+ "\u4F1A\u8BDD": "Conversation",
1527
+ "\u7EBF\u7A0B": "Thread",
1528
+ "Thread \u65F6\u95F4\u6233": "Thread timestamp",
1529
+ "\u804A\u5929": "Chat",
1530
+ "\u8BDD\u9898": "Topic",
1531
+ "\u9891\u9053\u6216\u79C1\u4FE1": "Channel or DM",
1532
+ "\u586B\u5199\u53EF\u53D1\u6D88\u606F\u7684 Channel ID": "Enter a Channel ID the bot can message",
1533
+ "\u7528\u6237 JID": "User JID",
1534
+ "\u7FA4 JID": "Group JID",
1535
+ "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Request failed. Try again later.",
1536
+ "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u81EA\u52A8\u590D\u5236\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u590D\u5236\u3002": "Automatic copy is unavailable. Select and copy the value manually.",
1537
+ "\u76EE\u6807\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002": "Could not save the target. Check it and try again.",
1538
+ "\u65E0\u6CD5\u751F\u6210\u672A\u5360\u7528\u7684 Target ID\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not generate an unused Target ID. Try again.",
1539
+ "\u7F16\u8F91\u6295\u9012\u76EE\u6807": "Edit delivery target",
1540
+ "\u65B0\u5EFA\u6295\u9012\u76EE\u6807": "New delivery target",
1541
+ "\u5DF2\u4ECE\u4F1A\u8BDD\u81EA\u52A8\u586B\u5165\u76EE\u6807\u4FE1\u606F\uFF1B\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002": "Target details were filled from the conversation. Review them before saving.",
1542
+ "\u8BF7\u624B\u52A8\u586B\u5199\u4ECE\u5BF9\u5E94\u5E73\u53F0\u53D6\u5F97\u7684\u539F\u751F\u6807\u8BC6\u3002": "Enter the native identifier from the platform manually.",
1543
+ "\u4F8B\u5982 daily-report": "For example, daily-report",
1544
+ "\u4F7F\u7528\u5927\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u3001\u5192\u53F7\u3001@ \u6216\u8FDE\u5B57\u7B26\u3002": "Use letters, numbers, dots, underscores, colons, @, or hyphens.",
1545
+ "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09": "Display name (optional)",
1546
+ "\u4F8B\u5982 \u6BCF\u65E5\u6C47\u62A5\u7FA4": "For example, Daily reports",
1547
+ "\u76EE\u6807\u7C7B\u578B": "Target type",
1548
+ "\u4FDD\u5B58\u76EE\u6807": "Save target",
1549
+ "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6": "Wait for delivery targets to finish loading",
1550
+ "\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9": "Choose from conversations",
1551
+ "\u5DF2\u804A\u4F1A\u8BDD": "Conversation",
1552
+ "\u4ECE\u4F1A\u8BDD\u9009\u62E9targetID": "Select a targetID from conversations",
1553
+ "\u9009\u62E9\u540E\u4F1A\u81EA\u52A8\u586B\u5199\u76EE\u6807\u4FE1\u606F\u548C\u8C03\u7528\u522B\u540D\uFF0C\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002": "Selecting a conversation fills the target details and call alias. Review them before saving.",
1554
+ "\u6B63\u5728\u5237\u65B0\u2026": "Refreshing\u2026",
1555
+ "\u5237\u65B0": "Refresh",
1556
+ "\u6B63\u5728\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\u2026": "Loading known conversations\u2026",
1557
+ "\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not load known conversations. Try again later.",
1558
+ "\u8FD8\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u4F1A\u8BDD": "No conversations to choose from",
1559
+ "\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF0C\u518D\u5237\u65B0\u3002": "Send the bot a message on the platform, then refresh.",
1560
+ "\u5DF2\u6DFB\u52A0": "Added",
1561
+ "\u624B\u52A8\u586B\u5199\uFF08\u9AD8\u7EA7\uFF09": "Enter manually (advanced)",
1562
+ "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002": "Test message sent. Confirm it in the target conversation.",
1563
+ "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Test delivery failed. Try again later.",
1564
+ "\u5DF2\u590D\u5236\u8C03\u7528\u53C2\u6570": "Call parameters copied",
1565
+ "\u590D\u5236\u5931\u8D25\u3002": "Copy failed.",
1566
+ "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not delete the target. Try again later.",
1567
+ "\u590D\u5236\u8C03\u7528\u53C2\u6570": "Copy call parameters",
1568
+ "\u673A\u5668\u4EBA\u79BB\u7EBF\u65F6\u4E0D\u53EF\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F": "Test delivery is unavailable while the bot is offline",
1569
+ "\u6D4B\u8BD5\u6295\u9012\u76EE\u6807": "Test delivery target",
1570
+ "\u6D4B\u8BD5": "Test",
1571
+ "\u7F16\u8F91": "Edit",
1572
+ "\u5220\u9664": "Delete",
1573
+ "\u5220\u9664 ": "Delete ",
1574
+ "\uFF1F\u4F7F\u7528\u8FD9\u4E2A targetId \u7684\u5916\u90E8\u8C03\u7528\u5C06\u8FD4\u56DE unknown-target\u3002": "? External calls using this targetId will return unknown-target.",
1575
+ "\u6B63\u5728\u5220\u9664\u2026": "Deleting\u2026",
1576
+ "\u786E\u8BA4\u5220\u9664": "Delete target",
1577
+ "\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Delivery target settings are unavailable.",
1578
+ "\u65E0\u6CD5\u8BFB\u53D6\u6295\u9012\u76EE\u6807\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Could not load delivery targets. Try again later.",
1579
+ "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868": "\u2190 Back to bots",
1580
+ "\u4F7F\u7528\u6587\u6863": "User guide",
1581
+ "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863": "Open the proactive delivery guide",
1582
+ "\u5F53\u524D\u6E20\u9053\u6682\u4E0D\u652F\u6301\u6295\u9012\u76EE\u6807\u3002": "Delivery targets are not supported for this channel.",
1583
+ "\u5DF2\u590D\u5236 Bot ID": "Bot ID copied",
1584
+ "\u590D\u5236": "Copy",
1585
+ "\u6295\u9012\u76EE\u6807": "Delivery targets",
1586
+ "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002": "The bot is offline. You can still configure targets and test them after reconnection.",
1587
+ "\u65B0\u5EFA\u76EE\u6807": "New target",
1588
+ "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026": "Loading delivery targets\u2026",
1589
+ "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807": "No delivery targets yet",
1590
+ "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002": "Select New target to choose a conversation or enter the details manually."
1506
1591
  });
1507
1592
  var en = EN;
1508
1593
  var zh = Object.freeze(Object.fromEntries(
@@ -2972,6 +3057,54 @@ function useWorkspaceSnapshotFence() {
2972
3057
 
2973
3058
  // plugin-src/client/channel-card-meta.js
2974
3059
  var React9 = __toESM(require("react"), 1);
3060
+ var BotSettingsContext = React9.createContext(Object.freeze({
3061
+ openBotSettings() {
3062
+ }
3063
+ }));
3064
+ function SettingsGlyph() {
3065
+ return h2(
3066
+ "svg",
3067
+ {
3068
+ viewBox: "0 0 24 24",
3069
+ width: 16,
3070
+ height: 16,
3071
+ fill: "none",
3072
+ stroke: "currentColor",
3073
+ strokeWidth: 1.8,
3074
+ strokeLinecap: "round",
3075
+ strokeLinejoin: "round",
3076
+ "aria-hidden": "true"
3077
+ },
3078
+ h2("circle", { cx: 12, cy: 12, r: 3 }),
3079
+ h2("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.83 2.83-.06-.06a1.7 1.7 0 0 0-1.88-.34 1.7 1.7 0 0 0-1.03 1.55V21h-4v-.08A1.7 1.7 0 0 0 8.97 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.83-2.83.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.52-1.03H3v-4h.08A1.7 1.7 0 0 0 4.6 8.97a1.7 1.7 0 0 0-.34-1.88l-.06-.06L7.03 4.2l.06.06a1.7 1.7 0 0 0 1.88.34A1.7 1.7 0 0 0 10 3.08V3h4v.08a1.7 1.7 0 0 0 1.03 1.52 1.7 1.7 0 0 0 1.88-.34l.06-.06 2.83 2.83-.06.06a1.7 1.7 0 0 0-.34 1.88A1.7 1.7 0 0 0 20.92 10H21v4h-.08A1.7 1.7 0 0 0 19.4 15Z" })
3080
+ );
3081
+ }
3082
+ function BotSettingsButton({ channel: channel4, botId, botName, connected }) {
3083
+ const { openBotSettings } = React9.useContext(BotSettingsContext);
3084
+ const tooltipId = React9.useId();
3085
+ return h2(
3086
+ "span",
3087
+ { className: "dim-botSettingsAction" },
3088
+ h2("button", {
3089
+ type: "button",
3090
+ className: "dim-botSettingsButton",
3091
+ "data-delivery-channel": channel4,
3092
+ "aria-label": "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E",
3093
+ "aria-describedby": tooltipId,
3094
+ onClick: () => openBotSettings?.({
3095
+ channel: channel4,
3096
+ botId,
3097
+ botName,
3098
+ connected: Boolean(connected)
3099
+ })
3100
+ }, h2(SettingsGlyph)),
3101
+ h2("span", {
3102
+ id: tooltipId,
3103
+ className: "dim-botSettingsTooltip",
3104
+ role: "tooltip"
3105
+ }, "\u66F4\u591A\u673A\u5668\u4EBA\u8BBE\u7F6E")
3106
+ );
3107
+ }
2975
3108
  function messageErrorTime(value) {
2976
3109
  try {
2977
3110
  return new Intl.DateTimeFormat(isEnglish() ? "en-US" : "zh-CN", {
@@ -3510,14 +3643,24 @@ function AccountCard({
3510
3643
  h2("p", { title: account.bot.clientIdMasked }, account.bot.clientIdMasked)
3511
3644
  )
3512
3645
  ),
3513
- h2(BotStatusMeta, {
3514
- className: "ddt-health",
3515
- dotClassName: "ddt-dot",
3516
- tone,
3517
- stateLabel: stateLabel2,
3518
- lastCheckedAt: account.health.lastCheckedAt,
3519
- formatCheckedTime: checkedTime
3520
- })
3646
+ h2(
3647
+ "div",
3648
+ { className: "dim-botCardTools" },
3649
+ h2(BotStatusMeta, {
3650
+ className: "ddt-health",
3651
+ dotClassName: "ddt-dot",
3652
+ tone,
3653
+ stateLabel: stateLabel2,
3654
+ lastCheckedAt: account.health.lastCheckedAt,
3655
+ formatCheckedTime: checkedTime
3656
+ }),
3657
+ h2(BotSettingsButton, {
3658
+ channel: "dingtalk",
3659
+ botId: account.botId,
3660
+ botName: account.bot.name,
3661
+ connected: account.connected
3662
+ })
3663
+ )
3521
3664
  ),
3522
3665
  h2(WorkspaceEditor, {
3523
3666
  workspace: account.workspace,
@@ -4175,7 +4318,7 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
4175
4318
  function createTokenChannelApi(channel4, connectionSummary, {
4176
4319
  normalizeBotExtension = () => ({})
4177
4320
  } = {}) {
4178
- const unwrapRpcResult10 = (result) => {
4321
+ const unwrapRpcResult11 = (result) => {
4179
4322
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
4180
4323
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
4181
4324
  }
@@ -4231,11 +4374,11 @@ function createTokenChannelApi(channel4, connectionSummary, {
4231
4374
  agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
4232
4375
  };
4233
4376
  };
4234
- const presentError11 = (error) => ({
4377
+ const presentError12 = (error) => ({
4235
4378
  code: text2(error?.code, `${channel4.toUpperCase()}_ERROR`, 80),
4236
4379
  message: text2(error?.message, `${channel4}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`)
4237
4380
  });
4238
- return Object.freeze({ unwrapRpcResult: unwrapRpcResult10, normalizeSnapshot: normalizeSnapshot9, presentError: presentError11 });
4381
+ return Object.freeze({ unwrapRpcResult: unwrapRpcResult11, normalizeSnapshot: normalizeSnapshot9, presentError: presentError12 });
4239
4382
  }
4240
4383
 
4241
4384
  // plugin-src/client/channels/discord/api.js
@@ -4330,14 +4473,24 @@ function createTokenChannelSettings(definition) {
4330
4473
  h2("p", null, identity)
4331
4474
  )
4332
4475
  ),
4333
- h2(BotStatusMeta, {
4334
- className: "ddt-health",
4335
- dotClassName: "ddt-dot",
4336
- tone,
4337
- stateLabel: stateLabel2,
4338
- lastCheckedAt: account.health.lastCheckedAt,
4339
- formatCheckedTime: checkedTime2
4340
- })
4476
+ h2(
4477
+ "div",
4478
+ { className: "dim-botCardTools" },
4479
+ h2(BotStatusMeta, {
4480
+ className: "ddt-health",
4481
+ dotClassName: "ddt-dot",
4482
+ tone,
4483
+ stateLabel: stateLabel2,
4484
+ lastCheckedAt: account.health.lastCheckedAt,
4485
+ formatCheckedTime: checkedTime2
4486
+ }),
4487
+ h2(BotSettingsButton, {
4488
+ channel: channel4.toLowerCase(),
4489
+ botId: account.botId,
4490
+ botName: account.bot.name,
4491
+ connected: account.connected
4492
+ })
4493
+ )
4341
4494
  ),
4342
4495
  h2(WorkspaceEditor, {
4343
4496
  workspace: account.workspace,
@@ -5637,15 +5790,25 @@ function BotCard({
5637
5790
  h2("p", { title: bot.appIdMasked }, bot.appIdMasked ?? "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58")
5638
5791
  )
5639
5792
  ),
5640
- h2(BotStatusMeta, {
5641
- className: "bxf-healthPill",
5642
- dotClassName: "bxf-dot",
5643
- tone,
5644
- stateLabel: HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5",
5645
- lastCheckedAt: health.lastCheckedAt,
5646
- formatCheckedTime,
5647
- healthState: stateForDisplay
5648
- })
5793
+ h2(
5794
+ "div",
5795
+ { className: "dim-botCardTools" },
5796
+ h2(BotStatusMeta, {
5797
+ className: "bxf-healthPill",
5798
+ dotClassName: "bxf-dot",
5799
+ tone,
5800
+ stateLabel: HEALTH_LABELS[stateForDisplay] ?? "\u72B6\u6001\u672A\u77E5",
5801
+ lastCheckedAt: health.lastCheckedAt,
5802
+ formatCheckedTime,
5803
+ healthState: stateForDisplay
5804
+ }),
5805
+ h2(BotSettingsButton, {
5806
+ channel: "feishu",
5807
+ botId: connection.botId,
5808
+ botName: bot.name,
5809
+ connected
5810
+ })
5811
+ )
5649
5812
  ),
5650
5813
  h2(WorkspaceEditor, {
5651
5814
  workspace: connection.workspace,
@@ -7507,14 +7670,24 @@ function AccountCard2({
7507
7670
  h2("p", null, account.bot.appIdMasked)
7508
7671
  )
7509
7672
  ),
7510
- h2(BotStatusMeta, {
7511
- className: "ddt-health",
7512
- dotClassName: "ddt-dot",
7513
- tone,
7514
- stateLabel: stateLabel2,
7515
- lastCheckedAt: account.health.lastCheckedAt,
7516
- formatCheckedTime: checkedTime3
7517
- })
7673
+ h2(
7674
+ "div",
7675
+ { className: "dim-botCardTools" },
7676
+ h2(BotStatusMeta, {
7677
+ className: "ddt-health",
7678
+ dotClassName: "ddt-dot",
7679
+ tone,
7680
+ stateLabel: stateLabel2,
7681
+ lastCheckedAt: account.health.lastCheckedAt,
7682
+ formatCheckedTime: checkedTime3
7683
+ }),
7684
+ h2(BotSettingsButton, {
7685
+ channel: "qq",
7686
+ botId: account.botId,
7687
+ botName: account.bot.name,
7688
+ connected: account.connected
7689
+ })
7690
+ )
7518
7691
  ),
7519
7692
  h2(WorkspaceEditor, {
7520
7693
  workspace: account.workspace,
@@ -9069,14 +9242,24 @@ function AccountCard3({
9069
9242
  h2("p", null, account.bot.appIdMasked)
9070
9243
  )
9071
9244
  ),
9072
- h2(BotStatusMeta, {
9073
- className: "ddt-health",
9074
- dotClassName: "ddt-dot",
9075
- tone,
9076
- stateLabel: stateLabel2,
9077
- lastCheckedAt: account.health.lastCheckedAt,
9078
- formatCheckedTime: checkedTime4
9079
- })
9245
+ h2(
9246
+ "div",
9247
+ { className: "dim-botCardTools" },
9248
+ h2(BotStatusMeta, {
9249
+ className: "ddt-health",
9250
+ dotClassName: "ddt-dot",
9251
+ tone,
9252
+ stateLabel: stateLabel2,
9253
+ lastCheckedAt: account.health.lastCheckedAt,
9254
+ formatCheckedTime: checkedTime4
9255
+ }),
9256
+ h2(BotSettingsButton, {
9257
+ channel: "wecom",
9258
+ botId: account.botId,
9259
+ botName: account.bot.name,
9260
+ connected: account.connected
9261
+ })
9262
+ )
9080
9263
  ),
9081
9264
  h2(WorkspaceEditor, {
9082
9265
  workspace: account.workspace,
@@ -9902,14 +10085,24 @@ function AccountCard4({
9902
10085
  h2("div", { className: "dxw-avatar dim-botAvatar", "aria-hidden": "true" }, h2(WeixinLogoGlyph, { size: 27 })),
9903
10086
  h2("div", { className: "dim-botName" }, h2("h3", null, account.bot.name), h2("p", null, account.bot.accountIdMasked))
9904
10087
  ),
9905
- h2(BotStatusMeta, {
9906
- className: "dxw-health",
9907
- dotClassName: "dxw-dot",
9908
- tone,
9909
- stateLabel: account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA",
9910
- lastCheckedAt: account.health.lastCheckedAt,
9911
- formatCheckedTime: checkedTime5
9912
- })
10088
+ h2(
10089
+ "div",
10090
+ { className: "dim-botCardTools" },
10091
+ h2(BotStatusMeta, {
10092
+ className: "dxw-health",
10093
+ dotClassName: "dxw-dot",
10094
+ tone,
10095
+ stateLabel: account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA",
10096
+ lastCheckedAt: account.health.lastCheckedAt,
10097
+ formatCheckedTime: checkedTime5
10098
+ }),
10099
+ h2(BotSettingsButton, {
10100
+ channel: "weixin",
10101
+ botId: account.botId,
10102
+ botName: account.bot.name,
10103
+ connected: account.connected
10104
+ })
10105
+ )
9913
10106
  ),
9914
10107
  h2(WorkspaceEditor, {
9915
10108
  workspace: account.workspace,
@@ -11134,14 +11327,24 @@ function WhatsappAccountCard({
11134
11327
  h2("p", null, account.bot.idMasked)
11135
11328
  )
11136
11329
  ),
11137
- h2(BotStatusMeta, {
11138
- className: "ddt-health",
11139
- dotClassName: "ddt-dot",
11140
- tone,
11141
- stateLabel: stateLabel2,
11142
- lastCheckedAt: account.health.lastCheckedAt,
11143
- formatCheckedTime: checkedTime6
11144
- })
11330
+ h2(
11331
+ "div",
11332
+ { className: "dim-botCardTools" },
11333
+ h2(BotStatusMeta, {
11334
+ className: "ddt-health",
11335
+ dotClassName: "ddt-dot",
11336
+ tone,
11337
+ stateLabel: stateLabel2,
11338
+ lastCheckedAt: account.health.lastCheckedAt,
11339
+ formatCheckedTime: checkedTime6
11340
+ }),
11341
+ h2(BotSettingsButton, {
11342
+ channel: "whatsapp",
11343
+ botId: account.botId,
11344
+ botName: account.bot.name,
11345
+ connected: account.connected
11346
+ })
11347
+ )
11145
11348
  ),
11146
11349
  h2(WorkspaceEditor, {
11147
11350
  workspace: account.workspace,
@@ -11501,6 +11704,792 @@ function WhatsappSettingsTab({ rpcCall }) {
11501
11704
  ));
11502
11705
  }
11503
11706
 
11707
+ // plugin-src/client/delivery-settings.js
11708
+ var React21 = __toESM(require("react"), 1);
11709
+ var DELIVERY_RPC_CHANNEL = "/dsh-im-delivery";
11710
+ var DELIVERY_DOCS_URL = Object.freeze({
11711
+ zh: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.md",
11712
+ en: "https://github.com/xmanrui/dsh-im/blob/main/PROACTIVE_DELIVERY.en.md"
11713
+ });
11714
+ var DELIVERY_ENDPOINTS = Object.freeze({
11715
+ list: "target.list",
11716
+ listSuggestions: "target.suggestion.list",
11717
+ create: "target.create",
11718
+ update: "target.update",
11719
+ delete: "target.delete",
11720
+ test: "target.test"
11721
+ });
11722
+ var CHANNEL_DEFINITIONS = Object.freeze({
11723
+ weixin: {
11724
+ label: "\u5FAE\u4FE1",
11725
+ kinds: [{ value: "user", label: "\u7528\u6237" }],
11726
+ fields: { user: [{ key: "toUserId", label: "\u5FAE\u4FE1\u7528\u6237 ID", placeholder: "\u586B\u5199\u63A5\u6536\u6D88\u606F\u7684\u7528\u6237 ID" }] }
11727
+ },
11728
+ feishu: {
11729
+ label: "\u98DE\u4E66",
11730
+ kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
11731
+ fields: {
11732
+ user: [{ key: "openId", label: "Open ID", placeholder: "ou_xxx" }],
11733
+ group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "oc_xxx" }]
11734
+ }
11735
+ },
11736
+ dingtalk: {
11737
+ label: "\u9489\u9489",
11738
+ kinds: [{ value: "user", label: "\u7528\u6237" }, { value: "group", label: "\u7FA4\u804A" }],
11739
+ fields: {
11740
+ user: [{ key: "userId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u9489\u9489\u7528\u6237 ID" }],
11741
+ group: [{ key: "openConversationId", label: "\u7FA4 Open Conversation ID", placeholder: "cidxxx" }]
11742
+ }
11743
+ },
11744
+ wecom: {
11745
+ label: "\u4F01\u4E1A\u5FAE\u4FE1",
11746
+ kinds: [{ value: "user", label: "\u79C1\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
11747
+ fields: {
11748
+ user: [{ key: "chatId", label: "\u7528\u6237 ID", placeholder: "\u586B\u5199\u4F01\u4E1A\u5FAE\u4FE1\u7528\u6237 ID" }],
11749
+ group: [{ key: "chatId", label: "\u7FA4 Chat ID", placeholder: "\u586B\u5199\u7FA4 chatid" }]
11750
+ }
11751
+ },
11752
+ qq: {
11753
+ label: "QQ",
11754
+ kinds: [{ value: "user", label: "\u5355\u804A" }, { value: "group", label: "\u7FA4\u804A" }],
11755
+ fields: {
11756
+ user: [{ key: "userOpenId", label: "\u7528\u6237 Open ID", placeholder: "\u586B\u5199 user_openid" }],
11757
+ group: [{ key: "groupOpenId", label: "\u7FA4 Open ID", placeholder: "\u586B\u5199 group_openid" }]
11758
+ }
11759
+ },
11760
+ slack: {
11761
+ label: "Slack",
11762
+ kinds: [{ value: "conversation", label: "\u4F1A\u8BDD" }, { value: "thread", label: "\u7EBF\u7A0B" }],
11763
+ fields: {
11764
+ conversation: [{ key: "channelId", label: "Channel ID", placeholder: "C0123456789" }],
11765
+ thread: [
11766
+ { key: "channelId", label: "Channel ID", placeholder: "C0123456789" },
11767
+ { key: "threadTs", label: "Thread \u65F6\u95F4\u6233", placeholder: "1712345678.123456" }
11768
+ ]
11769
+ }
11770
+ },
11771
+ telegram: {
11772
+ label: "Telegram",
11773
+ kinds: [{ value: "chat", label: "\u804A\u5929" }, { value: "topic", label: "\u8BDD\u9898" }],
11774
+ fields: {
11775
+ chat: [{ key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" }],
11776
+ topic: [
11777
+ { key: "chatId", label: "Chat ID", placeholder: "-1001234567890", inputMode: "numeric" },
11778
+ { key: "messageThreadId", label: "Topic ID", placeholder: "123", inputMode: "numeric", integer: true }
11779
+ ]
11780
+ }
11781
+ },
11782
+ discord: {
11783
+ label: "Discord",
11784
+ kinds: [{ value: "channel", label: "\u9891\u9053\u6216\u79C1\u4FE1" }],
11785
+ fields: { channel: [{ key: "channelId", label: "Channel ID", placeholder: "\u586B\u5199\u53EF\u53D1\u6D88\u606F\u7684 Channel ID", inputMode: "numeric" }] }
11786
+ },
11787
+ whatsapp: {
11788
+ label: "WhatsApp",
11789
+ kinds: [{ value: "user", label: "\u7528\u6237" }, { value: "group", label: "\u7FA4\u804A" }],
11790
+ fields: {
11791
+ user: [{ key: "jid", label: "\u7528\u6237 JID", placeholder: "8613800000000@s.whatsapp.net" }],
11792
+ group: [{ key: "jid", label: "\u7FA4 JID", placeholder: "1234567890@g.us" }]
11793
+ }
11794
+ }
11795
+ });
11796
+ function presentError10(error, fallback) {
11797
+ return error?.message || fallback;
11798
+ }
11799
+ function unwrapRpcResult10(result) {
11800
+ if (result?.ok === true) return result.value;
11801
+ if (result?.ok === false) {
11802
+ const error = new Error(result.error?.message || "\u8BF7\u6C42\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
11803
+ error.code = result.error?.code;
11804
+ throw error;
11805
+ }
11806
+ return result;
11807
+ }
11808
+ function targetsFrom(value) {
11809
+ if (Array.isArray(value)) return value;
11810
+ return Array.isArray(value?.targets) ? value.targets : [];
11811
+ }
11812
+ function suggestionsFrom(value) {
11813
+ if (Array.isArray(value)) return value;
11814
+ return Array.isArray(value?.suggestions) ? value.suggestions : [];
11815
+ }
11816
+ function kindLabel(definition, kind) {
11817
+ return definition.kinds.find((entry) => entry.value === kind)?.label ?? kind;
11818
+ }
11819
+ function fieldsFor(definition, kind) {
11820
+ return definition.fields[kind] ?? [];
11821
+ }
11822
+ function cleanDisplayName(value) {
11823
+ return typeof value === "string" && value.trim() ? value.trim().slice(0, 80) : null;
11824
+ }
11825
+ function suggestionDisplayName(suggestion) {
11826
+ return cleanDisplayName(
11827
+ suggestion?.name ?? suggestion?.displayName ?? suggestion?.label
11828
+ );
11829
+ }
11830
+ function routeIdentity(definition, candidate) {
11831
+ const fields = fieldsFor(definition, candidate?.kind);
11832
+ if (fields.length === 0 || !candidate?.route || typeof candidate.route !== "object") return null;
11833
+ const values = [];
11834
+ for (const field of fields) {
11835
+ const value = candidate.route[field.key];
11836
+ if (value === void 0 || value === null || String(value).trim() === "") return null;
11837
+ values.push(String(value).trim());
11838
+ }
11839
+ return JSON.stringify([candidate.kind, ...values]);
11840
+ }
11841
+ function validSuggestions(definition, value) {
11842
+ const seen = /* @__PURE__ */ new Set();
11843
+ return suggestionsFrom(value).filter((suggestion) => {
11844
+ const identity = routeIdentity(definition, suggestion);
11845
+ if (!identity || seen.has(identity)) return false;
11846
+ seen.add(identity);
11847
+ return true;
11848
+ });
11849
+ }
11850
+ function maskRouteValue(value) {
11851
+ const text6 = String(value ?? "").trim();
11852
+ if (!text6) return "";
11853
+ const at = text6.indexOf("@");
11854
+ if (at > 0) {
11855
+ const local = text6.slice(0, at);
11856
+ const domain = text6.slice(at);
11857
+ const visible = local.length <= 4 ? `${local.slice(0, 1)}\u2022\u2022${local.slice(-1)}` : `${local.slice(0, 3)}\u2026${local.slice(-2)}`;
11858
+ return `${visible}${domain}`;
11859
+ }
11860
+ if (text6.length <= 6) return `${text6.slice(0, 2)}\u2026${text6.slice(-1)}`;
11861
+ if (text6.length <= 12) return `${text6.slice(0, 3)}\u2026${text6.slice(-3)}`;
11862
+ return `${text6.slice(0, 5)}\u2026${text6.slice(-4)}`;
11863
+ }
11864
+ function suggestionFallbackName(definition, suggestion) {
11865
+ const displayName = suggestionDisplayName(suggestion);
11866
+ if (displayName) return displayName;
11867
+ const firstField = fieldsFor(definition, suggestion.kind)[0];
11868
+ const route = firstField ? maskRouteValue(suggestion.route?.[firstField.key]) : "";
11869
+ return `${localizeText(kindLabel(definition, suggestion.kind))}${route ? ` \xB7 ${route}` : ""}`;
11870
+ }
11871
+ function randomTargetId(targets) {
11872
+ const used = new Set(targets.map((target) => target?.targetId));
11873
+ for (let attempt = 0; attempt < 32; attempt += 1) {
11874
+ const bytes = new Uint8Array(8);
11875
+ const crypto = globalThis.crypto;
11876
+ if (typeof crypto?.getRandomValues === "function") {
11877
+ crypto.getRandomValues(bytes);
11878
+ } else {
11879
+ for (let index = 0; index < bytes.length; index += 1) {
11880
+ bytes[index] = Math.floor(Math.random() * 256);
11881
+ }
11882
+ }
11883
+ const suffix = Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
11884
+ const targetId = `tgt_${suffix}`;
11885
+ if (!used.has(targetId)) return targetId;
11886
+ }
11887
+ throw new Error("\u65E0\u6CD5\u751F\u6210\u672A\u5360\u7528\u7684 Target ID\uFF0C\u8BF7\u91CD\u8BD5\u3002");
11888
+ }
11889
+ function DeliveryButton({ children, kind = "secondary", className = "", ...props }) {
11890
+ return h2("button", {
11891
+ ...props,
11892
+ type: props.type ?? "button",
11893
+ className: `dim-deliveryButton ${className}`.trim(),
11894
+ "data-kind": kind
11895
+ }, children);
11896
+ }
11897
+ async function copyText(value) {
11898
+ const clipboard = globalThis.navigator?.clipboard;
11899
+ if (typeof clipboard?.writeText !== "function") {
11900
+ throw new Error("\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u81EA\u52A8\u590D\u5236\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u590D\u5236\u3002");
11901
+ }
11902
+ await clipboard.writeText(value);
11903
+ }
11904
+ function TargetForm({
11905
+ definition,
11906
+ mode,
11907
+ initialValue,
11908
+ source,
11909
+ busy,
11910
+ connected,
11911
+ onCancel,
11912
+ onSave,
11913
+ onTest
11914
+ }) {
11915
+ const editing = mode === "edit";
11916
+ const initialKind = initialValue?.kind && definition.fields[initialValue.kind] ? initialValue.kind : definition.kinds[0].value;
11917
+ const [targetId, setTargetId] = React21.useState(initialValue?.targetId ?? "");
11918
+ const [name2, setName] = React21.useState(initialValue?.name ?? "");
11919
+ const [kind, setKind] = React21.useState(initialKind);
11920
+ const [route, setRoute] = React21.useState(initialValue?.route ?? {});
11921
+ const [error, setError] = React21.useState(null);
11922
+ const [testing, setTesting] = React21.useState(false);
11923
+ const [testState, setTestState] = React21.useState(null);
11924
+ const currentTarget = () => {
11925
+ const normalizedRoute = Object.fromEntries(fieldsFor(definition, kind).map((field) => {
11926
+ const raw = String(route[field.key] ?? "").trim();
11927
+ return [field.key, field.integer ? Number(raw) : raw];
11928
+ }));
11929
+ return {
11930
+ targetId: targetId.trim(),
11931
+ ...name2.trim() ? { name: name2.trim() } : {},
11932
+ kind,
11933
+ route: normalizedRoute
11934
+ };
11935
+ };
11936
+ const submit = async (event) => {
11937
+ event.preventDefault();
11938
+ setError(null);
11939
+ try {
11940
+ await onSave({
11941
+ ...currentTarget()
11942
+ });
11943
+ } catch (caught) {
11944
+ setError(presentError10(caught, "\u76EE\u6807\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002"));
11945
+ }
11946
+ };
11947
+ const testTarget = async () => {
11948
+ if (typeof onTest !== "function") return;
11949
+ setTesting(true);
11950
+ setTestState(null);
11951
+ try {
11952
+ const target = currentTarget();
11953
+ await onTest({ kind: target.kind, route: target.route });
11954
+ setTestState({ tone: "success", message: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002" });
11955
+ } catch (caught) {
11956
+ setTestState({ tone: "error", message: presentError10(caught, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
11957
+ } finally {
11958
+ setTesting(false);
11959
+ }
11960
+ };
11961
+ const testReady = fieldsFor(definition, kind).every((field) => String(route[field.key] ?? "").trim());
11962
+ return h2(
11963
+ "form",
11964
+ { className: "dim-targetForm", onSubmit: submit },
11965
+ h2(
11966
+ "div",
11967
+ { className: "dim-targetFormHeading" },
11968
+ h2("h3", null, editing ? "\u7F16\u8F91\u6295\u9012\u76EE\u6807" : "\u65B0\u5EFA\u6295\u9012\u76EE\u6807"),
11969
+ editing ? null : h2("p", null, source === "suggestion" ? "\u5DF2\u4ECE\u4F1A\u8BDD\u81EA\u52A8\u586B\u5165\u76EE\u6807\u4FE1\u606F\uFF1B\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002" : "\u8BF7\u624B\u52A8\u586B\u5199\u4ECE\u5BF9\u5E94\u5E73\u53F0\u53D6\u5F97\u7684\u539F\u751F\u6807\u8BC6\u3002")
11970
+ ),
11971
+ h2(
11972
+ "div",
11973
+ { className: "dim-targetFormGrid" },
11974
+ h2(
11975
+ "label",
11976
+ { className: "dim-targetField" },
11977
+ h2("span", null, "Target ID"),
11978
+ h2("input", {
11979
+ name: "targetId",
11980
+ value: targetId,
11981
+ readOnly: editing,
11982
+ disabled: testing,
11983
+ required: true,
11984
+ pattern: "[A-Za-z0-9._:@-]{1,128}",
11985
+ maxLength: 128,
11986
+ autoCapitalize: "none",
11987
+ autoCorrect: "off",
11988
+ spellCheck: false,
11989
+ placeholder: "\u4F8B\u5982 daily-report",
11990
+ onChange: (event) => setTargetId(event.target.value)
11991
+ })
11992
+ ),
11993
+ h2(
11994
+ "label",
11995
+ { className: "dim-targetField" },
11996
+ h2("span", null, "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09"),
11997
+ h2("input", {
11998
+ name: "name",
11999
+ value: name2,
12000
+ disabled: testing,
12001
+ maxLength: 80,
12002
+ placeholder: "\u4F8B\u5982 \u6BCF\u65E5\u6C47\u62A5\u7FA4",
12003
+ onChange: (event) => setName(event.target.value)
12004
+ })
12005
+ ),
12006
+ h2(
12007
+ "label",
12008
+ { className: "dim-targetField" },
12009
+ h2("span", null, "\u76EE\u6807\u7C7B\u578B"),
12010
+ h2("select", {
12011
+ name: "kind",
12012
+ value: kind,
12013
+ disabled: testing,
12014
+ onChange: (event) => {
12015
+ setKind(event.target.value);
12016
+ setRoute({});
12017
+ setTestState(null);
12018
+ }
12019
+ }, definition.kinds.map((entry) => h2("option", {
12020
+ key: entry.value,
12021
+ value: entry.value
12022
+ }, entry.label)))
12023
+ ),
12024
+ fieldsFor(definition, kind).map((field) => h2(
12025
+ "label",
12026
+ {
12027
+ key: field.key,
12028
+ className: "dim-targetField"
12029
+ },
12030
+ h2("span", null, field.label),
12031
+ h2("input", {
12032
+ name: field.key,
12033
+ value: route[field.key] ?? "",
12034
+ disabled: testing,
12035
+ required: true,
12036
+ inputMode: field.inputMode,
12037
+ autoCapitalize: "none",
12038
+ autoCorrect: "off",
12039
+ spellCheck: false,
12040
+ placeholder: field.placeholder,
12041
+ onChange: (event) => {
12042
+ setRoute((current) => ({
12043
+ ...current,
12044
+ [field.key]: event.target.value
12045
+ }));
12046
+ setTestState(null);
12047
+ }
12048
+ })
12049
+ ))
12050
+ ),
12051
+ error ? h2("p", { className: "dim-targetFormError", role: "alert" }, error) : null,
12052
+ testState ? h2("p", {
12053
+ className: "dim-targetFeedback",
12054
+ "data-tone": testState.tone,
12055
+ role: "status",
12056
+ "aria-live": "polite"
12057
+ }, testState.message) : null,
12058
+ h2(
12059
+ "div",
12060
+ { className: "dim-targetFormActions" },
12061
+ h2(DeliveryButton, { onClick: onCancel, disabled: busy || testing }, "\u53D6\u6D88"),
12062
+ h2(DeliveryButton, {
12063
+ onClick: () => void testTarget(),
12064
+ disabled: busy || testing || !connected || !testReady || typeof onTest !== "function",
12065
+ title: connected ? void 0 : "\u673A\u5668\u4EBA\u79BB\u7EBF\u65F6\u4E0D\u53EF\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F",
12066
+ "aria-label": "\u6D4B\u8BD5\u6295\u9012\u76EE\u6807"
12067
+ }, testing ? "\u6D4B\u8BD5\u4E2D\u2026" : "\u6D4B\u8BD5"),
12068
+ h2(
12069
+ DeliveryButton,
12070
+ { type: "submit", kind: "primary", disabled: busy || testing },
12071
+ busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u76EE\u6807"
12072
+ )
12073
+ )
12074
+ );
12075
+ }
12076
+ function suggestionOptionLabel(definition, suggestion, added) {
12077
+ const name2 = suggestionDisplayName(suggestion);
12078
+ const type = localizeText(kindLabel(definition, suggestion.kind));
12079
+ const route = fieldsFor(definition, suggestion.kind).map((field) => maskRouteValue(suggestion.route?.[field.key])).filter(Boolean).join(" \xB7 ");
12080
+ return [name2, type, route, added ? localizeText("\u5DF2\u6DFB\u52A0") : null].filter(Boolean).join(" \xB7 ");
12081
+ }
12082
+ function TargetSuggestionPicker({
12083
+ definition,
12084
+ phase,
12085
+ suggestions,
12086
+ error,
12087
+ targets,
12088
+ onRefresh,
12089
+ onSelect,
12090
+ onManual,
12091
+ onCancel
12092
+ }) {
12093
+ const configured = new Set(targets.map((target) => routeIdentity(definition, target)).filter(Boolean));
12094
+ return h2(
12095
+ "section",
12096
+ { className: "dim-targetSuggestions", "aria-label": "\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9" },
12097
+ h2(
12098
+ "div",
12099
+ { className: "dim-targetSuggestionHeading" },
12100
+ h2(
12101
+ "div",
12102
+ null,
12103
+ h2("h3", null, "\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9"),
12104
+ h2("p", null, "\u9009\u62E9\u540E\u4F1A\u81EA\u52A8\u586B\u5199\u76EE\u6807\u4FE1\u606F\u548C\u8C03\u7528\u522B\u540D\uFF0C\u786E\u8BA4\u540E\u518D\u4FDD\u5B58\u3002")
12105
+ ),
12106
+ h2(DeliveryButton, {
12107
+ onClick: () => void onRefresh(),
12108
+ disabled: phase === "loading"
12109
+ }, phase === "loading" ? "\u6B63\u5728\u5237\u65B0\u2026" : "\u5237\u65B0")
12110
+ ),
12111
+ phase === "loading" ? h2("div", { className: "dim-targetSuggestionState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\u2026") : phase === "error" ? h2(
12112
+ "div",
12113
+ { className: "dim-targetSuggestionState", role: "alert" },
12114
+ h2("p", null, error),
12115
+ h2(DeliveryButton, { onClick: () => void onRefresh() }, "\u91CD\u65B0\u8BFB\u53D6")
12116
+ ) : suggestions.length === 0 ? h2(
12117
+ "div",
12118
+ { className: "dim-targetSuggestionState" },
12119
+ h2("strong", null, "\u8FD8\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u4F1A\u8BDD"),
12120
+ h2("p", null, "\u5148\u5728\u5BF9\u5E94\u5E73\u53F0\u4E0E\u673A\u5668\u4EBA\u804A\u4E00\u6761\u6D88\u606F\uFF0C\u518D\u5237\u65B0\u3002")
12121
+ ) : h2(
12122
+ "label",
12123
+ { className: "dim-targetSuggestionField" },
12124
+ h2("span", null, "\u5DF2\u804A\u4F1A\u8BDD"),
12125
+ h2(
12126
+ "select",
12127
+ {
12128
+ name: "suggestion",
12129
+ value: "",
12130
+ onChange: (event) => {
12131
+ if (event.target.value === "") return;
12132
+ const suggestion = suggestions[Number(event.target.value)];
12133
+ if (suggestion) onSelect(suggestion);
12134
+ }
12135
+ },
12136
+ h2("option", { value: "", disabled: true }, "\u4ECE\u4F1A\u8BDD\u9009\u62E9targetID"),
12137
+ suggestions.map((suggestion, index) => {
12138
+ const identity = routeIdentity(definition, suggestion);
12139
+ const added = configured.has(identity);
12140
+ return React21.createElement("option", {
12141
+ key: suggestion.id ?? suggestion.suggestionId ?? `${identity}:${index}`,
12142
+ value: String(index),
12143
+ disabled: added
12144
+ }, suggestionOptionLabel(definition, suggestion, added));
12145
+ })
12146
+ )
12147
+ ),
12148
+ h2(
12149
+ "div",
12150
+ { className: "dim-targetFormActions" },
12151
+ h2(DeliveryButton, { onClick: onCancel }, "\u53D6\u6D88"),
12152
+ h2(DeliveryButton, { onClick: onManual }, "\u624B\u52A8\u586B\u5199\uFF08\u9AD8\u7EA7\uFF09")
12153
+ )
12154
+ );
12155
+ }
12156
+ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
12157
+ const [action, setAction] = React21.useState(null);
12158
+ const [testState, setTestState] = React21.useState(null);
12159
+ const [copyState, setCopyState] = React21.useState(null);
12160
+ const [confirmDelete, setConfirmDelete] = React21.useState(false);
12161
+ const testTarget = async () => {
12162
+ setAction("test");
12163
+ setTestState(null);
12164
+ try {
12165
+ await rpcCall(DELIVERY_ENDPOINTS.test, { botId, targetId: target.targetId });
12166
+ setTestState({ tone: "success", message: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u76EE\u6807\u4F1A\u8BDD\u786E\u8BA4\u3002" });
12167
+ } catch (error) {
12168
+ setTestState({ tone: "error", message: presentError10(error, "\u6D4B\u8BD5\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
12169
+ } finally {
12170
+ setAction(null);
12171
+ }
12172
+ };
12173
+ const copyPair = async () => {
12174
+ setCopyState(null);
12175
+ try {
12176
+ await copyText(JSON.stringify({ botId, targetId: target.targetId }));
12177
+ setCopyState("\u5DF2\u590D\u5236\u8C03\u7528\u53C2\u6570");
12178
+ } catch (error) {
12179
+ setCopyState(presentError10(error, "\u590D\u5236\u5931\u8D25\u3002"));
12180
+ }
12181
+ };
12182
+ const deleteTarget = async () => {
12183
+ setAction("delete");
12184
+ try {
12185
+ await rpcCall(DELIVERY_ENDPOINTS.delete, { botId, targetId: target.targetId });
12186
+ await onChanged();
12187
+ } catch (error) {
12188
+ setTestState({ tone: "error", message: presentError10(error, "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002") });
12189
+ setAction(null);
12190
+ setConfirmDelete(false);
12191
+ }
12192
+ };
12193
+ return h2(
12194
+ "li",
12195
+ { className: "dim-targetRow", "data-target-id": target.targetId },
12196
+ h2(
12197
+ "div",
12198
+ { className: "dim-targetSummary" },
12199
+ h2(
12200
+ "div",
12201
+ { className: "dim-targetTitle" },
12202
+ React21.createElement("strong", null, target.name || target.targetId),
12203
+ h2("span", null, kindLabel(definition, target.kind))
12204
+ ),
12205
+ h2("code", null, `targetId: ${target.targetId}`)
12206
+ ),
12207
+ h2(
12208
+ "div",
12209
+ { className: "dim-targetActions" },
12210
+ h2(DeliveryButton, { onClick: () => void copyPair() }, "\u590D\u5236\u8C03\u7528\u53C2\u6570"),
12211
+ h2(DeliveryButton, {
12212
+ onClick: () => void testTarget(),
12213
+ disabled: !connected || action === "test",
12214
+ title: connected ? void 0 : "\u673A\u5668\u4EBA\u79BB\u7EBF\u65F6\u4E0D\u53EF\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F",
12215
+ "aria-label": "\u6D4B\u8BD5\u6295\u9012\u76EE\u6807"
12216
+ }, action === "test" ? "\u6D4B\u8BD5\u4E2D\u2026" : "\u6D4B\u8BD5"),
12217
+ h2(DeliveryButton, { onClick: onEdit, disabled: Boolean(action) }, "\u7F16\u8F91"),
12218
+ h2(DeliveryButton, {
12219
+ kind: "danger",
12220
+ onClick: () => setConfirmDelete(true),
12221
+ disabled: Boolean(action)
12222
+ }, "\u5220\u9664")
12223
+ ),
12224
+ copyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, copyState) : null,
12225
+ testState ? h2("p", {
12226
+ className: "dim-targetFeedback",
12227
+ "data-tone": testState.tone,
12228
+ role: "status",
12229
+ "aria-live": "polite"
12230
+ }, testState.message) : null,
12231
+ confirmDelete ? h2(
12232
+ "div",
12233
+ { className: "dim-targetDeleteConfirm", role: "alertdialog" },
12234
+ h2(
12235
+ "p",
12236
+ null,
12237
+ "\u5220\u9664 ",
12238
+ h2("code", null, target.targetId),
12239
+ "\uFF1F\u4F7F\u7528\u8FD9\u4E2A targetId \u7684\u5916\u90E8\u8C03\u7528\u5C06\u8FD4\u56DE unknown-target\u3002"
12240
+ ),
12241
+ h2(
12242
+ "div",
12243
+ { className: "dim-targetFormActions" },
12244
+ h2(DeliveryButton, {
12245
+ onClick: () => setConfirmDelete(false),
12246
+ disabled: action === "delete"
12247
+ }, "\u53D6\u6D88"),
12248
+ h2(DeliveryButton, {
12249
+ kind: "danger",
12250
+ onClick: () => void deleteTarget(),
12251
+ disabled: action === "delete"
12252
+ }, action === "delete" ? "\u6B63\u5728\u5220\u9664\u2026" : "\u786E\u8BA4\u5220\u9664")
12253
+ )
12254
+ ) : null
12255
+ );
12256
+ }
12257
+ function DeliveryTargetSettingsPage({ channel: channel4, account, rpcCall, onBack }) {
12258
+ const definition = CHANNEL_DEFINITIONS[channel4];
12259
+ const [phase, setPhase] = React21.useState("loading");
12260
+ const [targets, setTargets] = React21.useState([]);
12261
+ const [suggestionPhase, setSuggestionPhase] = React21.useState("idle");
12262
+ const [suggestions, setSuggestions] = React21.useState([]);
12263
+ const [suggestionError, setSuggestionError] = React21.useState(null);
12264
+ const [error, setError] = React21.useState(null);
12265
+ const [editor, setEditor] = React21.useState(null);
12266
+ const [saving, setSaving] = React21.useState(false);
12267
+ const [botCopyState, setBotCopyState] = React21.useState(null);
12268
+ const mounted = React21.useRef(true);
12269
+ const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
12270
+ if (typeof rpcCall !== "function") throw new Error("\u6295\u9012\u76EE\u6807\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
12271
+ return unwrapRpcResult10(await rpcCall(endpoint, payload, signal));
12272
+ }, [rpcCall]);
12273
+ const loadTargets = React21.useCallback(async ({ signal, silent = false } = {}) => {
12274
+ if (!silent) setPhase("loading");
12275
+ setError(null);
12276
+ try {
12277
+ const value = await invoke(DELIVERY_ENDPOINTS.list, { botId: account.botId }, signal);
12278
+ if (signal?.aborted || !mounted.current) return;
12279
+ setTargets(targetsFrom(value));
12280
+ setPhase("ready");
12281
+ } catch (caught) {
12282
+ if (signal?.aborted || caught?.name === "AbortError" || !mounted.current) return;
12283
+ setError(presentError10(caught, "\u65E0\u6CD5\u8BFB\u53D6\u6295\u9012\u76EE\u6807\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
12284
+ setPhase("error");
12285
+ }
12286
+ }, [account.botId, invoke]);
12287
+ const loadSuggestions = React21.useCallback(async () => {
12288
+ setSuggestionPhase("loading");
12289
+ setSuggestionError(null);
12290
+ try {
12291
+ const value = await invoke(DELIVERY_ENDPOINTS.listSuggestions, { botId: account.botId });
12292
+ if (!mounted.current) return;
12293
+ setSuggestions(validSuggestions(definition, value));
12294
+ setSuggestionPhase("ready");
12295
+ } catch (caught) {
12296
+ if (caught?.name === "AbortError" || !mounted.current) return;
12297
+ setSuggestionError(presentError10(caught, "\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u804A\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"));
12298
+ setSuggestionPhase("error");
12299
+ }
12300
+ }, [account.botId, definition, invoke]);
12301
+ React21.useEffect(() => {
12302
+ mounted.current = true;
12303
+ const controller = new AbortController();
12304
+ void loadTargets({ signal: controller.signal });
12305
+ return () => {
12306
+ mounted.current = false;
12307
+ controller.abort();
12308
+ };
12309
+ }, [loadTargets]);
12310
+ if (!definition) {
12311
+ return h2(
12312
+ "section",
12313
+ { className: "dim-deliveryPage" },
12314
+ h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868"),
12315
+ h2("p", { role: "alert" }, "\u5F53\u524D\u6E20\u9053\u6682\u4E0D\u652F\u6301\u6295\u9012\u76EE\u6807\u3002")
12316
+ );
12317
+ }
12318
+ const saveTarget = async (target) => {
12319
+ setSaving(true);
12320
+ try {
12321
+ if (editor?.mode === "edit") {
12322
+ await invoke(DELIVERY_ENDPOINTS.update, {
12323
+ botId: account.botId,
12324
+ targetId: editor.target.targetId,
12325
+ target: {
12326
+ ...target.name ? { name: target.name } : {},
12327
+ kind: target.kind,
12328
+ route: target.route
12329
+ }
12330
+ });
12331
+ } else {
12332
+ await invoke(DELIVERY_ENDPOINTS.create, { botId: account.botId, target });
12333
+ }
12334
+ await loadTargets({ silent: true });
12335
+ if (mounted.current) setEditor(null);
12336
+ } finally {
12337
+ if (mounted.current) setSaving(false);
12338
+ }
12339
+ };
12340
+ const openSuggestionPicker = () => {
12341
+ setEditor({ mode: "suggestions" });
12342
+ void loadSuggestions();
12343
+ };
12344
+ const selectSuggestion = (suggestion) => {
12345
+ const identity = routeIdentity(definition, suggestion);
12346
+ if (!identity) return;
12347
+ const route = Object.fromEntries(fieldsFor(definition, suggestion.kind).map((field) => [
12348
+ field.key,
12349
+ suggestion.route[field.key]
12350
+ ]));
12351
+ setEditor({
12352
+ mode: "create",
12353
+ source: "suggestion",
12354
+ draftKey: identity,
12355
+ initialValue: {
12356
+ targetId: randomTargetId(targets),
12357
+ name: suggestionFallbackName(definition, suggestion),
12358
+ kind: suggestion.kind,
12359
+ route
12360
+ }
12361
+ });
12362
+ };
12363
+ const copyBotId = async () => {
12364
+ setBotCopyState(null);
12365
+ try {
12366
+ await copyText(account.botId);
12367
+ setBotCopyState("\u5DF2\u590D\u5236 Bot ID");
12368
+ } catch (caught) {
12369
+ setBotCopyState(presentError10(caught, "\u590D\u5236\u5931\u8D25\u3002"));
12370
+ }
12371
+ };
12372
+ return h2(
12373
+ "section",
12374
+ {
12375
+ className: "dim-deliveryPage",
12376
+ "aria-label": `${account.botName || definition.label}\u673A\u5668\u4EBA\u8BBE\u7F6E`
12377
+ },
12378
+ h2(
12379
+ "header",
12380
+ { className: "dim-deliveryHeader" },
12381
+ h2(DeliveryButton, { className: "dim-deliveryBack", onClick: onBack }, "\u2190 \u8FD4\u56DE\u673A\u5668\u4EBA\u5217\u8868"),
12382
+ h2(
12383
+ "div",
12384
+ { className: "dim-deliveryHeaderMeta" },
12385
+ h2(
12386
+ "a",
12387
+ {
12388
+ className: "dim-deliveryDocsLink",
12389
+ href: isEnglish() ? DELIVERY_DOCS_URL.en : DELIVERY_DOCS_URL.zh,
12390
+ target: "_blank",
12391
+ rel: "noopener noreferrer",
12392
+ "aria-label": "\u6253\u5F00\u4E3B\u52A8\u6295\u9012\u4F7F\u7528\u6587\u6863"
12393
+ },
12394
+ h2("span", null, "\u4F7F\u7528\u6587\u6863"),
12395
+ h2("span", { "aria-hidden": "true" }, "\u2197")
12396
+ )
12397
+ )
12398
+ ),
12399
+ h2(
12400
+ "section",
12401
+ { className: "dim-deliveryIdentity", "aria-labelledby": "dim-delivery-bot-title" },
12402
+ h2(
12403
+ "div",
12404
+ null,
12405
+ h2(
12406
+ "h2",
12407
+ { id: "dim-delivery-bot-title", className: "dim-deliveryBotName" },
12408
+ account.botName || "\u673A\u5668\u4EBA\u8BBE\u7F6E"
12409
+ )
12410
+ ),
12411
+ h2(
12412
+ "div",
12413
+ { className: "dim-deliveryBotId" },
12414
+ h2("span", null, "Bot ID"),
12415
+ h2("code", { title: account.botId }, account.botId),
12416
+ h2(DeliveryButton, { onClick: () => void copyBotId() }, "\u590D\u5236")
12417
+ ),
12418
+ botCopyState ? h2("p", { className: "dim-targetFeedback", role: "status" }, botCopyState) : null
12419
+ ),
12420
+ h2(
12421
+ "section",
12422
+ { className: "dim-deliveryTargets", "aria-labelledby": "dim-delivery-targets-title" },
12423
+ h2(
12424
+ "div",
12425
+ { className: "dim-deliverySectionHeading" },
12426
+ h2(
12427
+ "div",
12428
+ null,
12429
+ h2("h3", { id: "dim-delivery-targets-title" }, "\u6295\u9012\u76EE\u6807"),
12430
+ account.connected ? null : h2("p", null, "\u673A\u5668\u4EBA\u5F53\u524D\u79BB\u7EBF\uFF1B\u4ECD\u53EF\u914D\u7F6E\u76EE\u6807\uFF0C\u6062\u590D\u8FDE\u63A5\u540E\u518D\u6D4B\u8BD5\u3002")
12431
+ ),
12432
+ h2(DeliveryButton, {
12433
+ kind: "primary",
12434
+ onClick: openSuggestionPicker,
12435
+ disabled: Boolean(editor) || phase !== "ready",
12436
+ title: phase === "ready" ? void 0 : "\u8BF7\u5148\u5B8C\u6210\u6295\u9012\u76EE\u6807\u8BFB\u53D6"
12437
+ }, "\u65B0\u5EFA\u76EE\u6807")
12438
+ ),
12439
+ editor?.mode === "suggestions" ? h2(TargetSuggestionPicker, {
12440
+ definition,
12441
+ phase: suggestionPhase,
12442
+ suggestions,
12443
+ error: suggestionError,
12444
+ targets,
12445
+ onRefresh: loadSuggestions,
12446
+ onSelect: selectSuggestion,
12447
+ onManual: () => setEditor({
12448
+ mode: "create",
12449
+ source: "manual",
12450
+ draftKey: "manual",
12451
+ initialValue: { targetId: randomTargetId(targets) }
12452
+ }),
12453
+ onCancel: () => setEditor(null)
12454
+ }) : editor ? h2(TargetForm, {
12455
+ key: editor.mode === "edit" ? editor.target.targetId : editor.draftKey,
12456
+ definition,
12457
+ mode: editor.mode,
12458
+ initialValue: editor.mode === "edit" ? editor.target : editor.initialValue,
12459
+ source: editor.source,
12460
+ busy: saving,
12461
+ connected: account.connected,
12462
+ onCancel: () => setEditor(editor.source === "suggestion" ? { mode: "suggestions" } : null),
12463
+ onSave: saveTarget,
12464
+ onTest: (target) => invoke(DELIVERY_ENDPOINTS.test, {
12465
+ botId: account.botId,
12466
+ target
12467
+ })
12468
+ }) : null,
12469
+ phase === "loading" ? h2("div", { className: "dim-deliveryState", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6\u6295\u9012\u76EE\u6807\u2026") : phase === "error" ? h2(
12470
+ "div",
12471
+ { className: "dim-deliveryState", role: "alert" },
12472
+ h2("p", null, error),
12473
+ h2(DeliveryButton, { onClick: () => void loadTargets() }, "\u91CD\u65B0\u8BFB\u53D6")
12474
+ ) : targets.length === 0 ? h2(
12475
+ "div",
12476
+ { className: "dim-deliveryState dim-deliveryEmpty" },
12477
+ h2("strong", null, "\u5C1A\u672A\u914D\u7F6E\u6295\u9012\u76EE\u6807"),
12478
+ h2("p", null, "\u70B9\u51FB\u201C\u65B0\u5EFA\u76EE\u6807\u201D\u53EF\u4ECE\u5DF2\u804A\u8FC7\u7684\u4F1A\u8BDD\u9009\u62E9\uFF0C\u4E5F\u53EF\u624B\u52A8\u586B\u5199\u3002")
12479
+ ) : h2("ul", { className: "dim-targetList" }, targets.map((target) => h2(TargetRow, {
12480
+ key: target.targetId,
12481
+ definition,
12482
+ target,
12483
+ botId: account.botId,
12484
+ connected: account.connected,
12485
+ rpcCall: invoke,
12486
+ onChanged: () => loadTargets({ silent: true }),
12487
+ onEdit: () => setEditor({ mode: "edit", target, source: "edit" })
12488
+ })))
12489
+ )
12490
+ );
12491
+ }
12492
+
11504
12493
  // plugin-src/client/loopback-recovery.js
11505
12494
  var TRANSPORT_FORBIDDEN = /^transport failure for \/[A-Za-z0-9._~-]+\/[A-Za-z0-9_$./~-]+: HTTP 403$/;
11506
12495
  var LOOPBACK_RECOVERY_ERROR_CODE = "loopback-recovery-required";
@@ -11908,6 +12897,7 @@ var CSS11 = String.raw`
11908
12897
  .dim-panel .dim-botName { min-width: 0; }
11909
12898
  .dim-panel .dim-botName h3 { overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; font-weight: 650; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
11910
12899
  .dim-panel .dim-botName p { overflow: hidden; margin: 4px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
12900
+ .dim-panel .dim-botCardTools { flex: none; display: flex; align-items: flex-start; gap: 8px; }
11911
12901
  .dim-panel .dim-botHealthGroup { min-width: 0; max-width: 100%; flex: none; display: grid; justify-items: end; gap: 5px; }
11912
12902
  .dim-panel .dim-botCard .dim-botHealth { flex: none; min-height: 0; display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; border-radius: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
11913
12903
  .dim-panel .dim-lastChecked { display: inline-flex; align-items: baseline; gap: 4px; color: var(--dsw-alias-label-tertiary, #8f959e); font: inherit; font-size: 11px; font-weight: 400; line-height: normal; white-space: nowrap; }
@@ -11915,6 +12905,68 @@ var CSS11 = String.raw`
11915
12905
  .dim-panel .dim-botCard .dim-healthDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary, #20a162) 14%, transparent); }
11916
12906
  .dim-panel .dim-botCard .dim-healthDot[data-tone="warning"] { background: var(--dsw-alias-state-warn-primary, #d97706); }
11917
12907
  .dim-panel .dim-botCard .dim-healthDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
12908
+ .dim-botSettingsAction { position: relative; flex: none; display: inline-flex; }
12909
+ .dim-botSettingsButton { width: 32px; height: 32px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; cursor: pointer; transition: color .15s ease, background .15s ease; }
12910
+ .dim-botSettingsButton:hover { color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
12911
+ .dim-botSettingsButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
12912
+ .dim-botSettingsTooltip { position: absolute; top: calc(100% + 6px); right: 0; z-index: 30; width: max-content; padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
12913
+ .dim-botSettingsAction:hover .dim-botSettingsTooltip, .dim-botSettingsAction:focus-within .dim-botSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
12914
+ .dim-deliveryPage { min-width: 0; display: grid; gap: 14px; }
12915
+ .dim-deliveryHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
12916
+ .dim-deliveryHeaderMeta { min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 10px; text-align: right; }
12917
+ .dim-deliveryDocsLink { min-height: 30px; flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); font-size: 12px; line-height: 18px; text-decoration: none; white-space: nowrap; }
12918
+ .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
12919
+ .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
12920
+ .dim-deliveryButton { min-height: 30px; display: inline-flex; align-items: center; justify-content: center; padding: 5px 10px; 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: 12px; line-height: 18px; cursor: pointer; }
12921
+ .dim-deliveryButton:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
12922
+ .dim-deliveryButton:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
12923
+ .dim-deliveryButton:disabled { opacity: .5; cursor: not-allowed; }
12924
+ .dim-deliveryButton[data-kind="primary"] { border-color: var(--dsw-alias-state-business-primary, #3370ff); color: #fff; background: var(--dsw-alias-state-business-primary, #3370ff); }
12925
+ .dim-deliveryButton[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
12926
+ .dim-deliveryBack { flex: none; border-color: transparent; background: transparent; }
12927
+ .dim-deliveryIdentity, .dim-deliveryTargets { min-width: 0; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
12928
+ .dim-deliveryBotName, .dim-deliverySectionHeading h3 { min-width: 0; overflow: hidden; margin: 0; font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
12929
+ .dim-deliveryIdentity > div:first-child p, .dim-deliverySectionHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
12930
+ .dim-deliveryBotId { min-width: 0; display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content; align-items: center; gap: 10px; margin-top: 12px; padding: 10px 12px; border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
12931
+ .dim-deliveryBotId > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
12932
+ .dim-deliveryBotId code { min-width: 0; overflow: hidden; font: 12px/18px ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
12933
+ .dim-deliverySectionHeading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
12934
+ .dim-deliveryState { margin-top: 14px; padding: 24px 16px; border: 1px dashed var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; text-align: center; }
12935
+ .dim-deliveryState p { margin: 5px 0; }
12936
+ .dim-deliveryEmpty strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; }
12937
+ .dim-targetList { display: grid; gap: 10px; margin: 14px 0 0; padding: 0; list-style: none; }
12938
+ .dim-targetRow { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 8px 14px; padding: 13px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #fff); }
12939
+ .dim-targetSummary { min-width: 0; }
12940
+ .dim-targetTitle { min-width: 0; display: flex; align-items: center; gap: 7px; }
12941
+ .dim-targetTitle strong { overflow: hidden; font-size: 13px; line-height: 20px; font-weight: 620; text-overflow: ellipsis; white-space: nowrap; }
12942
+ .dim-targetTitle span { flex: none; padding: 1px 6px; border-radius: 5px; color: var(--dsw-alias-state-business-primary, #3370ff); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 9%, transparent); font-size: 10px; line-height: 16px; }
12943
+ .dim-targetSummary code { display: block; overflow: hidden; margin-top: 3px; color: var(--dsw-alias-label-secondary, #646a73); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
12944
+ .dim-targetActions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
12945
+ .dim-targetFeedback { grid-column: 1 / -1; margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
12946
+ .dim-targetFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
12947
+ .dim-targetFeedback[data-tone="error"], .dim-targetFormError { color: var(--dsw-alias-state-error-primary, #d54941); }
12948
+ .dim-targetDeleteConfirm { grid-column: 1 / -1; padding: 10px 12px; border-radius: 8px; background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 7%, var(--dsw-alias-bg-layer-1, #fff)); }
12949
+ .dim-targetDeleteConfirm p { margin: 0 0 8px; font-size: 12px; line-height: 18px; }
12950
+ .dim-targetSuggestions { margin-top: 14px; padding: 14px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
12951
+ .dim-targetSuggestionHeading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
12952
+ .dim-targetSuggestionHeading h3 { margin: 0; font-size: 14px; line-height: 21px; }
12953
+ .dim-targetSuggestionHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
12954
+ .dim-targetSuggestionState { margin-top: 12px; padding: 18px 12px; border: 1px dashed var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; text-align: center; }
12955
+ .dim-targetSuggestionState p { margin: 4px 0; }
12956
+ .dim-targetSuggestionState strong { color: var(--dsw-alias-label-primary, #1f2329); }
12957
+ .dim-targetSuggestionField { min-width: 0; display: grid; gap: 5px; margin-top: 12px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
12958
+ .dim-targetSuggestionField select { width: 100%; min-width: 0; height: 38px; padding: 0 10px; 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: 12px; cursor: pointer; }
12959
+ .dim-targetSuggestionField select:focus { outline: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 28%, transparent); border-color: var(--dsw-alias-state-business-primary, #3370ff); }
12960
+ .dim-targetForm { margin-top: 14px; padding: 14px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
12961
+ .dim-targetFormHeading h3 { margin: 0; font-size: 14px; line-height: 21px; }
12962
+ .dim-targetFormHeading p { margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
12963
+ .dim-targetFormGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 11px 12px; margin-top: 12px; }
12964
+ .dim-targetField { min-width: 0; display: grid; align-content: start; gap: 5px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
12965
+ .dim-targetField input, .dim-targetField select { width: 100%; min-width: 0; height: 34px; padding: 0 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
12966
+ .dim-targetField input:focus, .dim-targetField select:focus { outline: 2px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 28%, transparent); border-color: var(--dsw-alias-state-business-primary, #3370ff); }
12967
+ .dim-targetField input[readonly] { color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
12968
+ .dim-targetFormError { margin: 10px 0 0; font-size: 12px; line-height: 18px; }
12969
+ .dim-targetFormActions { display: flex; justify-content: flex-end; gap: 7px; margin-top: 12px; }
11918
12970
  .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
11919
12971
  .dim-panel .ddt-headingCopy { display: none; }
11920
12972
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
@@ -11934,6 +12986,10 @@ var CSS11 = String.raw`
11934
12986
  .dim-panel .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; }
11935
12987
  .dim-panel .ddt-qrColumn { width: 100%; min-width: 0; }
11936
12988
  .dim-panel .ddt-qrCopy { width: 100%; min-width: 0; overflow-wrap: anywhere; }
12989
+ .dim-targetRow { grid-template-columns: minmax(0, 1fr); }
12990
+ .dim-targetActions { justify-content: flex-start; }
12991
+ .dim-targetFormGrid { grid-template-columns: minmax(0, 1fr); }
12992
+ .dim-targetSuggestionHeading { align-items: stretch; flex-direction: column; }
11937
12993
  }
11938
12994
  @media (max-width: 840px) {
11939
12995
  .dim-title { align-items: flex-start; }
@@ -11957,6 +13013,11 @@ var CSS11 = String.raw`
11957
13013
  .dim-rail { grid-template-columns: minmax(0, 1fr); }
11958
13014
  .dim-loopbackRecovery { align-items: stretch; flex-direction: column; gap: 12px; }
11959
13015
  .dim-loopbackRecoveryAction { width: 100%; }
13016
+ .dim-deliveryHeader, .dim-deliverySectionHeading { align-items: stretch; flex-direction: column; }
13017
+ .dim-deliveryHeaderMeta { justify-content: space-between; text-align: left; }
13018
+ .dim-deliveryBotId { grid-template-columns: minmax(0, 1fr) max-content; }
13019
+ .dim-deliveryBotId > span { grid-column: 1 / -1; }
13020
+ .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
11960
13021
  .dim-directoryPickerBackdrop { padding: 10px; }
11961
13022
  .dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
11962
13023
  .dim-directoryPickerHeader { padding: 18px 17px 14px; }
@@ -11986,7 +13047,7 @@ function installImStyles() {
11986
13047
  }
11987
13048
 
11988
13049
  // plugin-src/client/update-panel.js
11989
- var React21 = __toESM(require("react"), 1);
13050
+ var React22 = __toESM(require("react"), 1);
11990
13051
  var import_react_dom3 = require("react-dom");
11991
13052
  var import_valid = __toESM(require_valid(), 1);
11992
13053
  var import_rcompare = __toESM(require_rcompare(), 1);
@@ -12033,7 +13094,7 @@ function unwrapSnapshot(result) {
12033
13094
  }
12034
13095
  return value;
12035
13096
  }
12036
- function presentError10(error) {
13097
+ function presentError11(error) {
12037
13098
  const code = error?.rpcError?.code ?? error?.code ?? "";
12038
13099
  const message = error?.rpcError?.message ?? error?.message ?? "";
12039
13100
  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)) {
@@ -12076,11 +13137,11 @@ function manualUpdateCommand(snapshot) {
12076
13137
  return `dsh plugin --profile ${profileArgument} add -w @xmanrui/dsh-im@${version}`;
12077
13138
  }
12078
13139
  function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
12079
- const [copyState, setCopyState] = React21.useState("idle");
12080
- const commandRef = React21.useRef(null);
12081
- const mounted = React21.useRef(false);
12082
- const copying = React21.useRef(false);
12083
- React21.useEffect(() => {
13140
+ const [copyState, setCopyState] = React22.useState("idle");
13141
+ const commandRef = React22.useRef(null);
13142
+ const mounted = React22.useRef(false);
13143
+ const copying = React22.useRef(false);
13144
+ React22.useEffect(() => {
12084
13145
  mounted.current = true;
12085
13146
  return () => {
12086
13147
  mounted.current = false;
@@ -12112,7 +13173,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
12112
13173
  { className: "dim-updateManual", "aria-label": "\u624B\u5DE5\u66F4\u65B0" },
12113
13174
  h2("h4", { className: "dim-updateManualHeading" }, "\u624B\u5DE5\u66F4\u65B0"),
12114
13175
  command ? h2(
12115
- React21.Fragment,
13176
+ React22.Fragment,
12116
13177
  null,
12117
13178
  h2("p", { className: "dim-updateManualHint" }, "\u81EA\u52A8\u66F4\u65B0\u5931\u8D25\u53EF\u4EE5\u4F7F\u7528\u547D\u4EE4\u66F4\u65B0\uFF1A"),
12118
13179
  h2(
@@ -12148,7 +13209,7 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
12148
13209
  "aria-hidden": "true",
12149
13210
  focusable: "false"
12150
13211
  }, copyState === "copied" ? h2("path", { d: "m4 10 4 4 8-8" }) : h2(
12151
- React21.Fragment,
13212
+ React22.Fragment,
12152
13213
  null,
12153
13214
  h2("rect", { x: 7, y: 7, width: 10, height: 11, rx: 1.5 }),
12154
13215
  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" })
@@ -12173,10 +13234,10 @@ function ManualUpdateCommand({ command, disabled, sourceInstall, desktop }) {
12173
13234
  );
12174
13235
  }
12175
13236
  function UpdateDialog({ children, onClose }) {
12176
- const dialogRef = React21.useRef(null);
12177
- const titleId = React21.useId();
12178
- const descriptionId = React21.useId();
12179
- React21.useEffect(() => {
13237
+ const dialogRef = React22.useRef(null);
13238
+ const titleId = React22.useId();
13239
+ const descriptionId = React22.useId();
13240
+ React22.useEffect(() => {
12180
13241
  const previous = globalThis.document?.activeElement;
12181
13242
  dialogRef.current?.focus?.();
12182
13243
  return () => {
@@ -12234,19 +13295,19 @@ function UpdateDialog({ children, onClose }) {
12234
13295
  return typeof document !== "undefined" && document.body ? (0, import_react_dom3.createPortal)(content, document.body) : content;
12235
13296
  }
12236
13297
  function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12237
- const [snapshot, setSnapshot] = React21.useState(null);
12238
- const [action, setAction] = React21.useState("status");
12239
- const [error, setError] = React21.useState(null);
12240
- const [open, setOpen] = React21.useState(false);
12241
- const [uncertainInstall, setUncertainInstall] = React21.useState(false);
12242
- const mounted = React21.useRef(false);
12243
- const busy = React21.useRef(false);
12244
- const readController = React21.useRef(null);
12245
- const pollReadController = React21.useRef(null);
12246
- const installRequest = React21.useRef(null);
12247
- const onStatusRef = React21.useRef(onStatus);
13298
+ const [snapshot, setSnapshot] = React22.useState(null);
13299
+ const [action, setAction] = React22.useState("status");
13300
+ const [error, setError] = React22.useState(null);
13301
+ const [open, setOpen] = React22.useState(false);
13302
+ const [uncertainInstall, setUncertainInstall] = React22.useState(false);
13303
+ const mounted = React22.useRef(false);
13304
+ const busy = React22.useRef(false);
13305
+ const readController = React22.useRef(null);
13306
+ const pollReadController = React22.useRef(null);
13307
+ const installRequest = React22.useRef(null);
13308
+ const onStatusRef = React22.useRef(onStatus);
12248
13309
  onStatusRef.current = onStatus;
12249
- const accept = React21.useCallback((next) => {
13310
+ const accept = React22.useCallback((next) => {
12250
13311
  setSnapshot(next);
12251
13312
  onStatusRef.current?.(next);
12252
13313
  }, []);
@@ -12255,7 +13316,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12255
13316
  setUncertainInstall(false);
12256
13317
  accept(next);
12257
13318
  };
12258
- const invoke = React21.useCallback(async (endpoint, payload = {}, signal) => {
13319
+ const invoke = React22.useCallback(async (endpoint, payload = {}, signal) => {
12259
13320
  if (typeof rpcCall !== "function") {
12260
13321
  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");
12261
13322
  unavailable.code = "update-unavailable";
@@ -12263,14 +13324,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12263
13324
  }
12264
13325
  return unwrapSnapshot(await rpcCall(endpoint, payload, signal));
12265
13326
  }, [rpcCall]);
12266
- React21.useEffect(() => {
13327
+ React22.useEffect(() => {
12267
13328
  mounted.current = true;
12268
13329
  busy.current = true;
12269
13330
  const controller = new AbortController();
12270
13331
  void invoke("update.status", {}, controller.signal).then((next) => {
12271
13332
  if (!controller.signal.aborted) accept(next);
12272
13333
  }).catch((cause) => {
12273
- if (!controller.signal.aborted) setError(presentError10(cause));
13334
+ if (!controller.signal.aborted) setError(presentError11(cause));
12274
13335
  }).finally(() => {
12275
13336
  if (!controller.signal.aborted) {
12276
13337
  busy.current = false;
@@ -12286,7 +13347,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12286
13347
  const activeJob = ACTIVE_STATES4.has(snapshot?.job?.state);
12287
13348
  const restartRequired = snapshot?.job?.state === "restart-required" || snapshot?.blockedReason === "pending-restart";
12288
13349
  const shouldPoll = activeJob || uncertainInstall;
12289
- React21.useEffect(() => {
13350
+ React22.useEffect(() => {
12290
13351
  if (!shouldPoll) return void 0;
12291
13352
  let controller;
12292
13353
  const scheduler = createPollScheduler({
@@ -12307,7 +13368,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12307
13368
  keepPolling = ACTIVE_STATES4.has(next.job?.state);
12308
13369
  }
12309
13370
  } catch (cause) {
12310
- if (!pendingController.signal.aborted) setError(presentError10(cause));
13371
+ if (!pendingController.signal.aborted) setError(presentError11(cause));
12311
13372
  } finally {
12312
13373
  if (pollReadController.current === pendingController) pollReadController.current = null;
12313
13374
  }
@@ -12338,7 +13399,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12338
13399
  else acceptAuthoritative(next);
12339
13400
  }
12340
13401
  } catch (cause) {
12341
- if (!controller.signal.aborted && mounted.current) setError(presentError10(cause));
13402
+ if (!controller.signal.aborted && mounted.current) setError(presentError11(cause));
12342
13403
  } finally {
12343
13404
  if (!controller.signal.aborted && mounted.current) {
12344
13405
  busy.current = false;
@@ -12364,7 +13425,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12364
13425
  if (mounted.current) acceptAuthoritative(next);
12365
13426
  } catch (cause) {
12366
13427
  if (mounted.current) {
12367
- setError(presentError10(cause));
13428
+ setError(presentError11(cause));
12368
13429
  setUncertainInstall(true);
12369
13430
  }
12370
13431
  } finally {
@@ -12384,7 +13445,7 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12384
13445
  const manualCommand = manualUpdateCommand(snapshot);
12385
13446
  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";
12386
13447
  return h2(
12387
- React21.Fragment,
13448
+ React22.Fragment,
12388
13449
  null,
12389
13450
  h2("button", {
12390
13451
  type: "button",
@@ -12409,13 +13470,13 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12409
13470
  h2("dt", null, "\u8FD0\u884C\u7248\u672C"),
12410
13471
  h2("dd", null, `v${snapshot?.runningVersion ?? clientVersion}`),
12411
13472
  snapshot?.installedVersion && snapshot.installedVersion !== snapshot.runningVersion ? h2(
12412
- React21.Fragment,
13473
+ React22.Fragment,
12413
13474
  null,
12414
13475
  h2("dt", null, "\u5DF2\u5B89\u88C5\u7248\u672C"),
12415
13476
  h2("dd", null, `v${snapshot.installedVersion}`)
12416
13477
  ) : null,
12417
13478
  targetVersion ? h2(
12418
- React21.Fragment,
13479
+ React22.Fragment,
12419
13480
  null,
12420
13481
  h2("dt", null, "\u76EE\u6807\u7248\u672C"),
12421
13482
  h2("dd", null, `v${targetVersion}`)
@@ -12487,6 +13548,14 @@ function UpdatePanel({ rpcCall, clientVersion, onStatus }) {
12487
13548
  var name = "im-settings";
12488
13549
  var inject = ["slots", "connection", "locale", "workspaces"];
12489
13550
  var IM_PLUGIN_VERSION = package_default.version;
13551
+ function callWorkspaceDirectoryApi(ctx, method, ...args) {
13552
+ const uiWorkspace = typeof ctx.get === "function" ? ctx.get("uiWorkspace") : void 0;
13553
+ const service = typeof uiWorkspace?.[method] === "function" ? uiWorkspace : ctx.workspaces;
13554
+ if (typeof service?.[method] !== "function") {
13555
+ throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002");
13556
+ }
13557
+ return service[method](...args);
13558
+ }
12490
13559
  var CHANNELS = Object.freeze([
12491
13560
  { id: "weixin", label: "\u5FAE\u4FE1" },
12492
13561
  { id: "feishu", label: "\u98DE\u4E66" },
@@ -12606,22 +13675,24 @@ function IMSettingsTab({
12606
13675
  whatsappRpcCall,
12607
13676
  officeRpcCall,
12608
13677
  updateRpcCall,
13678
+ deliveryRpcCall,
12609
13679
  workspaceDirectoryPicker,
12610
13680
  browserLocation = globalThis.location,
12611
13681
  navigateToRecoveryUrl = replacePageLocation
12612
13682
  }) {
12613
- const [selected, setSelected] = React22.useState("weixin");
12614
- const [loopbackRecovery, setLoopbackRecovery] = React22.useState(null);
12615
- const [runningVersion, setRunningVersion] = React22.useState(IM_PLUGIN_VERSION);
12616
- const githubTooltipId = React22.useId();
13683
+ const [selected, setSelected] = React23.useState("weixin");
13684
+ const [loopbackRecovery, setLoopbackRecovery] = React23.useState(null);
13685
+ const [runningVersion, setRunningVersion] = React23.useState(IM_PLUGIN_VERSION);
13686
+ const [deliverySettings, setDeliverySettings] = React23.useState(null);
13687
+ const githubTooltipId = React23.useId();
12617
13688
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
12618
- const reportLoopbackRecovery = React22.useCallback((recovery) => {
13689
+ const reportLoopbackRecovery = React23.useCallback((recovery) => {
12619
13690
  setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
12620
13691
  }, []);
12621
- const reportUpdateStatus = React22.useCallback((snapshot) => {
13692
+ const reportUpdateStatus = React23.useCallback((snapshot) => {
12622
13693
  setRunningVersion(snapshot.runningVersion);
12623
13694
  }, []);
12624
- const rpcCalls = React22.useMemo(() => createLoopbackAwareRpcCalls({
13695
+ const rpcCalls = React23.useMemo(() => createLoopbackAwareRpcCalls({
12625
13696
  dingtalkRpcCall,
12626
13697
  discordRpcCall,
12627
13698
  feishuRpcCall,
@@ -12632,7 +13703,8 @@ function IMSettingsTab({
12632
13703
  weixinRpcCall,
12633
13704
  whatsappRpcCall,
12634
13705
  officeRpcCall,
12635
- updateRpcCall
13706
+ updateRpcCall,
13707
+ deliveryRpcCall
12636
13708
  }, {
12637
13709
  location: browserLocation,
12638
13710
  onRecovery: reportLoopbackRecovery
@@ -12640,6 +13712,7 @@ function IMSettingsTab({
12640
13712
  browserLocation,
12641
13713
  dingtalkRpcCall,
12642
13714
  discordRpcCall,
13715
+ deliveryRpcCall,
12643
13716
  feishuRpcCall,
12644
13717
  officeRpcCall,
12645
13718
  qqRpcCall,
@@ -12651,6 +13724,9 @@ function IMSettingsTab({
12651
13724
  weixinRpcCall,
12652
13725
  whatsappRpcCall
12653
13726
  ]);
13727
+ const botSettingsContext = React23.useMemo(() => Object.freeze({
13728
+ openBotSettings: setDeliverySettings
13729
+ }), []);
12654
13730
  return h2(
12655
13731
  WorkspaceDirectoryPickerContext.Provider,
12656
13732
  { value: workspaceDirectoryPicker },
@@ -12719,7 +13795,10 @@ function IMSettingsTab({
12719
13795
  className: "dim-channel",
12720
13796
  "aria-selected": channel4.id === active.id,
12721
13797
  "aria-controls": `dim-panel-${channel4.id}`,
12722
- onClick: () => setSelected(channel4.id)
13798
+ onClick: () => {
13799
+ setSelected(channel4.id);
13800
+ setDeliverySettings(null);
13801
+ }
12723
13802
  },
12724
13803
  h2(ChannelLogo, { channel: channel4.id }),
12725
13804
  h2(
@@ -12743,7 +13822,16 @@ function IMSettingsTab({
12743
13822
  recovery: loopbackRecovery,
12744
13823
  onNavigate: navigateToRecoveryUrl
12745
13824
  }) : null,
12746
- 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 })
13825
+ h2(
13826
+ BotSettingsContext.Provider,
13827
+ { value: botSettingsContext },
13828
+ deliverySettings?.channel === active.id ? h2(DeliveryTargetSettingsPage, {
13829
+ channel: active.id,
13830
+ account: deliverySettings,
13831
+ rpcCall: rpcCalls.deliveryRpcCall,
13832
+ onBack: () => setDeliverySettings(null)
13833
+ }) : 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 })
13834
+ )
12747
13835
  )
12748
13836
  )
12749
13837
  )
@@ -12784,9 +13872,10 @@ function apply(ctx) {
12784
13872
  const slackRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(SLACK_RPC_CHANNEL, endpoint, payload, signal);
12785
13873
  const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal);
12786
13874
  const updateRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal);
13875
+ const deliveryRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DELIVERY_RPC_CHANNEL, endpoint, payload, signal);
12787
13876
  const workspaceDirectoryPicker = Object.freeze({
12788
- listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal),
12789
- pickDirectory: () => ctx.workspaces.pickDirectory()
13877
+ listDirectory: (path, signal) => callWorkspaceDirectoryApi(ctx, "listDirectory", path, signal),
13878
+ pickDirectory: () => callWorkspaceDirectoryApi(ctx, "pickDirectory")
12790
13879
  });
12791
13880
  ctx.slots.inject("settings.section", () => ctx.slots.register({
12792
13881
  name: "settings.section",
@@ -12806,6 +13895,7 @@ function apply(ctx) {
12806
13895
  whatsappRpcCall,
12807
13896
  officeRpcCall,
12808
13897
  updateRpcCall,
13898
+ deliveryRpcCall,
12809
13899
  workspaceDirectoryPicker
12810
13900
  })
12811
13901
  }, IMSettingsTab));