@xmanrui/dsh-im 0.13.0 → 0.15.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 (43) hide show
  1. package/README.en.md +25 -10
  2. package/README.md +26 -11
  3. package/assets/logo-dsh-im-connecting-readme-3x2.png +0 -0
  4. package/assets/logo-dsh-im-connecting-square-v4.png +0 -0
  5. package/assets/logo-plugin-phone.png +0 -0
  6. package/lib/client.js +815 -173
  7. package/lib/index.js +142 -130
  8. package/package.json +4 -3
  9. package/plugin-src/client/build.mjs +1 -6
  10. package/plugin-src/client/channel-logos.js +12 -0
  11. package/plugin-src/client/channels/office/api.js +44 -0
  12. package/plugin-src/client/channels/office/index.js +152 -0
  13. package/plugin-src/client/channels/office/styles.js +50 -0
  14. package/plugin-src/client/channels/shared/token-api.js +5 -1
  15. package/plugin-src/client/channels/shared/token-channel.js +16 -1
  16. package/plugin-src/client/channels/telegram/api.js +17 -2
  17. package/plugin-src/client/channels/telegram/index.js +112 -0
  18. package/plugin-src/client/channels/telegram/styles.js +26 -0
  19. package/plugin-src/client/i18n.js +76 -0
  20. package/plugin-src/client/index.js +22 -11
  21. package/plugin-src/client/styles.js +7 -4
  22. package/plugin-src/host/channels/office/index.mjs +10 -0
  23. package/plugin-src/host/channels/office/production.mjs +45 -0
  24. package/plugin-src/host/channels/office/rpc.mjs +48 -0
  25. package/plugin-src/host/channels/shared/production.mjs +9 -1
  26. package/plugin-src/host/channels/telegram/production.mjs +2 -0
  27. package/plugin-src/host/channels/telegram/rpc.mjs +58 -8
  28. package/plugin-src/host/index.mjs +3 -0
  29. package/src/channels/discord/discord-api.mjs +1 -1
  30. package/src/channels/office/config-store.mjs +107 -0
  31. package/src/channels/office/office-controller.mjs +167 -0
  32. package/src/channels/office/office-job-executor.mjs +390 -0
  33. package/src/channels/office/office-runtime.mjs +171 -0
  34. package/src/channels/office/office-transport.mjs +170 -0
  35. package/src/channels/office/protocol.mjs +43 -0
  36. package/src/channels/shared/token-bot-controller.mjs +28 -0
  37. package/src/channels/shared/token-config-store.mjs +14 -1
  38. package/src/channels/telegram/config-store.mjs +62 -1
  39. package/src/channels/telegram/telegram-controller.mjs +25 -1
  40. package/src/channels/telegram/telegram-runtime.mjs +26 -1
  41. package/src/channels/weixin/weixin-api.mjs +1 -1
  42. package/src/channels/weixin/weixin-controller.mjs +56 -12
  43. package/src/channels/weixin/weixin-runtime.mjs +12 -1
package/lib/client.js CHANGED
@@ -40,7 +40,7 @@ __export(index_exports, {
40
40
  name: () => name
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
- var React16 = __toESM(require("react"), 1);
43
+ var React18 = __toESM(require("react"), 1);
44
44
 
45
45
  // plugin-src/client/channel-logos.js
46
46
  var React = __toESM(require("react"), 1);
@@ -171,6 +171,20 @@ function WhatsappLogoGlyph({ size } = {}) {
171
171
  d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.074-.297-.149-1.255-.462-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.521.149-.173.198-.297.298-.495.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 0 0-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479s1.065 2.875 1.213 3.074c.149.198 2.095 3.2 5.077 4.487.709.306 1.262.489 1.694.626.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.29.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.981.999-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.83 9.83 0 0 1 2.893 6.991c-.003 5.45-4.437 9.884-9.886 9.884m8.413-18.297A11.8 11.8 0 0 0 12.055 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.9 11.9 0 0 0 5.688 1.448h.005c6.557 0 11.892-5.335 11.895-11.893a11.82 11.82 0 0 0-3.486-8.413"
172
172
  }));
173
173
  }
174
+ function OfficeLogoGlyph({ size } = {}) {
175
+ return h(
176
+ "svg",
177
+ {
178
+ ...dimensions(size),
179
+ viewBox: "0 0 24 24",
180
+ focusable: "false",
181
+ "aria-hidden": "true",
182
+ "data-im-channel-logo": "office"
183
+ },
184
+ h("path", { fill: "currentColor", d: "M4 3.5h10.5a2 2 0 0 1 2 2v13H4v-15Zm2.2 3v1.8h2V6.5h-2Zm4.1 0v1.8h2V6.5h-2Zm-4.1 4v1.8h2v-1.8h-2Zm4.1 0v1.8h2v-1.8h-2ZM8.4 15v3.5h3V15h-3Z" }),
185
+ h("path", { fill: "currentColor", d: "M18.3 8.2h1.5v3h3v1.5h-3v3h-1.5v-3h-3v-1.5h3v-3Z" })
186
+ );
187
+ }
174
188
 
175
189
  // plugin-src/client/channels/dingtalk/api.js
176
190
  var DINGTALK_RPC_CHANNEL = "/dingtalk";
@@ -384,6 +398,52 @@ var EN = Object.freeze({
384
398
  "IM\u673A\u5668\u4EBA\u8BBE\u7F6E": "IM bot settings",
385
399
  "IM \u6E20\u9053": "IM channels",
386
400
  "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA": "DeepSeek Harness, always within reach",
401
+ "AI Office": "AI Office",
402
+ "\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09": "(Experimental)",
403
+ "AI Office \u8BBE\u7F6E": "AI Office settings",
404
+ "AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5": "AI Office settings are missing an RPC connection",
405
+ "\u6B63\u5728\u8BFB\u53D6 AI Office Connector\u2026": "Loading AI Office Connector\u2026",
406
+ "\u672C\u673A\u4E3B\u52A8\u8FDE\u63A5\u516C\u7F51 Office\uFF1BHarness \u4E0D\u5F00\u653E\u7AEF\u53E3\u3002\u534F\u8BAE Hook \u56FA\u5B9A\u4E3A ": "This machine connects outward to the public Office; Harness exposes no port. Protocol hooks: ",
407
+ "\u5C1A\u672A\u914D\u7F6E": "Not configured",
408
+ "\u5DF2\u8FDE\u63A5 Office": "Connected to Office",
409
+ "\u5DF2\u914D\u7F6E": "Configured",
410
+ "\u7B49\u5F85\u91CD\u8FDE": "Waiting to reconnect",
411
+ "\u51ED\u636E\u7F3A\u5931": "Credential missing",
412
+ "\u6700\u8FD1\u5FC3\u8DF3": "Last heartbeat",
413
+ "\u6700\u8FD1\u4E8B\u4EF6": "Last event",
414
+ "\u91CD\u8FDE\u6B21\u6570": "Reconnects",
415
+ "\u8FD0\u884C Job": "Running Jobs",
416
+ "\u5B8C\u6210 Job": "Completed Jobs",
417
+ "\u5C1A\u65E0": "None yet",
418
+ "\u8BBE\u5907\u8FDE\u63A5": "Device connection",
419
+ "Token \u53EA\u5199\u5165\u672C\u673A\u51ED\u636E\u5B58\u50A8": "Token is written only to the local credential store",
420
+ "\u7C98\u8D34 Office \u4E00\u6B21\u6027\u51ED\u636E": "Paste the one-time Office credential",
421
+ "\u5DF2\u5B89\u5168\u4FDD\u5B58\uFF1B\u7559\u7A7A\u4FDD\u6301\u4E0D\u53D8": "Stored securely; leave blank to keep it",
422
+ "\u6700\u5927\u5E76\u53D1": "Max concurrency",
423
+ "Heartbeat \u79D2\u6570": "Heartbeat seconds",
424
+ "Workspace \u6620\u5C04": "Workspace mappings",
425
+ "\u6BCF\u884C alias=/\u672C\u673A/\u7EDD\u5BF9\u8DEF\u5F84\uFF1BOffice \u53EA\u80FD\u770B\u5230 alias\u3002": "One alias=/local/absolute/path per line; Office sees only aliases.",
426
+ "Instruction Preset \u6620\u5C04": "Instruction preset mappings",
427
+ "\u6BCF\u884C alias=\u6307\u4EE4\uFF1B\u65B0\u589E preset \u4E0D\u9700\u8981\u6539 Office \u4EE3\u7801\u3002": "One alias=instruction per line; new presets require no Office code change.",
428
+ "\u4FDD\u5B58\u5E76\u8FDE\u63A5": "Save and connect",
429
+ "\u6D4B\u8BD5\u8FDE\u63A5": "Test connection",
430
+ "\u6D4B\u8BD5\u4E2D\u2026": "Testing\u2026",
431
+ "\u91CD\u65B0\u8FDE\u63A5": "Reconnect",
432
+ "\u79FB\u9664\u8FDE\u63A5": "Remove connection",
433
+ "\u8FDE\u63A5\u6D4B\u8BD5\u901A\u8FC7\u3002": "Connection test passed.",
434
+ "\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002": "Configuration saved.",
435
+ "\u534F\u8BAE Hook \u9884\u89C8": "Protocol hook preview",
436
+ "\u7531 Base URL \u81EA\u52A8\u6D3E\u751F\uFF0C\u4E0D\u5355\u72EC\u586B\u5199": "Derived from Base URL; no separate input",
437
+ "Base URL \u65E0\u6548": "Invalid Base URL",
438
+ "Office Hook \u5C1A\u672A\u90E8\u7F72\u65F6\uFF0C\u914D\u7F6E\u4F1A\u5B89\u5168\u4FDD\u5B58\u5E76\u81EA\u52A8\u91CD\u8BD5\uFF1B\u51FA\u73B0 HTTP 404 \u4EE3\u8868\u534F\u8BAE\u7AEF\u70B9\u5F85\u4E0A\u7EBF\uFF0C\u4E0D\u4EE3\u8868 Harness \u6545\u969C\u3002": "Configuration is saved and retried while Office hooks are unavailable; HTTP 404 means the protocol endpoint is pending, not a Harness failure.",
439
+ "Workspace \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value": "Each workspace mapping must use alias=value",
440
+ "Instruction Preset \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value": "Each instruction preset mapping must use alias=value",
441
+ "action-items=\u8F6C\u6362\u4E3A\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u548C\u9A8C\u6536\u660E\u786E\u7684\u5DE5\u5355": "action-items=Turn this into accountable tasks with deadlines and acceptance criteria",
442
+ "AI Office \u62D2\u7EDD\u4E86 Device Token\u3002": "AI Office rejected the Device Token.",
443
+ "AI Office Connector Hook \u5C1A\u672A\u5C31\u7EEA\u3002": "AI Office Connector hooks are not available yet.",
444
+ "AI Office Connector \u534F\u8BAE\u7248\u672C\u4E0D\u517C\u5BB9\u3002": "The AI Office Connector protocol is incompatible.",
445
+ "\u672C\u673A\u6682\u65F6\u65E0\u6CD5\u8BBF\u95EE AI Office\u3002": "AI Office cannot currently be reached from this machine.",
446
+ "AI Office \u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002": "The AI Office connection was interrupted.",
387
447
  "\u5E2E\u52A9\u4E0E\u53CD\u9988 \xB7 \u524D\u5F80 GitHub": "Help & feedback \xB7 Open GitHub",
388
448
  "\u5FAE\u4FE1": "WeChat",
389
449
  "\u98DE\u4E66": "Feishu",
@@ -561,6 +621,13 @@ var EN = Object.freeze({
561
621
  "\u8FD9\u662F\u5FAE\u4FE1\u9644\u52A0\u7684\u5B89\u5168\u786E\u8BA4\u6B65\u9AA4\u3002\u914D\u5BF9\u7801\u53EA\u7528\u4E8E\u672C\u6B21\u626B\u7801\u8F6E\u8BE2\uFF0C\u4E0D\u4F1A\u5199\u5165\u914D\u7F6E\u6216\u65E5\u5FD7\u3002": "This is an additional WeChat confirmation step. The pairing code is used only for this connection and is never stored.",
562
622
  "\u6B63\u5728\u4FDD\u5B58\u51ED\u636E\u5E76\u9A8C\u8BC1 Harness \u4E0E\u5FAE\u4FE1\u957F\u8F6E\u8BE2\u3002": "Saving credentials and verifying the WeChat connection.",
563
623
  "\u5FAE\u4FE1\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u52A8\u6D88\u606F\u8FDE\u63A5": "Confirmed in WeChat. Starting the message connection",
624
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u65E0\u6CD5\u8BFB\u53D6\u73B0\u6709\u767B\u5F55\u51ED\u636E\u3002\u8BF7\u68C0\u67E5 DSH \u51ED\u636E\u5B58\u50A8\u3002": "WeChat was authorized, but the existing login credential could not be read. Check the DSH credential store.",
625
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u767B\u5F55\u51ED\u636E\u65E0\u6CD5\u5199\u5165 DSH \u51ED\u636E\u5B58\u50A8\u3002\u8BF7\u68C0\u67E5\u51ED\u636E\u5B58\u50A8\u662F\u5426\u53EF\u5199\u3002": "WeChat was authorized, but the login credential could not be written to the DSH credential store. Check that the store is writable.",
626
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u8D26\u53F7\u914D\u7F6E\u65E0\u6CD5\u5199\u5165\u672C\u673A\u3002\u8BF7\u68C0\u67E5 DSH_HOME \u76EE\u5F55\u6743\u9650\u3002": "WeChat was authorized, but the account configuration could not be saved locally. Check the DSH_HOME directory permissions.",
627
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u65E0\u6CD5\u521D\u59CB\u5316\u8D26\u53F7\u72B6\u6001\u6216\u5DE5\u4F5C\u533A\u3002\u8BF7\u68C0\u67E5 DSH_HOME \u548C\u5DE5\u4F5C\u533A\u76EE\u5F55\u3002": "WeChat was authorized, but the account state or workspace could not be initialized. Check DSH_HOME and the workspace directory.",
628
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u63D2\u4EF6\u65E0\u6CD5\u8FDE\u63A5\u672C\u673A Harness\u3002\u8BF7\u786E\u8BA4 dsh web \u5DF2\u6B63\u5E38\u542F\u52A8\u3002": "WeChat was authorized, but the plugin could not reach the local Harness. Confirm that dsh web is running normally.",
629
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u6D88\u606F\u8FDE\u63A5\u521D\u59CB\u5316\u5931\u8D25\u3002\u8BF7\u67E5\u770B dsh web \u65E5\u5FD7\u540E\u91CD\u8BD5\u3002": "WeChat was authorized, but the message connection could not be initialized. Check the dsh web logs and try again.",
630
+ "\u5FAE\u4FE1\u5DF2\u6388\u6743\uFF0C\u4F46\u6FC0\u6D3B\u8FC7\u7A0B\u4E2D\u53D1\u751F\u672A\u77E5\u9519\u8BEF\u3002\u8BF7\u67E5\u770B dsh web \u65E5\u5FD7\u3002": "WeChat was authorized, but an unknown error occurred during activation. Check the dsh web logs.",
564
631
  "\u5FAE\u4FE1\u5DF2\u7ED1\u5B9A\uFF0C\u53EF\u4EE5\u5F00\u59CB\u5411\u5DF2\u7ED1\u5B9A\u7684\u673A\u5668\u4EBA\u53D1\u6D88\u606F\u3002": "WeChat is connected and ready for messages.",
565
632
  "\u8FD9\u4E2A\u5FAE\u4FE1\u8D26\u53F7\u5DF2\u7ECF\u7ED1\u5B9A\u5E76\u4FDD\u6301\u5728\u7EBF\u3002": "This WeChat account is connected and online.",
566
633
  "\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002": "The WeChat account and local credentials were removed.",
@@ -612,6 +679,29 @@ var EN = Object.freeze({
612
679
  "\u63A5\u5165 Telegram \u673A\u5668\u4EBA": "Connect a Telegram bot",
613
680
  "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Get a Bot Token from @BotFather, then connect it here.",
614
681
  "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token": "Enter the Bot Token from @BotFather",
682
+ "\u8BBF\u95EE\u6A21\u5F0F": "Access mode",
683
+ "\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
684
+ "Telegram \u8BBF\u95EE\u6A21\u5F0F": "Telegram access mode",
685
+ "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E": "View Telegram access mode details",
686
+ "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002": "All group messages are ignored; only allowlisted users may send DMs.",
687
+ "\u4FDD\u6301\u539F\u6709\u884C\u4E3A\uFF1A\u79C1\u804A\u76F4\u63A5\u54CD\u5E94\uFF0C\u7FA4\u804A\u5728\u88AB\u63D0\u53CA\u6216\u56DE\u590D\u65F6\u54CD\u5E94\u3002": "Keep the original behavior: respond to DMs and to group mentions or replies.",
688
+ "\u5B89\u5168\u6A21\u5F0F": "Safe mode",
689
+ "\u517C\u5BB9\u6A21\u5F0F": "Compatible mode",
690
+ "\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F": "Active: Safe mode",
691
+ "\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F": "Active: Compatible mode",
692
+ "\u6A21\u5F0F": "Mode",
693
+ "\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09": "Compatible mode (default)",
694
+ "\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09": "Safe mode (private-chat allowlist)",
695
+ "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID": "Telegram User IDs allowed to send DMs",
696
+ "\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID": "One numeric User ID per line",
697
+ "\u767D\u540D\u5355\u4EC5\u5C5E\u4E8E\u5F53\u524D\u673A\u5668\u4EBA\u3002": "This allowlist belongs only to the current bot.",
698
+ "\u517C\u5BB9\u6A21\u5F0F\u4E0B\u6682\u4E0D\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002": "Compatible mode does not enforce the allowlist; it is retained when modes change.",
699
+ "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002": "The allowlist is empty; this bot will reject all inbound messages after saving.",
700
+ "\u6B63\u5728\u4FDD\u5B58\u2026": "Saving\u2026",
701
+ "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E": "Save access settings",
702
+ "User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002": "Each User ID must be a 1\u201316 digit positive integer on its own line.",
703
+ "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Telegram access settings are currently unavailable.",
704
+ "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002": "Could not save Telegram access settings.",
615
705
  "\u63A5\u5165 Discord \u673A\u5668\u4EBA": "Connect a Discord bot",
616
706
  "\u5148\u5728 Developer Portal \u521B\u5EFA Bot \u5E76\u9080\u8BF7\u5230\u670D\u52A1\u5668\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Create a bot in the Developer Portal and invite it to your server, then connect it here.",
617
707
  "\u586B\u5199 Discord Developer Portal \u7684 Bot Token": "Enter the Bot Token from the Discord Developer Portal",
@@ -1773,7 +1863,7 @@ function AccountCard({
1773
1863
  }) {
1774
1864
  const state = busy === "reconnect" ? "connecting" : account.state;
1775
1865
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
1776
- const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
1866
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
1777
1867
  const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
1778
1868
  return h2(
1779
1869
  "article",
@@ -1799,7 +1889,7 @@ function AccountCard({
1799
1889
  "div",
1800
1890
  { className: "ddt-health dim-botHealth" },
1801
1891
  h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
1802
- h2("span", null, stateLabel)
1892
+ h2("span", null, stateLabel2)
1803
1893
  )
1804
1894
  ),
1805
1895
  h2(
@@ -2405,7 +2495,9 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
2405
2495
  deleteBot: "bot.delete",
2406
2496
  setWorkspace: "bot.workspace.set"
2407
2497
  });
2408
- function createTokenChannelApi(channel4, connectionSummary) {
2498
+ function createTokenChannelApi(channel4, connectionSummary, {
2499
+ normalizeBotExtension = () => ({})
2500
+ } = {}) {
2409
2501
  const unwrapRpcResult10 = (result) => {
2410
2502
  if (!isRecord2(result) || typeof result.ok !== "boolean") {
2411
2503
  throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
@@ -2421,6 +2513,7 @@ function createTokenChannelApi(channel4, connectionSummary) {
2421
2513
  if (!isRecord2(value) || !id(value.botId)) return void 0;
2422
2514
  const connected = value.connected === true;
2423
2515
  const state = ACCOUNT_STATES2.has(value.state) ? value.state : "offline";
2516
+ const extension = normalizeBotExtension(value);
2424
2517
  return {
2425
2518
  botId: id(value.botId),
2426
2519
  connected,
@@ -2441,7 +2534,8 @@ function createTokenChannelApi(channel4, connectionSummary) {
2441
2534
  error: isRecord2(value.error) ? {
2442
2535
  code: text(value.error.code, `${channel4.toUpperCase()}_ACCOUNT_ERROR`, 80),
2443
2536
  message: text(value.error.message, `${channel4}\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA`)
2444
- } : null
2537
+ } : null,
2538
+ ...isRecord2(extension) ? extension : {}
2445
2539
  };
2446
2540
  };
2447
2541
  const normalizeSnapshot9 = (value) => {
@@ -2521,12 +2615,14 @@ function createTokenChannelSettings(definition) {
2521
2615
  credentialOpenLabel = "\u624B\u52A8\u63A5\u5165",
2522
2616
  credentialCloseLabel = "\u6536\u8D77\u51ED\u636E",
2523
2617
  credentialNoun = "Bot Token",
2524
- emptyActionLabel = "\u586B\u5199 Bot Token"
2618
+ emptyActionLabel = "\u586B\u5199 Bot Token",
2619
+ AccountSettings = null,
2620
+ accountSettingsEndpoint = null
2525
2621
  } = definition;
2526
- function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2622
+ function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2527
2623
  const state = busy === "reconnect" ? "connecting" : account.state;
2528
2624
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
2529
- const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
2625
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
2530
2626
  const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
2531
2627
  const identity = account.bot.username ? `@${account.bot.username}` : account.bot.idMasked;
2532
2628
  return h2(
@@ -2557,7 +2653,7 @@ function createTokenChannelSettings(definition) {
2557
2653
  "div",
2558
2654
  { className: "ddt-health dim-botHealth" },
2559
2655
  h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
2560
- h2("span", null, stateLabel)
2656
+ h2("span", null, stateLabel2)
2561
2657
  )
2562
2658
  ),
2563
2659
  h2(
@@ -2581,6 +2677,11 @@ function createTokenChannelSettings(definition) {
2581
2677
  disabled: Boolean(busy),
2582
2678
  onSave: onWorkspaceSave
2583
2679
  }),
2680
+ AccountSettings ? h2(AccountSettings, {
2681
+ account,
2682
+ busy: Boolean(busy),
2683
+ onSave: onAccountSettingsSave
2684
+ }) : null,
2584
2685
  h2(
2585
2686
  "div",
2586
2687
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -2765,6 +2866,12 @@ function createTokenChannelSettings(definition) {
2765
2866
  endpoints.setWorkspace,
2766
2867
  { botId: account.botId, workspace }
2767
2868
  ),
2869
+ onAccountSettingsSave: AccountSettings && accountSettingsEndpoint ? (payload) => botAction(
2870
+ account,
2871
+ "settings",
2872
+ accountSettingsEndpoint,
2873
+ { botId: account.botId, ...payload }
2874
+ ) : void 0,
2768
2875
  onRequestRemove: () => setRemoveTarget(account.botId),
2769
2876
  onCancelRemove: () => setRemoveTarget(null),
2770
2877
  onConfirmRemove: async () => {
@@ -5148,7 +5255,7 @@ function AccountCard2({
5148
5255
  onCancelRemove
5149
5256
  }) {
5150
5257
  const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
5151
- const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
5258
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
5152
5259
  const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
5153
5260
  return h2(
5154
5261
  "article",
@@ -5174,7 +5281,7 @@ function AccountCard2({
5174
5281
  "div",
5175
5282
  { className: "ddt-health dim-botHealth" },
5176
5283
  h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
5177
- h2("span", null, stateLabel)
5284
+ h2("span", null, stateLabel2)
5178
5285
  )
5179
5286
  ),
5180
5287
  h2(
@@ -5490,6 +5597,350 @@ function QqSettingsTab({ rpcCall }) {
5490
5597
  );
5491
5598
  }
5492
5599
 
5600
+ // src/channels/office/protocol.mjs
5601
+ var OFFICE_PROTOCOL_VERSION = "office-harness.v1";
5602
+ var OFFICE_RPC_CHANNEL = "/office";
5603
+ var OFFICE_RPC_ENDPOINTS = Object.freeze({
5604
+ status: "connection.status",
5605
+ configure: "connector.configure",
5606
+ reconnect: "connector.reconnect",
5607
+ test: "connector.test",
5608
+ remove: "connector.remove"
5609
+ });
5610
+ var OFFICE_HOOK_PATHS = Object.freeze({
5611
+ stream: "/api/harness/connector/stream",
5612
+ heartbeat: "/api/harness/connector/heartbeat",
5613
+ job: "/api/harness/connector/jobs/:id",
5614
+ accept: "/api/harness/connector/jobs/:id/accept",
5615
+ renew: "/api/harness/connector/jobs/:id/renew",
5616
+ progress: "/api/harness/connector/jobs/:id/progress",
5617
+ approval: "/api/harness/connector/jobs/:id/approval",
5618
+ result: "/api/harness/connector/jobs/:id/result",
5619
+ fail: "/api/harness/connector/jobs/:id/fail"
5620
+ });
5621
+ function normalizeOfficeBaseUrl(value) {
5622
+ const url = new URL(typeof value === "string" ? value.trim() : "");
5623
+ const localHttp = url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
5624
+ if (url.protocol !== "https:" && !localHttp) {
5625
+ throw new TypeError("AI Office URL must use HTTPS (HTTP is allowed only for loopback testing)");
5626
+ }
5627
+ if (url.username || url.password || url.search || url.hash) {
5628
+ throw new TypeError("AI Office URL must be a bare origin");
5629
+ }
5630
+ url.pathname = "/";
5631
+ return url;
5632
+ }
5633
+ function officeHookUrls(baseUrl) {
5634
+ const origin = normalizeOfficeBaseUrl(baseUrl);
5635
+ return Object.fromEntries(Object.entries(OFFICE_HOOK_PATHS).map(([name2, path]) => [
5636
+ name2,
5637
+ new URL(path, origin).toString()
5638
+ ]));
5639
+ }
5640
+
5641
+ // plugin-src/client/channels/office/api.js
5642
+ function record(value) {
5643
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5644
+ }
5645
+ function unwrapOfficeRpc(result) {
5646
+ if (!record(result) || typeof result.ok !== "boolean") throw new Error("AI Office \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
5647
+ if (!result.ok) {
5648
+ const error = new Error(typeof result.error?.message === "string" ? result.error.message : "AI Office \u64CD\u4F5C\u5931\u8D25");
5649
+ error.code = typeof result.error?.code === "string" ? result.error.code : "office-rpc-error";
5650
+ throw error;
5651
+ }
5652
+ return result.value;
5653
+ }
5654
+ function normalizeOfficeStatus(value) {
5655
+ if (!record(value) || value.configured !== true) {
5656
+ return { configured: false, connected: false, state: "unconfigured", config: null, health: null };
5657
+ }
5658
+ const config = record(value.config) ? value.config : {};
5659
+ return {
5660
+ configured: true,
5661
+ connected: value.connected === true,
5662
+ state: typeof value.state === "string" ? value.state : "idle",
5663
+ tokenConfigured: value.tokenConfigured === true,
5664
+ config: {
5665
+ protocolVersion: config.protocolVersion ?? OFFICE_PROTOCOL_VERSION,
5666
+ baseUrl: typeof config.baseUrl === "string" ? config.baseUrl : "",
5667
+ deviceId: typeof config.deviceId === "string" ? config.deviceId : "",
5668
+ maxConcurrency: Number(config.maxConcurrency ?? 1),
5669
+ heartbeatSeconds: Number(config.heartbeatSeconds ?? 30),
5670
+ workspaces: record(config.workspaces) ? config.workspaces : {},
5671
+ instructionPresets: record(config.instructionPresets) ? config.instructionPresets : {},
5672
+ hooks: record(config.hooks) ? config.hooks : {}
5673
+ },
5674
+ health: record(value.health) ? value.health : null
5675
+ };
5676
+ }
5677
+
5678
+ // plugin-src/client/channels/office/index.js
5679
+ var React12 = __toESM(require("react"), 1);
5680
+ function Button9({ children, kind = "secondary", ...props }) {
5681
+ return h2("button", { ...props, type: "button", className: "ddt-button", "data-kind": kind }, children);
5682
+ }
5683
+ function mapText(value) {
5684
+ return Object.entries(value ?? {}).map(([key, item]) => `${key}=${item}`).join("\n");
5685
+ }
5686
+ function parseMap(value, label) {
5687
+ const output = {};
5688
+ for (const raw of value.split(/\r?\n/)) {
5689
+ const line = raw.trim();
5690
+ if (!line) continue;
5691
+ const index = line.indexOf("=");
5692
+ if (index < 1 || !line.slice(index + 1).trim()) {
5693
+ throw new Error(label === "Workspace \u6620\u5C04" ? "Workspace \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value" : "Instruction Preset \u6620\u5C04\u6BCF\u884C\u5FC5\u987B\u4F7F\u7528 alias=value");
5694
+ }
5695
+ output[line.slice(0, index).trim()] = line.slice(index + 1).trim();
5696
+ }
5697
+ return output;
5698
+ }
5699
+ function stateLabel(model) {
5700
+ if (model.connected) return "\u5DF2\u8FDE\u63A5 Office";
5701
+ if (!model.configured) return "\u5C1A\u672A\u914D\u7F6E";
5702
+ if (model.state === "connecting") return "\u6B63\u5728\u8FDE\u63A5";
5703
+ if (model.state === "reconnecting") return "\u7B49\u5F85\u91CD\u8FDE";
5704
+ if (model.state === "missing-token") return "\u51ED\u636E\u7F3A\u5931";
5705
+ return "\u5DF2\u914D\u7F6E";
5706
+ }
5707
+ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5708
+ const [model, setModel] = React12.useState(normalizeOfficeStatus(initialStatus));
5709
+ const [phase, setPhase] = React12.useState(initialStatus === void 0 ? "loading" : "ready");
5710
+ const [busy, setBusy] = React12.useState("");
5711
+ const [error, setError] = React12.useState("");
5712
+ const [notice, setNotice] = React12.useState("");
5713
+ const [form, setForm] = React12.useState({
5714
+ baseUrl: "",
5715
+ deviceId: "local-harness",
5716
+ deviceToken: "",
5717
+ maxConcurrency: "1",
5718
+ heartbeatSeconds: "30",
5719
+ workspaces: "",
5720
+ instructionPresets: ""
5721
+ });
5722
+ const invoke = React12.useCallback(async (endpoint, payload = {}) => {
5723
+ if (typeof rpcCall !== "function") throw new Error("AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
5724
+ return unwrapOfficeRpc(await rpcCall(endpoint, payload));
5725
+ }, [rpcCall]);
5726
+ const adopt = React12.useCallback((value) => {
5727
+ const next = normalizeOfficeStatus(value?.snapshot ?? value);
5728
+ setModel(next);
5729
+ if (next.config) setForm((current) => ({
5730
+ ...current,
5731
+ baseUrl: next.config.baseUrl,
5732
+ deviceId: next.config.deviceId,
5733
+ maxConcurrency: String(next.config.maxConcurrency),
5734
+ heartbeatSeconds: String(next.config.heartbeatSeconds),
5735
+ workspaces: mapText(next.config.workspaces),
5736
+ instructionPresets: mapText(next.config.instructionPresets),
5737
+ deviceToken: ""
5738
+ }));
5739
+ return next;
5740
+ }, []);
5741
+ const load = React12.useCallback(async () => {
5742
+ try {
5743
+ adopt(await invoke(OFFICE_RPC_ENDPOINTS.status));
5744
+ setPhase("ready");
5745
+ setError("");
5746
+ } catch (caught) {
5747
+ setPhase("error");
5748
+ setError(caught.message);
5749
+ }
5750
+ }, [adopt, invoke]);
5751
+ React12.useEffect(() => {
5752
+ void load();
5753
+ }, [load]);
5754
+ const run = async (name2, operation) => {
5755
+ setBusy(name2);
5756
+ setError("");
5757
+ setNotice("");
5758
+ try {
5759
+ const value = await operation();
5760
+ adopt(value);
5761
+ setNotice(name2 === "test" ? "\u8FDE\u63A5\u6D4B\u8BD5\u901A\u8FC7\u3002" : "\u914D\u7F6E\u5DF2\u4FDD\u5B58\u3002");
5762
+ } catch (caught) {
5763
+ setError(caught.message);
5764
+ } finally {
5765
+ setBusy("");
5766
+ }
5767
+ };
5768
+ const hooks = React12.useMemo(() => {
5769
+ try {
5770
+ return officeHookUrls(form.baseUrl);
5771
+ } catch {
5772
+ return {};
5773
+ }
5774
+ }, [form.baseUrl]);
5775
+ const health = model.health ?? {};
5776
+ if (phase === "loading") return h2("div", { className: "ddt-card ddt-loading", "aria-busy": "true" }, "\u6B63\u5728\u8BFB\u53D6 AI Office Connector\u2026");
5777
+ return h2(
5778
+ "section",
5779
+ { className: "dof-page", "aria-label": "AI Office \u8BBE\u7F6E" },
5780
+ h2(
5781
+ "div",
5782
+ { className: "dof-hero" },
5783
+ h2(
5784
+ "div",
5785
+ { className: "dof-heroCopy" },
5786
+ h2("h3", null, "AI Office Connector"),
5787
+ h2("p", null, "\u672C\u673A\u4E3B\u52A8\u8FDE\u63A5\u516C\u7F51 Office\uFF1BHarness \u4E0D\u5F00\u653E\u7AEF\u53E3\u3002\u534F\u8BAE Hook \u56FA\u5B9A\u4E3A ", OFFICE_PROTOCOL_VERSION, "\u3002")
5788
+ ),
5789
+ h2(
5790
+ "span",
5791
+ { className: "dof-status", "data-connected": String(model.connected) },
5792
+ h2("span", { className: "dof-dot" }),
5793
+ stateLabel(model)
5794
+ )
5795
+ ),
5796
+ model.configured ? h2(
5797
+ "div",
5798
+ { className: "dof-metrics" },
5799
+ h2("div", { className: "dof-metric" }, h2("span", null, "\u6700\u8FD1\u5FC3\u8DF3"), h2("strong", null, health.lastHeartbeatAt ?? "\u5C1A\u65E0")),
5800
+ h2("div", { className: "dof-metric" }, h2("span", null, "\u6700\u8FD1\u4E8B\u4EF6"), h2("strong", null, health.lastEventType ?? "\u5C1A\u65E0")),
5801
+ h2("div", { className: "dof-metric" }, h2("span", null, "\u91CD\u8FDE\u6B21\u6570"), h2("strong", null, String(health.reconnects ?? 0))),
5802
+ h2("div", { className: "dof-metric" }, h2("span", null, "Job Offer"), h2("strong", null, String(health.jobsOffered ?? 0))),
5803
+ h2("div", { className: "dof-metric" }, h2("span", null, "\u8FD0\u884C Job"), h2("strong", null, String(health.jobs?.running ?? 0))),
5804
+ h2("div", { className: "dof-metric" }, h2("span", null, "\u5B8C\u6210 Job"), h2("strong", null, String(health.jobs?.completed ?? 0)))
5805
+ ) : null,
5806
+ h2(
5807
+ "div",
5808
+ { className: "dof-card" },
5809
+ h2("div", { className: "dof-cardTitle" }, h2("h4", null, "\u8BBE\u5907\u8FDE\u63A5"), h2("span", null, "Token \u53EA\u5199\u5165\u672C\u673A\u51ED\u636E\u5B58\u50A8")),
5810
+ h2(
5811
+ "div",
5812
+ { className: "dof-grid" },
5813
+ h2(
5814
+ "label",
5815
+ { className: "dof-field", "data-wide": "true" },
5816
+ "Office Base URL",
5817
+ h2("input", { value: form.baseUrl, placeholder: "https://office.example.com", onChange: (event) => setForm({ ...form, baseUrl: event.target.value }) })
5818
+ ),
5819
+ h2(
5820
+ "label",
5821
+ { className: "dof-field" },
5822
+ "Device ID",
5823
+ h2("input", { value: form.deviceId, placeholder: "local-harness", onChange: (event) => setForm({ ...form, deviceId: event.target.value }) })
5824
+ ),
5825
+ h2(
5826
+ "label",
5827
+ { className: "dof-field" },
5828
+ "Device Token",
5829
+ h2("input", { type: "password", value: form.deviceToken, placeholder: model.tokenConfigured ? "\u5DF2\u5B89\u5168\u4FDD\u5B58\uFF1B\u7559\u7A7A\u4FDD\u6301\u4E0D\u53D8" : "\u7C98\u8D34 Office \u4E00\u6B21\u6027\u51ED\u636E", autoComplete: "new-password", onChange: (event) => setForm({ ...form, deviceToken: event.target.value }) })
5830
+ ),
5831
+ h2(
5832
+ "label",
5833
+ { className: "dof-field" },
5834
+ "\u6700\u5927\u5E76\u53D1",
5835
+ h2("input", { type: "number", min: 1, max: 4, value: form.maxConcurrency, onChange: (event) => setForm({ ...form, maxConcurrency: event.target.value }) })
5836
+ ),
5837
+ h2(
5838
+ "label",
5839
+ { className: "dof-field" },
5840
+ "Heartbeat \u79D2\u6570",
5841
+ h2("input", { type: "number", min: 10, max: 300, value: form.heartbeatSeconds, onChange: (event) => setForm({ ...form, heartbeatSeconds: event.target.value }) })
5842
+ ),
5843
+ h2(
5844
+ "label",
5845
+ { className: "dof-field", "data-wide": "true" },
5846
+ "Workspace \u6620\u5C04",
5847
+ h2("textarea", { value: form.workspaces, placeholder: "office-project=/Users/you/projects/ai-office", onChange: (event) => setForm({ ...form, workspaces: event.target.value }) }),
5848
+ h2("small", null, "\u6BCF\u884C alias=/\u672C\u673A/\u7EDD\u5BF9\u8DEF\u5F84\uFF1BOffice \u53EA\u80FD\u770B\u5230 alias\u3002")
5849
+ ),
5850
+ h2(
5851
+ "label",
5852
+ { className: "dof-field", "data-wide": "true" },
5853
+ "Instruction Preset \u6620\u5C04",
5854
+ h2("textarea", { value: form.instructionPresets, placeholder: "action-items=\u8F6C\u6362\u4E3A\u8D1F\u8D23\u4EBA\u3001\u622A\u6B62\u548C\u9A8C\u6536\u660E\u786E\u7684\u5DE5\u5355", onChange: (event) => setForm({ ...form, instructionPresets: event.target.value }) }),
5855
+ h2("small", null, "\u6BCF\u884C alias=\u6307\u4EE4\uFF1B\u65B0\u589E preset \u4E0D\u9700\u8981\u6539 Office \u4EE3\u7801\u3002")
5856
+ )
5857
+ ),
5858
+ error ? h2("p", { className: "dof-error", role: "alert" }, error) : null,
5859
+ notice ? h2("p", { className: "dof-notice", role: "status" }, notice) : null,
5860
+ health.error?.message ? h2("p", { className: "dof-error" }, health.error.message) : null,
5861
+ h2(
5862
+ "div",
5863
+ { className: "dof-actions" },
5864
+ h2(Button9, { kind: "primary", disabled: Boolean(busy), onClick: () => void run("save", () => invoke(OFFICE_RPC_ENDPOINTS.configure, {
5865
+ baseUrl: form.baseUrl,
5866
+ deviceId: form.deviceId,
5867
+ ...form.deviceToken ? { deviceToken: form.deviceToken } : {},
5868
+ maxConcurrency: Number(form.maxConcurrency),
5869
+ heartbeatSeconds: Number(form.heartbeatSeconds),
5870
+ workspaces: parseMap(form.workspaces, "Workspace \u6620\u5C04"),
5871
+ instructionPresets: parseMap(form.instructionPresets, "Instruction Preset \u6620\u5C04")
5872
+ })) }, busy === "save" ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5E76\u8FDE\u63A5"),
5873
+ h2(Button9, { disabled: !model.configured || Boolean(busy), onClick: () => void run("test", () => invoke(OFFICE_RPC_ENDPOINTS.test)) }, busy === "test" ? "\u6D4B\u8BD5\u4E2D\u2026" : "\u6D4B\u8BD5\u8FDE\u63A5"),
5874
+ h2(Button9, { disabled: !model.configured || Boolean(busy), onClick: () => void run("reconnect", () => invoke(OFFICE_RPC_ENDPOINTS.reconnect)) }, "\u91CD\u65B0\u8FDE\u63A5"),
5875
+ h2(Button9, { kind: "danger", disabled: !model.configured || Boolean(busy), onClick: () => void run("remove", () => invoke(OFFICE_RPC_ENDPOINTS.remove, { confirm: true })) }, "\u79FB\u9664\u8FDE\u63A5")
5876
+ )
5877
+ ),
5878
+ h2(
5879
+ "div",
5880
+ { className: "dof-card" },
5881
+ h2("div", { className: "dof-cardTitle" }, h2("h4", null, "\u534F\u8BAE Hook \u9884\u89C8"), h2("span", null, "\u7531 Base URL \u81EA\u52A8\u6D3E\u751F\uFF0C\u4E0D\u5355\u72EC\u586B\u5199")),
5882
+ h2(
5883
+ "div",
5884
+ { className: "dof-hooks" },
5885
+ [["SSE", hooks.stream], ["Heartbeat", hooks.heartbeat], ["Job", hooks.job], ["Result", hooks.result]].map(([label, url]) => h2("div", { className: "dof-hook", key: label }, h2("strong", null, label), h2("code", null, url ?? "Base URL \u65E0\u6548")))
5886
+ )
5887
+ ),
5888
+ h2("p", { className: "dof-notice" }, "Office Hook \u5C1A\u672A\u90E8\u7F72\u65F6\uFF0C\u914D\u7F6E\u4F1A\u5B89\u5168\u4FDD\u5B58\u5E76\u81EA\u52A8\u91CD\u8BD5\uFF1B\u51FA\u73B0 HTTP 404 \u4EE3\u8868\u534F\u8BAE\u7AEF\u70B9\u5F85\u4E0A\u7EBF\uFF0C\u4E0D\u4EE3\u8868 Harness \u6545\u969C\u3002")
5889
+ );
5890
+ }
5891
+
5892
+ // plugin-src/client/channels/office/styles.js
5893
+ var OFFICE_STYLE_ID = "xmanrui-dsh-im-office-settings";
5894
+ var CSS5 = `
5895
+ .dof-page { --dof-accent: var(--dsw-alias-brand-primary, #3964fe); }
5896
+ .dof-hero { position: relative; overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px; align-items: center; margin-bottom: 12px; padding: 18px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 16px; background: linear-gradient(135deg, color-mix(in srgb, var(--dof-accent) 9%, var(--dsw-alias-bg-layer-1, #fff)), var(--dsw-alias-bg-layer-1, #fff) 62%); }
5897
+ .dof-hero::after { content: ""; position: absolute; width: 150px; height: 150px; right: -75px; top: -90px; border: 24px solid color-mix(in srgb, var(--dof-accent) 12%, transparent); border-radius: 50%; pointer-events: none; }
5898
+ .dof-heroCopy { min-width: 0; }
5899
+ .dof-heroCopy h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: 1.35; }
5900
+ .dof-heroCopy p { margin: 6px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.6; }
5901
+ .dof-status { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 7px; padding: 7px 10px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 999px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; white-space: nowrap; }
5902
+ .dof-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-state-warn-primary, #d97706); }
5903
+ .dof-status[data-connected="true"] .dof-dot { 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); }
5904
+ .dof-card { margin-top: 10px; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); }
5905
+ .dof-cardTitle { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; margin-bottom: 12px; }
5906
+ .dof-cardTitle h4 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; }
5907
+ .dof-cardTitle span { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; }
5908
+ .dof-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
5909
+ .dof-field { min-width: 0; display: flex; flex-direction: column; gap: 6px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
5910
+ .dof-field[data-wide="true"] { grid-column: 1 / -1; }
5911
+ .dof-field input, .dof-field textarea { box-sizing: border-box; width: 100%; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-1, #fff); color: var(--dsw-alias-label-primary, #1f2329); font: inherit; font-size: 13px; line-height: 1.5; outline: none; }
5912
+ .dof-field input { height: 38px; padding: 0 11px; }
5913
+ .dof-field textarea { min-height: 86px; resize: vertical; padding: 9px 11px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
5914
+ .dof-field input:focus, .dof-field textarea:focus { border-color: var(--dof-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dof-accent) 12%, transparent); }
5915
+ .dof-field small { color: var(--dsw-alias-label-tertiary, #8f959e); line-height: 1.45; }
5916
+ .dof-hooks { display: grid; gap: 7px; }
5917
+ .dof-hook { min-width: 0; display: grid; grid-template-columns: 82px minmax(0, 1fr); gap: 10px; align-items: center; padding: 8px 10px; border-radius: 9px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
5918
+ .dof-hook strong { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; }
5919
+ .dof-hook code { overflow: hidden; color: var(--dsw-alias-label-primary, #1f2329); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
5920
+ .dof-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
5921
+ .dof-actions .ddt-button[data-kind="primary"] { color: #fff; border-color: var(--dof-accent); background: var(--dof-accent); }
5922
+ .dof-error, .dof-notice { margin: 10px 0 0; padding: 9px 11px; border-radius: 9px; font-size: 12px; line-height: 1.5; }
5923
+ .dof-error { color: var(--dsw-alias-state-error-primary, #d54941); background: var(--dsw-alias-state-error-secondary, #fff0ef); }
5924
+ .dof-notice { color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
5925
+ .dof-metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }
5926
+ .dof-metric { min-width: 0; padding: 9px; border-radius: 10px; background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
5927
+ .dof-metric span { display: block; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 10px; }
5928
+ .dof-metric strong { display: block; overflow: hidden; margin-top: 4px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
5929
+ @container (max-width: 680px) { .dof-grid { grid-template-columns: minmax(0, 1fr); } .dof-field[data-wide="true"] { grid-column: auto; } .dof-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
5930
+ @media (prefers-reduced-motion: reduce) { .dof-page * { transition: none !important; } }
5931
+ `;
5932
+ function installOfficeStyles() {
5933
+ if (typeof document === "undefined") return () => {
5934
+ };
5935
+ if (document.querySelector(`style[data-plugin-css="${OFFICE_STYLE_ID}"]`)) return () => {
5936
+ };
5937
+ const style = document.createElement("style");
5938
+ style.dataset.pluginCss = OFFICE_STYLE_ID;
5939
+ style.textContent = CSS5;
5940
+ document.head.append(style);
5941
+ return () => style.remove();
5942
+ }
5943
+
5493
5944
  // plugin-src/client/channels/slack/api.js
5494
5945
  var SLACK_RPC_CHANNEL = "/slack";
5495
5946
  var SLACK_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
@@ -5499,7 +5950,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
5499
5950
  var presentError5 = api2.presentError;
5500
5951
 
5501
5952
  // plugin-src/client/channels/slack/index.js
5502
- var React12 = __toESM(require("react"), 1);
5953
+ var React13 = __toESM(require("react"), 1);
5503
5954
 
5504
5955
  // src/channels/slack/manifest.mjs
5505
5956
  var SLACK_APP_MANIFEST_YAML = `_metadata:
@@ -5536,7 +5987,7 @@ var SLACK_CREATE_APP_URL = "https://api.slack.com/apps?new_app=1";
5536
5987
 
5537
5988
  // plugin-src/client/channels/slack/styles.js
5538
5989
  var SLACK_STYLE_ID = "xmanrui-dsh-im-slack-settings";
5539
- var CSS5 = String.raw`
5990
+ var CSS6 = String.raw`
5540
5991
  .dsl-page { --ddt-accent: #4a154b; --ddt-accent-deep: #321033; --ddt-accent-wash: #f7eef7; }
5541
5992
  .dsl-avatar { color: #fff; background: #4a154b; }
5542
5993
  .dsl-avatar svg { display: block; }
@@ -5565,17 +6016,17 @@ function installSlackStyles() {
5565
6016
  const style = document.createElement("style");
5566
6017
  style.dataset.plugin = "@xmanrui/dsh-im";
5567
6018
  style.dataset.pluginCss = SLACK_STYLE_ID;
5568
- style.textContent = CSS5;
6019
+ style.textContent = CSS6;
5569
6020
  document.head.appendChild(style);
5570
6021
  return () => style.remove();
5571
6022
  }
5572
6023
 
5573
6024
  // plugin-src/client/channels/slack/index.js
5574
6025
  function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
5575
- const [botToken, setBotToken] = React12.useState("");
5576
- const [appToken, setAppToken] = React12.useState("");
5577
- const [copied, setCopied] = React12.useState(false);
5578
- const headingId = React12.useId();
6026
+ const [botToken, setBotToken] = React13.useState("");
6027
+ const [appToken, setAppToken] = React13.useState("");
6028
+ const [copied, setCopied] = React13.useState(false);
6029
+ const headingId = React13.useId();
5579
6030
  const copyManifest = async () => {
5580
6031
  try {
5581
6032
  await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
@@ -5714,18 +6165,57 @@ var SlackAccountCard = channel2.AccountCard;
5714
6165
 
5715
6166
  // plugin-src/client/channels/telegram/api.js
5716
6167
  var TELEGRAM_RPC_CHANNEL = "/telegram";
5717
- var TELEGRAM_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
5718
- var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2");
6168
+ var TELEGRAM_ENDPOINTS = Object.freeze({
6169
+ ...TOKEN_BOT_ENDPOINTS,
6170
+ setAccessPolicy: "bot.access-policy.set"
6171
+ });
6172
+ var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2", {
6173
+ normalizeBotExtension: (value) => {
6174
+ const source = value?.accessPolicy;
6175
+ const accessMode = source?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible";
6176
+ const allowedUsers = Array.isArray(source?.allowedUsers) ? [...new Set(source.allowedUsers.filter((entry) => typeof entry === "string" && /^[1-9]\d{0,15}$/.test(entry)))] : [];
6177
+ return { accessPolicy: { accessMode, allowedUsers } };
6178
+ }
6179
+ });
5719
6180
  var unwrapRpcResult6 = api3.unwrapRpcResult;
5720
6181
  var normalizeSnapshot5 = api3.normalizeSnapshot;
5721
6182
  var presentError6 = api3.presentError;
5722
6183
 
6184
+ // plugin-src/client/channels/telegram/index.js
6185
+ var React14 = __toESM(require("react"), 1);
6186
+
5723
6187
  // plugin-src/client/channels/telegram/styles.js
5724
6188
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
5725
- var CSS6 = String.raw`
6189
+ var CSS7 = String.raw`
5726
6190
  .dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
5727
6191
  .dtg-avatar { color: #fff; background: #229ed9; }
5728
6192
  .dtg-avatar svg { display: block; }
6193
+ .dtg-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
6194
+ .dtg-accessHeading { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
6195
+ .dtg-accessHeading > strong { font-size: 13px; }
6196
+ .dtg-accessStatus { min-width: 0; display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px; }
6197
+ .dtg-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #1687bd; background: #eaf7fd; font-size: 11px; font-weight: 700; }
6198
+ .dtg-accessBadge[data-mode="private-allowlist"] { color: #a15c00; background: #fff3d6; }
6199
+ .dtg-accessHelp { position: relative; display: inline-flex; flex: none; }
6200
+ .dtg-accessHelpButton { width: 20px; height: 20px; display: grid; place-items: center; padding: 0; border: 1px solid color-mix(in srgb, #229ed9 28%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 50%; color: #1687bd; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; line-height: 1; font-weight: 750; cursor: help; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
6201
+ .dtg-accessHelpButton:hover { border-color: #229ed9; color: #1178a8; background: #eaf7fd; }
6202
+ .dtg-accessHelpButton:focus-visible { outline: none; border-color: #229ed9; box-shadow: 0 0 0 3px color-mix(in srgb, #229ed9 18%, transparent); }
6203
+ .dtg-accessTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 30; width: 260px; max-width: min(280px, calc(100vw - 48px)); display: grid; gap: 8px; padding: 10px 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
6204
+ .dtg-accessTooltipItem { display: grid; gap: 2px; }
6205
+ .dtg-accessTooltipItem + .dtg-accessTooltipItem { padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l2, #eef0f3); }
6206
+ .dtg-accessTooltipItem strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 17px; font-weight: 700; }
6207
+ .dtg-accessTooltipItem > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; font-weight: 400; }
6208
+ .dtg-accessHelp:hover .dtg-accessTooltip, .dtg-accessHelp:focus-within .dtg-accessTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
6209
+ .dtg-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
6210
+ .dtg-accessField select, .dtg-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
6211
+ .dtg-accessField select { height: 34px; padding: 0 9px; }
6212
+ .dtg-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
6213
+ .dtg-accessField textarea:disabled { color: var(--dsw-alias-label-tertiary, #8f959e); background: var(--dsw-alias-bg-module-platform, #f2f3f5); cursor: not-allowed; resize: none; opacity: 1; }
6214
+ .dtg-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
6215
+ .dtg-accessWarning, .dtg-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
6216
+ .dtg-accessWarning { color: #a15c00; }
6217
+ .dtg-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
6218
+ .dtg-accessActions { display: flex; justify-content: flex-end; }
5729
6219
  `;
5730
6220
  function installTelegramStyles() {
5731
6221
  if (typeof document === "undefined") return () => {
@@ -5736,12 +6226,152 @@ function installTelegramStyles() {
5736
6226
  const style = document.createElement("style");
5737
6227
  style.dataset.plugin = "@xmanrui/dsh-im";
5738
6228
  style.dataset.pluginCss = TELEGRAM_STYLE_ID;
5739
- style.textContent = CSS6;
6229
+ style.textContent = CSS7;
5740
6230
  document.head.appendChild(style);
5741
6231
  return () => style.remove();
5742
6232
  }
5743
6233
 
5744
6234
  // plugin-src/client/channels/telegram/index.js
6235
+ function policyFor(account) {
6236
+ return {
6237
+ accessMode: account?.accessPolicy?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible",
6238
+ allowedUsers: Array.isArray(account?.accessPolicy?.allowedUsers) ? account.accessPolicy.allowedUsers : []
6239
+ };
6240
+ }
6241
+ function allowedUsersFromText(value) {
6242
+ const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
6243
+ if (entries.some((entry) => !/^[1-9]\d{0,15}$/.test(entry))) {
6244
+ throw new TypeError("User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
6245
+ }
6246
+ return [...new Set(entries)];
6247
+ }
6248
+ function TelegramAccessSettings({ account, busy = false, onSave }) {
6249
+ const policy = policyFor(account);
6250
+ const sourceUsers = policy.allowedUsers.join("\n");
6251
+ const accessHelpId = React14.useId();
6252
+ const [accessMode, setAccessMode] = React14.useState(policy.accessMode);
6253
+ const [allowedUsers, setAllowedUsers] = React14.useState(sourceUsers);
6254
+ const [error, setError] = React14.useState(null);
6255
+ React14.useEffect(() => {
6256
+ setAccessMode(policy.accessMode);
6257
+ setAllowedUsers(sourceUsers);
6258
+ setError(null);
6259
+ }, [policy.accessMode, sourceUsers]);
6260
+ const save = async (event) => {
6261
+ event.preventDefault();
6262
+ setError(null);
6263
+ try {
6264
+ const normalized = allowedUsersFromText(allowedUsers);
6265
+ if (typeof onSave !== "function") throw new Error("Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
6266
+ await onSave({ accessMode, allowedUsers: normalized });
6267
+ } catch (caught) {
6268
+ setError(caught?.message ?? "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
6269
+ }
6270
+ };
6271
+ const privateAllowlist = accessMode === "private-allowlist";
6272
+ const savedPrivateAllowlist = policy.accessMode === "private-allowlist";
6273
+ const emptyAllowlist = privateAllowlist && allowedUsers.trim() === "";
6274
+ return h2(
6275
+ "form",
6276
+ { className: "dtg-access", onSubmit: save },
6277
+ h2(
6278
+ "div",
6279
+ { className: "dtg-accessHeading" },
6280
+ h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
6281
+ h2(
6282
+ "span",
6283
+ { className: "dtg-accessStatus" },
6284
+ h2(
6285
+ "span",
6286
+ { className: "dtg-accessBadge", "data-mode": policy.accessMode },
6287
+ savedPrivateAllowlist ? "\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F" : "\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F"
6288
+ ),
6289
+ h2(
6290
+ "span",
6291
+ { className: "dtg-accessHelp" },
6292
+ h2("button", {
6293
+ type: "button",
6294
+ className: "dtg-accessHelpButton",
6295
+ "aria-label": "\u67E5\u770B Telegram \u8BBF\u95EE\u6A21\u5F0F\u8BF4\u660E",
6296
+ "aria-describedby": accessHelpId
6297
+ }, h2("span", { "aria-hidden": "true" }, "?")),
6298
+ h2(
6299
+ "span",
6300
+ {
6301
+ id: accessHelpId,
6302
+ className: "dtg-accessTooltip",
6303
+ role: "tooltip"
6304
+ },
6305
+ h2(
6306
+ "span",
6307
+ { className: "dtg-accessTooltipItem" },
6308
+ h2("strong", null, "\u517C\u5BB9\u6A21\u5F0F"),
6309
+ h2("span", null, "\u4FDD\u6301\u539F\u6709\u884C\u4E3A\uFF1A\u79C1\u804A\u76F4\u63A5\u54CD\u5E94\uFF0C\u7FA4\u804A\u5728\u88AB\u63D0\u53CA\u6216\u56DE\u590D\u65F6\u54CD\u5E94\u3002")
6310
+ ),
6311
+ h2(
6312
+ "span",
6313
+ { className: "dtg-accessTooltipItem" },
6314
+ h2("strong", null, "\u5B89\u5168\u6A21\u5F0F"),
6315
+ h2("span", null, "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002")
6316
+ )
6317
+ )
6318
+ )
6319
+ )
6320
+ ),
6321
+ h2(
6322
+ "label",
6323
+ { className: "dtg-accessField" },
6324
+ h2("span", null, "\u6A21\u5F0F"),
6325
+ h2(
6326
+ "select",
6327
+ {
6328
+ value: accessMode,
6329
+ disabled: busy,
6330
+ "aria-label": "Telegram \u8BBF\u95EE\u6A21\u5F0F",
6331
+ onChange: (event) => {
6332
+ setAccessMode(event.target.value);
6333
+ setError(null);
6334
+ }
6335
+ },
6336
+ h2("option", { value: "compatible" }, "\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
6337
+ h2("option", { value: "private-allowlist" }, "\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09")
6338
+ )
6339
+ ),
6340
+ h2(
6341
+ "label",
6342
+ { className: "dtg-accessField" },
6343
+ h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID"),
6344
+ h2("textarea", {
6345
+ value: allowedUsers,
6346
+ disabled: busy || !privateAllowlist,
6347
+ rows: 3,
6348
+ placeholder: "\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID",
6349
+ "aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID",
6350
+ onChange: (event) => {
6351
+ setAllowedUsers(event.target.value);
6352
+ setError(null);
6353
+ }
6354
+ }),
6355
+ h2("small", null, privateAllowlist ? "\u767D\u540D\u5355\u4EC5\u5C5E\u4E8E\u5F53\u524D\u673A\u5668\u4EBA\u3002" : "\u517C\u5BB9\u6A21\u5F0F\u4E0B\u6682\u4E0D\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002")
6356
+ ),
6357
+ emptyAllowlist ? h2(
6358
+ "p",
6359
+ { className: "dtg-accessWarning", role: "status" },
6360
+ "\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002"
6361
+ ) : null,
6362
+ error ? h2("p", { className: "dtg-accessError", role: "alert" }, error) : null,
6363
+ h2(
6364
+ "div",
6365
+ { className: "dtg-accessActions" },
6366
+ h2("button", {
6367
+ type: "submit",
6368
+ className: "ddt-button",
6369
+ "data-kind": "secondary",
6370
+ disabled: busy
6371
+ }, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
6372
+ )
6373
+ );
6374
+ }
5745
6375
  var channel3 = createTokenChannelSettings({
5746
6376
  channel: "Telegram",
5747
6377
  endpoints: TELEGRAM_ENDPOINTS,
@@ -5754,7 +6384,9 @@ var channel3 = createTokenChannelSettings({
5754
6384
  tokenPlaceholder: "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token",
5755
6385
  emptyTitle: "\u63A5\u5165 Telegram \u673A\u5668\u4EBA",
5756
6386
  emptyDescription: "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002",
5757
- platformLabel: "Telegram"
6387
+ platformLabel: "Telegram",
6388
+ AccountSettings: TelegramAccessSettings,
6389
+ accountSettingsEndpoint: TELEGRAM_ENDPOINTS.setAccessPolicy
5758
6390
  });
5759
6391
  var TelegramSettingsTab = channel3.SettingsTab;
5760
6392
  var TelegramAccountCard = channel3.AccountCard;
@@ -5877,11 +6509,11 @@ function formatRemaining4(milliseconds) {
5877
6509
  }
5878
6510
 
5879
6511
  // plugin-src/client/channels/wecom/index.js
5880
- var React13 = __toESM(require("react"), 1);
6512
+ var React15 = __toESM(require("react"), 1);
5881
6513
 
5882
6514
  // plugin-src/client/channels/wecom/styles.js
5883
6515
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
5884
- var CSS7 = String.raw`
6516
+ var CSS8 = String.raw`
5885
6517
  .dwecom-page { --ddt-accent: #3370ff; --ddt-accent-deep: #245bdb; --ddt-accent-wash: #eef4ff; }
5886
6518
  .dwecom-avatar, .dwecom-brand { color: #3370ff; background: #fff; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); }
5887
6519
  .dwecom-avatar svg, .dwecom-brand svg { display: block; }
@@ -5895,14 +6527,14 @@ function installWecomStyles() {
5895
6527
  const style = document.createElement("style");
5896
6528
  style.dataset.plugin = "@xmanrui/dsh-im";
5897
6529
  style.dataset.pluginCss = WECOM_STYLE_ID;
5898
- style.textContent = CSS7;
6530
+ style.textContent = CSS8;
5899
6531
  document.head.appendChild(style);
5900
6532
  return () => style.remove();
5901
6533
  }
5902
6534
 
5903
6535
  // plugin-src/client/channels/wecom/index.js
5904
6536
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
5905
- var Button9 = React13.forwardRef(function Button10({ children, kind = "secondary", className = "", ...props }, ref) {
6537
+ var Button10 = React15.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
5906
6538
  return h2("button", {
5907
6539
  ...props,
5908
6540
  ref,
@@ -5933,7 +6565,7 @@ function Heading4({ totals, adding, busy, onAdd, onCredential, credentialOpen, a
5933
6565
  h2(
5934
6566
  "div",
5935
6567
  { className: "dim-bindActions" },
5936
- h2(Button9, {
6568
+ h2(Button10, {
5937
6569
  kind: "primary",
5938
6570
  className: "dim-scanButton",
5939
6571
  onClick: onAdd,
@@ -5941,7 +6573,7 @@ function Heading4({ totals, adding, busy, onAdd, onCredential, credentialOpen, a
5941
6573
  ref: addButtonRef,
5942
6574
  "aria-label": "\u626B\u7801\u63A5\u5165\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA"
5943
6575
  }, h2(QrActionIcon), adding ? "\u6B63\u5728\u63A5\u5165" : "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA"),
5944
- h2(Button9, {
6576
+ h2(Button10, {
5945
6577
  kind: "credential",
5946
6578
  className: "dim-credentialButton",
5947
6579
  onClick: onCredential,
@@ -5988,7 +6620,7 @@ function EmptyView4({ busy, onStart }) {
5988
6620
  "div",
5989
6621
  { className: "ddt-actions dim-viewActions" },
5990
6622
  h2(
5991
- Button9,
6623
+ Button10,
5992
6624
  { kind: "primary", onClick: onStart, disabled: busy },
5993
6625
  busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801"
5994
6626
  )
@@ -6059,8 +6691,8 @@ function QrPanel3({ provision, now, busy, onRefresh, onCancel }) {
6059
6691
  h2(
6060
6692
  "div",
6061
6693
  { className: "ddt-actions dim-viewActions" },
6062
- h2(Button9, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6063
- h2(Button9, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
6694
+ h2(Button10, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6695
+ h2(Button10, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
6064
6696
  )
6065
6697
  )
6066
6698
  )
@@ -6089,8 +6721,8 @@ function ProvisionView2({ provision, busy, onRetry, onClose }) {
6089
6721
  h2(
6090
6722
  "div",
6091
6723
  { className: "ddt-actions dim-viewActions" },
6092
- h2(Button9, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6093
- h2(Button9, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
6724
+ h2(Button10, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6725
+ h2(Button10, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
6094
6726
  )
6095
6727
  )
6096
6728
  );
@@ -6104,8 +6736,8 @@ function RemoveConfirmation4({ account, busy, onConfirm, onCancel }) {
6104
6736
  h2(
6105
6737
  "div",
6106
6738
  { className: "ddt-actions dim-viewActions" },
6107
- h2(Button9, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
6108
- h2(Button9, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165")
6739
+ h2(Button10, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
6740
+ h2(Button10, { kind: "danger", onClick: onConfirm, disabled: busy }, busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165")
6109
6741
  )
6110
6742
  );
6111
6743
  }
@@ -6121,7 +6753,7 @@ function AccountCard3({
6121
6753
  onCancelRemove
6122
6754
  }) {
6123
6755
  const tone = account.connected ? "success" : account.state === "error" ? "error" : "warning";
6124
- const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
6756
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : account.state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
6125
6757
  const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
6126
6758
  return h2(
6127
6759
  "article",
@@ -6147,7 +6779,7 @@ function AccountCard3({
6147
6779
  "div",
6148
6780
  { className: "ddt-health dim-botHealth" },
6149
6781
  h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
6150
- h2("span", null, stateLabel)
6782
+ h2("span", null, stateLabel2)
6151
6783
  )
6152
6784
  ),
6153
6785
  h2(
@@ -6173,8 +6805,8 @@ function AccountCard3({
6173
6805
  h2(
6174
6806
  "div",
6175
6807
  { className: "ddt-actions dim-cardActions" },
6176
- h2(Button9, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
6177
- h2(Button9, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
6808
+ h2(Button10, { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
6809
+ h2(Button10, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
6178
6810
  )
6179
6811
  )
6180
6812
  ),
@@ -6187,21 +6819,21 @@ function AccountCard3({
6187
6819
  );
6188
6820
  }
6189
6821
  function WecomSettingsTab({ rpcCall }) {
6190
- const [model, setModel] = React13.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
6191
- const [provision, setProvision] = React13.useState(null);
6192
- const [busy, setBusy] = React13.useState(false);
6193
- const [busyByBot, setBusyByBot] = React13.useState({});
6194
- const [feedbackByBot, setFeedbackByBot] = React13.useState({});
6195
- const [removeTarget, setRemoveTarget] = React13.useState(null);
6196
- const [credentialOpen, setCredentialOpen] = React13.useState(false);
6197
- const [credentialError, setCredentialError] = React13.useState(null);
6198
- const [notice, setNotice] = React13.useState("");
6199
- const [now, setNow] = React13.useState(Date.now());
6200
- const mounted = React13.useRef(true);
6822
+ const [model, setModel] = React15.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
6823
+ const [provision, setProvision] = React15.useState(null);
6824
+ const [busy, setBusy] = React15.useState(false);
6825
+ const [busyByBot, setBusyByBot] = React15.useState({});
6826
+ const [feedbackByBot, setFeedbackByBot] = React15.useState({});
6827
+ const [removeTarget, setRemoveTarget] = React15.useState(null);
6828
+ const [credentialOpen, setCredentialOpen] = React15.useState(false);
6829
+ const [credentialError, setCredentialError] = React15.useState(null);
6830
+ const [notice, setNotice] = React15.useState("");
6831
+ const [now, setNow] = React15.useState(Date.now());
6832
+ const mounted = React15.useRef(true);
6201
6833
  const workspaceFence = useWorkspaceSnapshotFence();
6202
- const addButtonRef = React13.useRef(null);
6203
- const noticeFrameRef = React13.useRef(null);
6204
- const announce = React13.useCallback((message) => {
6834
+ const addButtonRef = React15.useRef(null);
6835
+ const noticeFrameRef = React15.useRef(null);
6836
+ const announce = React15.useCallback((message) => {
6205
6837
  if (!mounted.current) return;
6206
6838
  if (noticeFrameRef.current !== null) {
6207
6839
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -6215,7 +6847,7 @@ function WecomSettingsTab({ rpcCall }) {
6215
6847
  });
6216
6848
  }
6217
6849
  }, []);
6218
- React13.useEffect(() => {
6850
+ React15.useEffect(() => {
6219
6851
  const disposeDingtalk = installDingtalkStyles();
6220
6852
  const disposeWecom = installWecomStyles();
6221
6853
  mounted.current = true;
@@ -6229,11 +6861,11 @@ function WecomSettingsTab({ rpcCall }) {
6229
6861
  disposeDingtalk();
6230
6862
  };
6231
6863
  }, []);
6232
- const invoke = React13.useCallback(async (endpoint, payload = {}, signal) => {
6864
+ const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
6233
6865
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
6234
6866
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
6235
6867
  }, [rpcCall]);
6236
- const loadStatus = React13.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6868
+ const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6237
6869
  const workspaceVersion = workspaceFence.beginStatus();
6238
6870
  if (workspaceVersion === null) return void 0;
6239
6871
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -6253,12 +6885,12 @@ function WecomSettingsTab({ rpcCall }) {
6253
6885
  return void 0;
6254
6886
  }
6255
6887
  }, [invoke, workspaceFence]);
6256
- React13.useEffect(() => {
6888
+ React15.useEffect(() => {
6257
6889
  const controller = new AbortController();
6258
6890
  void loadStatus({ signal: controller.signal, restore: true });
6259
6891
  return () => controller.abort();
6260
6892
  }, [loadStatus]);
6261
- React13.useEffect(() => {
6893
+ React15.useEffect(() => {
6262
6894
  if (model.phase !== "ready") return void 0;
6263
6895
  const controller = new AbortController();
6264
6896
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -6267,12 +6899,12 @@ function WecomSettingsTab({ rpcCall }) {
6267
6899
  window.clearInterval(timer);
6268
6900
  };
6269
6901
  }, [loadStatus, model.phase]);
6270
- React13.useEffect(() => {
6902
+ React15.useEffect(() => {
6271
6903
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
6272
6904
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
6273
6905
  return () => window.clearInterval(timer);
6274
6906
  }, [provision?.attemptId, provision?.status]);
6275
- const startProvisioning = React13.useCallback(async (replace = false) => {
6907
+ const startProvisioning = React15.useCallback(async (replace = false) => {
6276
6908
  setCredentialOpen(false);
6277
6909
  setCredentialError(null);
6278
6910
  setBusy(true);
@@ -6290,7 +6922,7 @@ function WecomSettingsTab({ rpcCall }) {
6290
6922
  if (mounted.current) setBusy(false);
6291
6923
  }
6292
6924
  }, [invoke, provision?.attemptId]);
6293
- const bindCredentials = React13.useCallback(async ({ identity, secret }) => {
6925
+ const bindCredentials = React15.useCallback(async ({ identity, secret }) => {
6294
6926
  const snapshotVersion = workspaceFence.beginMutation();
6295
6927
  setBusy(true);
6296
6928
  setCredentialError(null);
@@ -6312,7 +6944,7 @@ function WecomSettingsTab({ rpcCall }) {
6312
6944
  if (mounted.current) setBusy(false);
6313
6945
  }
6314
6946
  }, [invoke, loadStatus, workspaceFence]);
6315
- const closeProvision = React13.useCallback(async () => {
6947
+ const closeProvision = React15.useCallback(async () => {
6316
6948
  setBusy(true);
6317
6949
  try {
6318
6950
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -6323,7 +6955,7 @@ function WecomSettingsTab({ rpcCall }) {
6323
6955
  if (mounted.current) setBusy(false);
6324
6956
  }
6325
6957
  }, [invoke, provision?.attemptId, provision?.status]);
6326
- React13.useEffect(() => {
6958
+ React15.useEffect(() => {
6327
6959
  const attemptId = provision?.attemptId;
6328
6960
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
6329
6961
  const controller = new AbortController();
@@ -6353,7 +6985,7 @@ function WecomSettingsTab({ rpcCall }) {
6353
6985
  window.clearTimeout(timer);
6354
6986
  };
6355
6987
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
6356
- const botAction = React13.useCallback(async (account, operation, endpoint, payload) => {
6988
+ const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
6357
6989
  const snapshotVersion = workspaceFence.beginMutation();
6358
6990
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
6359
6991
  try {
@@ -6372,7 +7004,7 @@ function WecomSettingsTab({ rpcCall }) {
6372
7004
  });
6373
7005
  }
6374
7006
  }, [invoke, loadStatus, workspaceFence]);
6375
- const reconnect = React13.useCallback(async (account) => {
7007
+ const reconnect = React15.useCallback(async (account) => {
6376
7008
  setFeedbackByBot((current) => {
6377
7009
  const next = { ...current };
6378
7010
  delete next[account.botId];
@@ -6484,8 +7116,8 @@ function WecomSettingsTab({ rpcCall }) {
6484
7116
  addButtonRef
6485
7117
  }),
6486
7118
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
6487
- model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button9, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
6488
- React13.Fragment,
7119
+ model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
7120
+ React15.Fragment,
6489
7121
  null,
6490
7122
  credentialView,
6491
7123
  provisionView,
@@ -6496,7 +7128,7 @@ function WecomSettingsTab({ rpcCall }) {
6496
7128
  }
6497
7129
 
6498
7130
  // plugin-src/client/channels/weixin/index.js
6499
- var React14 = __toESM(require("react"), 1);
7131
+ var React16 = __toESM(require("react"), 1);
6500
7132
 
6501
7133
  // plugin-src/client/channels/weixin/api.js
6502
7134
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -6648,7 +7280,7 @@ function formatRemaining5(milliseconds) {
6648
7280
 
6649
7281
  // plugin-src/client/channels/weixin/styles.js
6650
7282
  var WEIXIN_STYLE_ID = "xmanrui-dsh-weixin-settings";
6651
- var CSS8 = String.raw`
7283
+ var CSS9 = String.raw`
6652
7284
  .dxw-page {
6653
7285
  --dxw-accent: #07c160;
6654
7286
  --dxw-accent-dark: #05994c;
@@ -6763,13 +7395,13 @@ function installWeixinStyles() {
6763
7395
  const style = document.createElement("style");
6764
7396
  style.dataset.plugin = "@xmanrui/dsh-weixin";
6765
7397
  style.dataset.pluginCss = WEIXIN_STYLE_ID;
6766
- style.textContent = CSS8;
7398
+ style.textContent = CSS9;
6767
7399
  document.head.appendChild(style);
6768
7400
  return () => style.remove();
6769
7401
  }
6770
7402
 
6771
7403
  // plugin-src/client/channels/weixin/index.js
6772
- var Button11 = React14.forwardRef(function Button12({ children, kind = "secondary", className = "", ...props }, ref) {
7404
+ var Button12 = React16.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
6773
7405
  return h2("button", {
6774
7406
  ...props,
6775
7407
  ref,
@@ -6785,7 +7417,7 @@ function Heading5({ totals, adding, busy, onAdd, addButtonRef }) {
6785
7417
  h2(
6786
7418
  "div",
6787
7419
  { className: "dxw-tools" },
6788
- h2(Button11, {
7420
+ h2(Button12, {
6789
7421
  kind: "primary",
6790
7422
  className: "dim-scanButton",
6791
7423
  onClick: onAdd,
@@ -6831,7 +7463,7 @@ function EmptyView5({ onStart, busy }) {
6831
7463
  "div",
6832
7464
  { className: "dxw-actions dim-viewActions" },
6833
7465
  h2(
6834
- Button11,
7466
+ Button12,
6835
7467
  { kind: "primary", onClick: onStart, disabled: busy },
6836
7468
  busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u5FAE\u4FE1\u4E8C\u7EF4\u7801"
6837
7469
  )
@@ -6842,14 +7474,14 @@ function EmptyView5({ onStart, busy }) {
6842
7474
  );
6843
7475
  }
6844
7476
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
6845
- const [imageFailed, setImageFailed] = React14.useState(false);
7477
+ const [imageFailed, setImageFailed] = React16.useState(false);
6846
7478
  const source = safeQrSource5(provision.qrCodeDataUrl);
6847
7479
  const href = safeVerificationUrl(provision.verificationUrl);
6848
7480
  const remaining = Math.max(0, provision.expiresAt - now);
6849
7481
  const expired = remaining === 0 || provision.status === "expired";
6850
7482
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
6851
7483
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
6852
- React14.useEffect(() => setImageFailed(false), [source]);
7484
+ React16.useEffect(() => setImageFailed(false), [source]);
6853
7485
  return h2(
6854
7486
  "div",
6855
7487
  { className: "dxw-card dim-surfaceCard" },
@@ -6901,24 +7533,24 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
6901
7533
  h2(
6902
7534
  "div",
6903
7535
  { className: "dxw-actions dim-viewActions" },
6904
- expired ? h2(Button11, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null,
7536
+ expired ? h2(Button12, { kind: "primary", onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801") : null,
6905
7537
  href ? h2("a", {
6906
7538
  className: "dxw-button",
6907
7539
  href,
6908
7540
  target: "_blank",
6909
7541
  rel: "noopener noreferrer"
6910
7542
  }, "\u6253\u5F00\u5907\u7528\u94FE\u63A5") : null,
6911
- !expired ? h2(Button11, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
6912
- h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
7543
+ !expired ? h2(Button12, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
7544
+ h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
6913
7545
  )
6914
7546
  )
6915
7547
  )
6916
7548
  );
6917
7549
  }
6918
7550
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
6919
- const [code, setCode] = React14.useState("");
7551
+ const [code, setCode] = React16.useState("");
6920
7552
  const valid = /^\d{4,8}$/.test(code);
6921
- React14.useEffect(() => setCode(""), [provision.attemptId]);
7553
+ React16.useEffect(() => setCode(""), [provision.attemptId]);
6922
7554
  return h2(
6923
7555
  "div",
6924
7556
  { className: "dxw-card dim-surfaceCard" },
@@ -6959,7 +7591,7 @@ function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
6959
7591
  disabled: !valid || busy
6960
7592
  }, busy ? "\u6B63\u5728\u9A8C\u8BC1\u2026" : "\u7EE7\u7EED\u8FDE\u63A5")
6961
7593
  ),
6962
- h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A")
7594
+ h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88\u7ED1\u5B9A")
6963
7595
  )
6964
7596
  );
6965
7597
  }
@@ -6973,7 +7605,7 @@ function ProgressPanel2({ scanned, onCancel, busy }) {
6973
7605
  onCancel ? h2(
6974
7606
  "div",
6975
7607
  { className: "dxw-actions dim-viewActions", style: { justifyContent: "center", marginTop: 14 } },
6976
- h2(Button11, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
7608
+ h2(Button12, { onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
6977
7609
  ) : null
6978
7610
  );
6979
7611
  }
@@ -6991,8 +7623,8 @@ function ProvisionError3({ provision, busy, onRetry, onClose }) {
6991
7623
  h2(
6992
7624
  "div",
6993
7625
  { className: "dxw-actions dim-viewActions" },
6994
- h2(Button11, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
6995
- h2(Button11, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
7626
+ h2(Button12, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
7627
+ h2(Button12, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
6996
7628
  )
6997
7629
  )
6998
7630
  );
@@ -7079,11 +7711,11 @@ function AccountCard4({
7079
7711
  "div",
7080
7712
  { className: "dxw-actions dim-cardActions" },
7081
7713
  h2(
7082
- Button11,
7714
+ Button12,
7083
7715
  { className: "dim-cardAction", onClick: onReconnect, disabled: Boolean(busy) },
7084
7716
  busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"
7085
7717
  ),
7086
- h2(Button11, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
7718
+ h2(Button12, { className: "dim-cardAction", kind: "danger", onClick: onRequestRemove, disabled: Boolean(busy) }, "\u79FB\u9664\u63A5\u5165")
7087
7719
  )
7088
7720
  )
7089
7721
  ),
@@ -7095,9 +7727,9 @@ function AccountCard4({
7095
7727
  h2(
7096
7728
  "div",
7097
7729
  { className: "dxw-actions dim-viewActions" },
7098
- h2(Button11, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"),
7730
+ h2(Button12, { onClick: onCancelRemove, disabled: busy === "delete" }, "\u4FDD\u7559\u8D26\u53F7"),
7099
7731
  h2(
7100
- Button11,
7732
+ Button12,
7101
7733
  { kind: "danger", onClick: onConfirmRemove, disabled: busy === "delete" },
7102
7734
  busy === "delete" ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664"
7103
7735
  )
@@ -7138,40 +7770,40 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
7138
7770
  };
7139
7771
  }
7140
7772
  function WeixinSettingsTab({ rpcCall }) {
7141
- const [model, setModel] = React14.useState({
7773
+ const [model, setModel] = React16.useState({
7142
7774
  phase: "loading",
7143
7775
  bots: [],
7144
7776
  totals: EMPTY_TOTALS3,
7145
7777
  revision: 0,
7146
7778
  error: null
7147
7779
  });
7148
- const [provision, setProvision] = React14.useState(null);
7149
- const [busy, setBusy] = React14.useState(false);
7150
- const [busyByBot, setBusyByBot] = React14.useState({});
7151
- const [feedbackByBot, setFeedbackByBot] = React14.useState({});
7152
- const [removeTarget, setRemoveTarget] = React14.useState(null);
7153
- const [notice, setNotice] = React14.useState("");
7154
- const [now, setNow] = React14.useState(() => Date.now());
7155
- const addButtonRef = React14.useRef(null);
7156
- const mountedRef = React14.useRef(true);
7780
+ const [provision, setProvision] = React16.useState(null);
7781
+ const [busy, setBusy] = React16.useState(false);
7782
+ const [busyByBot, setBusyByBot] = React16.useState({});
7783
+ const [feedbackByBot, setFeedbackByBot] = React16.useState({});
7784
+ const [removeTarget, setRemoveTarget] = React16.useState(null);
7785
+ const [notice, setNotice] = React16.useState("");
7786
+ const [now, setNow] = React16.useState(() => Date.now());
7787
+ const addButtonRef = React16.useRef(null);
7788
+ const mountedRef = React16.useRef(true);
7157
7789
  const workspaceFence = useWorkspaceSnapshotFence();
7158
7790
  const scheduleAnimationFrame = useAnimationFrameScheduler();
7159
- React14.useEffect(() => {
7791
+ React16.useEffect(() => {
7160
7792
  mountedRef.current = true;
7161
7793
  return () => {
7162
7794
  mountedRef.current = false;
7163
7795
  };
7164
7796
  }, []);
7165
- const announce = React14.useCallback((value) => {
7797
+ const announce = React16.useCallback((value) => {
7166
7798
  setNotice("");
7167
7799
  scheduleAnimationFrame(() => {
7168
7800
  if (value) setNotice(value);
7169
7801
  }, "announcement");
7170
7802
  }, [scheduleAnimationFrame]);
7171
- const invoke = React14.useCallback(async (endpoint, payload = {}, signal) => {
7803
+ const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
7172
7804
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
7173
7805
  }, [rpcCall]);
7174
- const loadStatus = React14.useCallback(async ({
7806
+ const loadStatus = React16.useCallback(async ({
7175
7807
  signal,
7176
7808
  silent = false,
7177
7809
  restoreProvisioning = false
@@ -7207,12 +7839,12 @@ function WeixinSettingsTab({ rpcCall }) {
7207
7839
  return void 0;
7208
7840
  }
7209
7841
  }, [invoke, workspaceFence]);
7210
- React14.useEffect(() => {
7842
+ React16.useEffect(() => {
7211
7843
  const controller = new AbortController();
7212
7844
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
7213
7845
  return () => controller.abort();
7214
7846
  }, [loadStatus]);
7215
- React14.useEffect(() => {
7847
+ React16.useEffect(() => {
7216
7848
  if (model.phase !== "ready") return void 0;
7217
7849
  const controller = new AbortController();
7218
7850
  let running = false;
@@ -7231,12 +7863,12 @@ function WeixinSettingsTab({ rpcCall }) {
7231
7863
  window.clearInterval(timer);
7232
7864
  };
7233
7865
  }, [loadStatus, model.phase]);
7234
- React14.useEffect(() => {
7866
+ React16.useEffect(() => {
7235
7867
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
7236
7868
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
7237
7869
  return () => window.clearInterval(timer);
7238
7870
  }, [provision?.attemptId, provision?.status]);
7239
- const startProvisioning = React14.useCallback(async ({ replace = false } = {}) => {
7871
+ const startProvisioning = React16.useCallback(async ({ replace = false } = {}) => {
7240
7872
  setBusy(true);
7241
7873
  try {
7242
7874
  if (replace && provision?.attemptId) {
@@ -7257,7 +7889,7 @@ function WeixinSettingsTab({ rpcCall }) {
7257
7889
  setBusy(false);
7258
7890
  }
7259
7891
  }, [announce, invoke, provision?.attemptId]);
7260
- const cancelProvisioning = React14.useCallback(async () => {
7892
+ const cancelProvisioning = React16.useCallback(async () => {
7261
7893
  setBusy(true);
7262
7894
  try {
7263
7895
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -7272,7 +7904,7 @@ function WeixinSettingsTab({ rpcCall }) {
7272
7904
  setBusy(false);
7273
7905
  }
7274
7906
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
7275
- const submitVerification = React14.useCallback(async (verifyCode) => {
7907
+ const submitVerification = React16.useCallback(async (verifyCode) => {
7276
7908
  if (!provision?.attemptId) return;
7277
7909
  setBusy(true);
7278
7910
  try {
@@ -7288,7 +7920,7 @@ function WeixinSettingsTab({ rpcCall }) {
7288
7920
  setBusy(false);
7289
7921
  }
7290
7922
  }, [announce, invoke, provision?.attemptId]);
7291
- React14.useEffect(() => {
7923
+ React16.useEffect(() => {
7292
7924
  const attemptId = provision?.attemptId;
7293
7925
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
7294
7926
  const controller = new AbortController();
@@ -7336,7 +7968,7 @@ function WeixinSettingsTab({ rpcCall }) {
7336
7968
  controller.abort();
7337
7969
  };
7338
7970
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
7339
- const setBotBusy = React14.useCallback((botId, value) => {
7971
+ const setBotBusy = React16.useCallback((botId, value) => {
7340
7972
  setBusyByBot((current) => {
7341
7973
  const next = { ...current };
7342
7974
  if (value) next[botId] = value;
@@ -7344,7 +7976,7 @@ function WeixinSettingsTab({ rpcCall }) {
7344
7976
  return next;
7345
7977
  });
7346
7978
  }, []);
7347
- const reconnect = React14.useCallback(async (account) => {
7979
+ const reconnect = React16.useCallback(async (account) => {
7348
7980
  const snapshotVersion = workspaceFence.beginMutation();
7349
7981
  setBotBusy(account.botId, "reconnect");
7350
7982
  setFeedbackByBot((current) => {
@@ -7389,7 +8021,7 @@ function WeixinSettingsTab({ rpcCall }) {
7389
8021
  setBotBusy(account.botId, null);
7390
8022
  }
7391
8023
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
7392
- const saveWorkspace = React14.useCallback(async (account, workspace) => {
8024
+ const saveWorkspace = React16.useCallback(async (account, workspace) => {
7393
8025
  const workspaceVersion = workspaceFence.beginMutation();
7394
8026
  setBotBusy(account.botId, "workspace");
7395
8027
  try {
@@ -7412,7 +8044,7 @@ function WeixinSettingsTab({ rpcCall }) {
7412
8044
  if (mountedRef.current) setBotBusy(account.botId, null);
7413
8045
  }
7414
8046
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
7415
- const remove = React14.useCallback(async (account) => {
8047
+ const remove = React16.useCallback(async (account) => {
7416
8048
  const snapshotVersion = workspaceFence.beginMutation();
7417
8049
  setBotBusy(account.botId, "delete");
7418
8050
  try {
@@ -7485,10 +8117,10 @@ function WeixinSettingsTab({ rpcCall }) {
7485
8117
  { className: "dxw-error dim-inlineError" },
7486
8118
  h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u5FAE\u4FE1\u72B6\u6001"),
7487
8119
  h2("p", null, model.error?.message ?? "\u8BF7\u7A0D\u540E\u91CD\u8BD5"),
7488
- h2(Button11, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
8120
+ h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
7489
8121
  )
7490
8122
  ) : h2(
7491
- React14.Fragment,
8123
+ React16.Fragment,
7492
8124
  null,
7493
8125
  provisionView,
7494
8126
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -7620,11 +8252,11 @@ function formatRemaining6(milliseconds) {
7620
8252
  }
7621
8253
 
7622
8254
  // plugin-src/client/channels/whatsapp/index.js
7623
- var React15 = __toESM(require("react"), 1);
8255
+ var React17 = __toESM(require("react"), 1);
7624
8256
 
7625
8257
  // plugin-src/client/channels/whatsapp/styles.js
7626
8258
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
7627
- var CSS9 = String.raw`
8259
+ var CSS10 = String.raw`
7628
8260
  .dwa-page { --ddt-accent: #25d366; --ddt-accent-deep: #128c7e; --ddt-accent-wash: #eafbf0; }
7629
8261
  .dwa-avatar { color: #fff; background: #25d366; }
7630
8262
  .dwa-avatar svg { display: block; }
@@ -7638,14 +8270,14 @@ function installWhatsappStyles() {
7638
8270
  const style = document.createElement("style");
7639
8271
  style.dataset.plugin = "@xmanrui/dsh-im";
7640
8272
  style.dataset.pluginCss = WHATSAPP_STYLE_ID;
7641
- style.textContent = CSS9;
8273
+ style.textContent = CSS10;
7642
8274
  document.head.appendChild(style);
7643
8275
  return () => style.remove();
7644
8276
  }
7645
8277
 
7646
8278
  // plugin-src/client/channels/whatsapp/index.js
7647
8279
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
7648
- var Button13 = React15.forwardRef(function Button14({ children, kind = "secondary", className = "", ...props }, ref) {
8280
+ var Button14 = React17.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
7649
8281
  return h2("button", {
7650
8282
  ...props,
7651
8283
  ref,
@@ -7685,7 +8317,7 @@ function Heading6({ totals, busy, onAdd, addButtonRef }) {
7685
8317
  h2(
7686
8318
  "div",
7687
8319
  { className: "dim-bindActions" },
7688
- h2(Button13, {
8320
+ h2(Button14, {
7689
8321
  kind: "primary",
7690
8322
  className: "dim-scanButton",
7691
8323
  onClick: onAdd,
@@ -7730,7 +8362,7 @@ function EmptyView6({ busy, onStart }) {
7730
8362
  "div",
7731
8363
  { className: "ddt-actions dim-viewActions" },
7732
8364
  h2(
7733
- Button13,
8365
+ Button14,
7734
8366
  { kind: "primary", onClick: onStart, disabled: busy },
7735
8367
  busy ? "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026" : "\u751F\u6210\u4E8C\u7EF4\u7801"
7736
8368
  )
@@ -7799,8 +8431,8 @@ function QrPanel5({ provision, now, busy, onRefresh, onCancel }) {
7799
8431
  h2(
7800
8432
  "div",
7801
8433
  { className: "ddt-actions dim-viewActions" },
7802
- h2(Button13, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
7803
- h2(Button13, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
8434
+ h2(Button14, { onClick: onRefresh, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
8435
+ h2(Button14, { kind: "quiet", onClick: onCancel, disabled: busy }, "\u53D6\u6D88")
7804
8436
  )
7805
8437
  )
7806
8438
  )
@@ -7836,8 +8468,8 @@ function ProvisionView3({ provision, busy, onRetry, onClose }) {
7836
8468
  h2(
7837
8469
  "div",
7838
8470
  { className: "ddt-actions dim-viewActions" },
7839
- h2(Button13, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
7840
- h2(Button13, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
8471
+ h2(Button14, { kind: "primary", onClick: onRetry, disabled: busy }, "\u91CD\u65B0\u751F\u6210\u4E8C\u7EF4\u7801"),
8472
+ h2(Button14, { onClick: onClose, disabled: busy }, "\u5173\u95ED")
7841
8473
  )
7842
8474
  )
7843
8475
  );
@@ -7851,9 +8483,9 @@ function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
7851
8483
  h2(
7852
8484
  "div",
7853
8485
  { className: "ddt-actions dim-viewActions" },
7854
- h2(Button13, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
8486
+ h2(Button14, { onClick: onCancel, disabled: busy }, "\u4FDD\u7559\u673A\u5668\u4EBA"),
7855
8487
  h2(
7856
- Button13,
8488
+ Button14,
7857
8489
  { kind: "danger", onClick: onConfirm, disabled: busy },
7858
8490
  busy ? "\u6B63\u5728\u79FB\u9664\u2026" : "\u786E\u8BA4\u79FB\u9664\u63A5\u5165"
7859
8491
  )
@@ -7873,7 +8505,7 @@ function WhatsappAccountCard({
7873
8505
  }) {
7874
8506
  const state = busy === "reconnect" ? "connecting" : account.state;
7875
8507
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
7876
- const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
8508
+ const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
7877
8509
  const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
7878
8510
  return h2(
7879
8511
  "article",
@@ -7902,7 +8534,7 @@ function WhatsappAccountCard({
7902
8534
  "div",
7903
8535
  { className: "ddt-health dim-botHealth" },
7904
8536
  h2("span", { className: "ddt-dot dim-healthDot", "data-tone": tone }),
7905
- h2("span", null, stateLabel)
8537
+ h2("span", null, stateLabel2)
7906
8538
  )
7907
8539
  ),
7908
8540
  h2(
@@ -7937,12 +8569,12 @@ function WhatsappAccountCard({
7937
8569
  h2(
7938
8570
  "div",
7939
8571
  { className: "ddt-actions dim-cardActions" },
7940
- h2(Button13, {
8572
+ h2(Button14, {
7941
8573
  className: "dim-cardAction",
7942
8574
  onClick: onReconnect,
7943
8575
  disabled: Boolean(busy)
7944
8576
  }, busy === "reconnect" ? "\u68C0\u67E5\u4E2D\u2026" : account.connected ? "\u68C0\u67E5\u8FDE\u63A5" : "\u91CD\u8BD5\u8FDE\u63A5"),
7945
- h2(Button13, {
8577
+ h2(Button14, {
7946
8578
  className: "dim-cardAction",
7947
8579
  kind: "danger",
7948
8580
  onClick: onRequestRemove,
@@ -7960,22 +8592,22 @@ function WhatsappAccountCard({
7960
8592
  );
7961
8593
  }
7962
8594
  function WhatsappSettingsTab({ rpcCall }) {
7963
- const [model, setModel] = React15.useState({
8595
+ const [model, setModel] = React17.useState({
7964
8596
  phase: "loading",
7965
8597
  bots: [],
7966
8598
  totals: { configured: 0, connected: 0 },
7967
8599
  error: null
7968
8600
  });
7969
- const [provision, setProvision] = React15.useState(null);
7970
- const [busy, setBusy] = React15.useState(false);
7971
- const [busyByBot, setBusyByBot] = React15.useState({});
7972
- const [testNoticeByBot, setTestNoticeByBot] = React15.useState({});
7973
- const [removeTarget, setRemoveTarget] = React15.useState(null);
7974
- const [now, setNow] = React15.useState(Date.now());
7975
- const mounted = React15.useRef(true);
8601
+ const [provision, setProvision] = React17.useState(null);
8602
+ const [busy, setBusy] = React17.useState(false);
8603
+ const [busyByBot, setBusyByBot] = React17.useState({});
8604
+ const [testNoticeByBot, setTestNoticeByBot] = React17.useState({});
8605
+ const [removeTarget, setRemoveTarget] = React17.useState(null);
8606
+ const [now, setNow] = React17.useState(Date.now());
8607
+ const mounted = React17.useRef(true);
7976
8608
  const workspaceFence = useWorkspaceSnapshotFence();
7977
- const addButtonRef = React15.useRef(null);
7978
- React15.useEffect(() => {
8609
+ const addButtonRef = React17.useRef(null);
8610
+ React17.useEffect(() => {
7979
8611
  const disposeDingtalk = installDingtalkStyles();
7980
8612
  const disposeWhatsapp = installWhatsappStyles();
7981
8613
  mounted.current = true;
@@ -7985,11 +8617,11 @@ function WhatsappSettingsTab({ rpcCall }) {
7985
8617
  disposeDingtalk();
7986
8618
  };
7987
8619
  }, []);
7988
- const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
8620
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
7989
8621
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
7990
8622
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
7991
8623
  }, [rpcCall]);
7992
- const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
8624
+ const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7993
8625
  const workspaceVersion = workspaceFence.beginStatus();
7994
8626
  if (workspaceVersion === null) return void 0;
7995
8627
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@@ -8013,12 +8645,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8013
8645
  return void 0;
8014
8646
  }
8015
8647
  }, [invoke, workspaceFence]);
8016
- React15.useEffect(() => {
8648
+ React17.useEffect(() => {
8017
8649
  const controller = new AbortController();
8018
8650
  void loadStatus({ signal: controller.signal, restore: true });
8019
8651
  return () => controller.abort();
8020
8652
  }, [loadStatus]);
8021
- React15.useEffect(() => {
8653
+ React17.useEffect(() => {
8022
8654
  if (model.phase !== "ready") return void 0;
8023
8655
  const controller = new AbortController();
8024
8656
  const timer = window.setInterval(
@@ -8030,12 +8662,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8030
8662
  window.clearInterval(timer);
8031
8663
  };
8032
8664
  }, [loadStatus, model.phase]);
8033
- React15.useEffect(() => {
8665
+ React17.useEffect(() => {
8034
8666
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
8035
8667
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
8036
8668
  return () => window.clearInterval(timer);
8037
8669
  }, [provision?.attemptId, provision?.status]);
8038
- const startProvisioning = React15.useCallback(async (replace = false) => {
8670
+ const startProvisioning = React17.useCallback(async (replace = false) => {
8039
8671
  setBusy(true);
8040
8672
  try {
8041
8673
  if (replace && provision?.attemptId) {
@@ -8053,7 +8685,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8053
8685
  if (mounted.current) setBusy(false);
8054
8686
  }
8055
8687
  }, [invoke, provision?.attemptId]);
8056
- const closeProvision = React15.useCallback(async () => {
8688
+ const closeProvision = React17.useCallback(async () => {
8057
8689
  setBusy(true);
8058
8690
  try {
8059
8691
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -8064,7 +8696,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8064
8696
  if (mounted.current) setBusy(false);
8065
8697
  }
8066
8698
  }, [invoke, provision?.attemptId, provision?.status]);
8067
- React15.useEffect(() => {
8699
+ React17.useEffect(() => {
8068
8700
  const attemptId = provision?.attemptId;
8069
8701
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
8070
8702
  const controller = new AbortController();
@@ -8105,7 +8737,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8105
8737
  if (timer) window.clearTimeout(timer);
8106
8738
  };
8107
8739
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
8108
- const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
8740
+ const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
8109
8741
  const snapshotVersion = workspaceFence.beginMutation();
8110
8742
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
8111
8743
  if (operation === "reconnect") {
@@ -8201,10 +8833,10 @@ function WhatsappSettingsTab({ rpcCall }) {
8201
8833
  { className: "ddt-inlineError dim-inlineError" },
8202
8834
  h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 WhatsApp \u673A\u5668\u4EBA\u72B6\u6001"),
8203
8835
  h2("p", null, model.error?.message),
8204
- h2(Button13, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
8836
+ h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
8205
8837
  )
8206
8838
  ) : h2(
8207
- React15.Fragment,
8839
+ React17.Fragment,
8208
8840
  null,
8209
8841
  provision?.status === "pending" ? h2(QrPanel5, {
8210
8842
  provision,
@@ -8225,7 +8857,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8225
8857
 
8226
8858
  // plugin-src/client/styles.js
8227
8859
  var IM_STYLE_ID = "xmanrui-dsh-im-settings";
8228
- var CSS10 = String.raw`
8860
+ var CSS11 = String.raw`
8229
8861
  .dim-page {
8230
8862
  --dim-blue: var(--dsw-alias-state-business-primary, #3370ff);
8231
8863
  --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent);
@@ -8237,9 +8869,9 @@ var CSS10 = String.raw`
8237
8869
  }
8238
8870
  .dim-page *, .dim-page *::before, .dim-page *::after { box-sizing: border-box; }
8239
8871
  .dim-title { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 18px; }
8240
- .dim-brand { min-width: 0; display: flex; align-items: center; gap: 12px; }
8241
- .dim-brandLogo { width: 48px; height: 48px; flex: 0 0 48px; display: block; object-fit: contain; filter: grayscale(1) contrast(1.08); }
8242
- .dim-title p { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 20px; font-weight: 600; white-space: nowrap; }
8872
+ .dim-brand { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
8873
+ .dim-brandName { color: var(--dsw-alias-label-primary, #1f2329); font-size: 20px; line-height: 24px; font-weight: 800; letter-spacing: .04em; }
8874
+ .dim-title p { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; font-weight: 500; white-space: nowrap; }
8243
8875
  .dim-githubAction { position: relative; display: inline-flex; flex: none; }
8244
8876
  .dim-githubLink { min-height: 30px; display: inline-flex; align-items: center; gap: 5px; flex: none; padding: 0 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); font-size: 12px; line-height: normal; font-weight: 560; text-decoration: none; transition: border-color .15s ease, color .15s ease, background .15s ease; }
8245
8877
  .dim-githubLink:hover { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
@@ -8269,14 +8901,17 @@ var CSS10 = String.raw`
8269
8901
  .dim-logoWecom svg { width: 22px; height: 22px; }
8270
8902
  .dim-logoTelegram { color: white; background: #229ed9; }
8271
8903
  .dim-logoTelegram svg { width: 21px; height: 21px; }
8904
+ .dim-logoOffice { color: white; background: linear-gradient(145deg, #12213f, #3964fe); }
8905
+ .dim-logoOffice svg { width: 23px; height: 23px; }
8272
8906
  .dim-logoDiscord { color: white; background: #5865f2; }
8273
8907
  .dim-logoDiscord svg { width: 21px; height: 21px; }
8274
8908
  .dim-logoSlack { color: white; background: #4a154b; }
8275
8909
  .dim-logoSlack svg { width: 21px; height: 21px; }
8276
8910
  .dim-logoWhatsapp { color: white; background: #25d366; }
8277
8911
  .dim-logoWhatsapp svg { width: 21px; height: 21px; }
8278
- .dim-channelCopy { min-width: 0; display: block; }
8912
+ .dim-channelCopy { min-width: 0; display: grid; }
8279
8913
  .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
8914
+ .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; }
8280
8915
  .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-border-l1, #eef0f3); }
8281
8916
  .dim-panel { min-width: 0; container-type: inline-size; }
8282
8917
  .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page, .dim-panel .dsl-page, .dim-panel .dwa-page { width: 100%; max-width: none; padding: 0 0 24px; }
@@ -8487,7 +9122,7 @@ function installImStyles() {
8487
9122
  const style = document.createElement("style");
8488
9123
  style.dataset.plugin = "@xmanrui/dsh-im";
8489
9124
  style.dataset.pluginCss = IM_STYLE_ID;
8490
- style.textContent = CSS10;
9125
+ style.textContent = CSS11;
8491
9126
  document.head.appendChild(style);
8492
9127
  return () => style.remove();
8493
9128
  }
@@ -8495,7 +9130,6 @@ function installImStyles() {
8495
9130
  // plugin-src/client/index.js
8496
9131
  var name = "im-settings";
8497
9132
  var inject = ["slots", "connection", "locale", "workspaces"];
8498
- var IM_PLUGIN_LOGO_URL = "data:image/png;base64,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";
8499
9133
  var CHANNELS = Object.freeze([
8500
9134
  { id: "weixin", label: "\u5FAE\u4FE1" },
8501
9135
  { id: "feishu", label: "\u98DE\u4E66" },
@@ -8505,7 +9139,8 @@ var CHANNELS = Object.freeze([
8505
9139
  { id: "slack", label: "Slack" },
8506
9140
  { id: "telegram", label: "Telegram" },
8507
9141
  { id: "discord", label: "Discord" },
8508
- { id: "whatsapp", label: "WhatsApp" }
9142
+ { id: "whatsapp", label: "WhatsApp" },
9143
+ { id: "office", label: "AI Office", note: "\uFF08\u5B9E\u9A8C\u529F\u80FD\uFF09" }
8509
9144
  ]);
8510
9145
  function WeixinLogo() {
8511
9146
  return h2(
@@ -8562,6 +9197,13 @@ function WhatsappLogo() {
8562
9197
  h2(WhatsappLogoGlyph)
8563
9198
  );
8564
9199
  }
9200
+ function OfficeLogo() {
9201
+ return h2(
9202
+ "span",
9203
+ { className: "dim-logo dim-logoOffice", "aria-hidden": "true" },
9204
+ h2(OfficeLogoGlyph)
9205
+ );
9206
+ }
8565
9207
  function ChannelLogo({ channel: channel4 }) {
8566
9208
  if (channel4 === "weixin") return h2(WeixinLogo);
8567
9209
  if (channel4 === "feishu") return h2(FeishuLogo);
@@ -8571,7 +9213,8 @@ function ChannelLogo({ channel: channel4 }) {
8571
9213
  if (channel4 === "slack") return h2(SlackLogo);
8572
9214
  if (channel4 === "telegram") return h2(TelegramLogo);
8573
9215
  if (channel4 === "discord") return h2(DiscordLogo);
8574
- return h2(WhatsappLogo);
9216
+ if (channel4 === "whatsapp") return h2(WhatsappLogo);
9217
+ return h2(OfficeLogo);
8575
9218
  }
8576
9219
  function IMSettingsTab({
8577
9220
  dingtalkRpcCall,
@@ -8583,10 +9226,11 @@ function IMSettingsTab({
8583
9226
  wecomRpcCall,
8584
9227
  weixinRpcCall,
8585
9228
  whatsappRpcCall,
9229
+ officeRpcCall,
8586
9230
  workspaceDirectoryPicker
8587
9231
  }) {
8588
- const [selected, setSelected] = React16.useState("weixin");
8589
- const githubTooltipId = React16.useId();
9232
+ const [selected, setSelected] = React18.useState("weixin");
9233
+ const githubTooltipId = React18.useId();
8590
9234
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
8591
9235
  return h2(
8592
9236
  WorkspaceDirectoryPickerContext.Provider,
@@ -8600,13 +9244,7 @@ function IMSettingsTab({
8600
9244
  h2(
8601
9245
  "div",
8602
9246
  { className: "dim-brand" },
8603
- h2("img", {
8604
- className: "dim-brandLogo",
8605
- src: IM_PLUGIN_LOGO_URL,
8606
- alt: "dsh-im",
8607
- width: 48,
8608
- height: 48
8609
- }),
9247
+ h2("strong", { className: "dim-brandName" }, "DSH-IM"),
8610
9248
  h2("p", null, "\u8BA9 DeepSeek Harness \u89E6\u624B\u53EF\u53CA")
8611
9249
  ),
8612
9250
  h2(
@@ -8654,7 +9292,8 @@ function IMSettingsTab({
8654
9292
  h2(
8655
9293
  "span",
8656
9294
  { className: "dim-channelCopy" },
8657
- h2("strong", null, channel4.label)
9295
+ h2("strong", null, channel4.label),
9296
+ channel4.note ? h2("small", { className: "dim-channelNote" }, channel4.note) : null
8658
9297
  )
8659
9298
  ))
8660
9299
  ),
@@ -8664,7 +9303,7 @@ function IMSettingsTab({
8664
9303
  role: "tabpanel",
8665
9304
  id: `dim-panel-${active.id}`,
8666
9305
  "aria-labelledby": `dim-tab-${active.id}`
8667
- }, active.id === "weixin" ? h2(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h2(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h2(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h2(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h2(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "slack" ? h2(SlackSettingsTab, { rpcCall: slackRpcCall }) : active.id === "telegram" ? h2(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : active.id === "discord" ? h2(DiscordSettingsTab, { rpcCall: discordRpcCall }) : h2(WhatsappSettingsTab, { rpcCall: whatsappRpcCall }))
9306
+ }, active.id === "weixin" ? h2(WeixinSettingsTab, { rpcCall: weixinRpcCall }) : active.id === "feishu" ? h2(FeishuSettingsTab, { rpcCall: feishuRpcCall }) : active.id === "dingtalk" ? h2(DingtalkSettingsTab, { rpcCall: dingtalkRpcCall }) : active.id === "wecom" ? h2(WecomSettingsTab, { rpcCall: wecomRpcCall }) : active.id === "qq" ? h2(QqSettingsTab, { rpcCall: qqRpcCall }) : active.id === "slack" ? h2(SlackSettingsTab, { rpcCall: slackRpcCall }) : active.id === "telegram" ? h2(TelegramSettingsTab, { rpcCall: telegramRpcCall }) : active.id === "discord" ? h2(DiscordSettingsTab, { rpcCall: discordRpcCall }) : active.id === "whatsapp" ? h2(WhatsappSettingsTab, { rpcCall: whatsappRpcCall }) : h2(OfficeSettingsTab, { rpcCall: officeRpcCall }))
8668
9307
  )
8669
9308
  )
8670
9309
  );
@@ -8686,6 +9325,7 @@ function apply(ctx) {
8686
9325
  installTelegramStyles(),
8687
9326
  installDiscordStyles(),
8688
9327
  installWhatsappStyles(),
9328
+ installOfficeStyles(),
8689
9329
  installImStyles()
8690
9330
  ];
8691
9331
  return () => {
@@ -8701,6 +9341,7 @@ function apply(ctx) {
8701
9341
  const discordRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(DISCORD_RPC_CHANNEL, endpoint, payload, signal);
8702
9342
  const whatsappRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
8703
9343
  const slackRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(SLACK_RPC_CHANNEL, endpoint, payload, signal);
9344
+ const officeRpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(OFFICE_RPC_CHANNEL, endpoint, payload, signal);
8704
9345
  const workspaceDirectoryPicker = Object.freeze({
8705
9346
  listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal),
8706
9347
  pickDirectory: () => ctx.workspaces.pickDirectory()
@@ -8721,6 +9362,7 @@ function apply(ctx) {
8721
9362
  wecomRpcCall,
8722
9363
  weixinRpcCall,
8723
9364
  whatsappRpcCall,
9365
+ officeRpcCall,
8724
9366
  workspaceDirectoryPicker
8725
9367
  })
8726
9368
  }, IMSettingsTab));