@xmanrui/dsh-im 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/THIRD_PARTY_NOTICES.md +15 -0
  4. package/bin/dsh-im.mjs +99 -0
  5. package/cordis.patch.yml +3 -0
  6. package/lib/client.js +6211 -0
  7. package/lib/index.js +13339 -0
  8. package/package.json +73 -0
  9. package/plugin-src/client/build.mjs +38 -0
  10. package/plugin-src/client/channel-logos.js +107 -0
  11. package/plugin-src/client/channels/dingtalk/api.js +203 -0
  12. package/plugin-src/client/channels/dingtalk/index.js +709 -0
  13. package/plugin-src/client/channels/dingtalk/styles.js +140 -0
  14. package/plugin-src/client/channels/discord/api.js +11 -0
  15. package/plugin-src/client/channels/discord/index.js +25 -0
  16. package/plugin-src/client/channels/discord/styles.js +19 -0
  17. package/plugin-src/client/channels/feishu/api.js +325 -0
  18. package/plugin-src/client/channels/feishu/index.js +986 -0
  19. package/plugin-src/client/channels/feishu/styles.js +505 -0
  20. package/plugin-src/client/channels/qq/api.js +119 -0
  21. package/plugin-src/client/channels/qq/index.js +402 -0
  22. package/plugin-src/client/channels/qq/styles.js +19 -0
  23. package/plugin-src/client/channels/shared/token-api.js +88 -0
  24. package/plugin-src/client/channels/shared/token-channel.js +277 -0
  25. package/plugin-src/client/channels/telegram/api.js +11 -0
  26. package/plugin-src/client/channels/telegram/index.js +25 -0
  27. package/plugin-src/client/channels/telegram/styles.js +19 -0
  28. package/plugin-src/client/channels/wecom/api.js +119 -0
  29. package/plugin-src/client/channels/wecom/index.js +402 -0
  30. package/plugin-src/client/channels/wecom/styles.js +19 -0
  31. package/plugin-src/client/channels/weixin/api.js +156 -0
  32. package/plugin-src/client/channels/weixin/index.js +577 -0
  33. package/plugin-src/client/channels/weixin/styles.js +120 -0
  34. package/plugin-src/client/credential-binding.js +117 -0
  35. package/plugin-src/client/index.js +192 -0
  36. package/plugin-src/client/lifecycle.js +86 -0
  37. package/plugin-src/client/styles.js +192 -0
  38. package/plugin-src/host/build.mjs +24 -0
  39. package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
  40. package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
  41. package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
  42. package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
  43. package/plugin-src/host/channels/discord/index.mjs +30 -0
  44. package/plugin-src/host/channels/discord/production.mjs +17 -0
  45. package/plugin-src/host/channels/discord/rpc.mjs +21 -0
  46. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
  47. package/plugin-src/host/channels/feishu/controller.mjs +172 -0
  48. package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
  49. package/plugin-src/host/channels/feishu/index.mjs +73 -0
  50. package/plugin-src/host/channels/feishu/production.mjs +138 -0
  51. package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
  52. package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
  53. package/plugin-src/host/channels/qq/index.mjs +30 -0
  54. package/plugin-src/host/channels/qq/production.mjs +109 -0
  55. package/plugin-src/host/channels/qq/rpc.mjs +146 -0
  56. package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
  57. package/plugin-src/host/channels/shared/production.mjs +104 -0
  58. package/plugin-src/host/channels/shared/rpc.mjs +119 -0
  59. package/plugin-src/host/channels/telegram/index.mjs +30 -0
  60. package/plugin-src/host/channels/telegram/production.mjs +17 -0
  61. package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
  62. package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
  63. package/plugin-src/host/channels/wecom/index.mjs +36 -0
  64. package/plugin-src/host/channels/wecom/production.mjs +113 -0
  65. package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
  66. package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
  67. package/plugin-src/host/channels/weixin/index.mjs +39 -0
  68. package/plugin-src/host/channels/weixin/production.mjs +119 -0
  69. package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
  70. package/plugin-src/host/index.mjs +44 -0
  71. package/plugin-src/host/rpc-authority.mjs +11 -0
  72. package/scripts/verify-package.mjs +97 -0
  73. package/src/channels/dingtalk/config-store.mjs +282 -0
  74. package/src/channels/dingtalk/device-auth.mjs +237 -0
  75. package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
  76. package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
  77. package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
  78. package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
  79. package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
  80. package/src/channels/dingtalk/harness-client.mjs +299 -0
  81. package/src/channels/dingtalk/state-store.mjs +212 -0
  82. package/src/channels/discord/config-store.mjs +24 -0
  83. package/src/channels/discord/discord-api.mjs +153 -0
  84. package/src/channels/discord/discord-bridge.mjs +15 -0
  85. package/src/channels/discord/discord-controller.mjs +16 -0
  86. package/src/channels/discord/discord-runtime.mjs +460 -0
  87. package/src/channels/discord/harness-client.mjs +3 -0
  88. package/src/channels/discord/state-store.mjs +3 -0
  89. package/src/channels/feishu/bridge.mjs +216 -0
  90. package/src/channels/feishu/config.mjs +71 -0
  91. package/src/channels/feishu/feishu-app.mjs +51 -0
  92. package/src/channels/feishu/feishu-channel.mjs +153 -0
  93. package/src/channels/feishu/feishu-runtime.mjs +222 -0
  94. package/src/channels/feishu/harness-client.mjs +268 -0
  95. package/src/channels/feishu/message-utils.mjs +51 -0
  96. package/src/channels/feishu/multi-bot-controller.mjs +686 -0
  97. package/src/channels/feishu/plugin-config-store.mjs +204 -0
  98. package/src/channels/feishu/plugin-controller.mjs +248 -0
  99. package/src/channels/feishu/registration-manager.mjs +345 -0
  100. package/src/channels/feishu/state-store.mjs +71 -0
  101. package/src/channels/qq/config-store.mjs +171 -0
  102. package/src/channels/qq/harness-client.mjs +3 -0
  103. package/src/channels/qq/qq-bridge.mjs +177 -0
  104. package/src/channels/qq/qq-controller.mjs +452 -0
  105. package/src/channels/qq/qq-runtime.mjs +208 -0
  106. package/src/channels/qq/qr-auth.mjs +24 -0
  107. package/src/channels/qq/state-store.mjs +96 -0
  108. package/src/channels/shared/conversation-state-store.mjs +107 -0
  109. package/src/channels/shared/editable-message-stream.mjs +81 -0
  110. package/src/channels/shared/text-harness-bridge.mjs +180 -0
  111. package/src/channels/shared/token-bot-controller.mjs +298 -0
  112. package/src/channels/shared/token-config-store.mjs +180 -0
  113. package/src/channels/telegram/config-store.mjs +24 -0
  114. package/src/channels/telegram/harness-client.mjs +3 -0
  115. package/src/channels/telegram/state-store.mjs +3 -0
  116. package/src/channels/telegram/telegram-api.mjs +120 -0
  117. package/src/channels/telegram/telegram-bridge.mjs +15 -0
  118. package/src/channels/telegram/telegram-controller.mjs +16 -0
  119. package/src/channels/telegram/telegram-runtime.mjs +278 -0
  120. package/src/channels/wecom/config-store.mjs +170 -0
  121. package/src/channels/wecom/harness-client.mjs +3 -0
  122. package/src/channels/wecom/qr-auth.mjs +95 -0
  123. package/src/channels/wecom/state-store.mjs +90 -0
  124. package/src/channels/wecom/wecom-bridge.mjs +243 -0
  125. package/src/channels/wecom/wecom-controller.mjs +474 -0
  126. package/src/channels/wecom/wecom-runtime.mjs +209 -0
  127. package/src/channels/weixin/config-store.mjs +182 -0
  128. package/src/channels/weixin/harness-client.mjs +259 -0
  129. package/src/channels/weixin/state-store.mjs +112 -0
  130. package/src/channels/weixin/weixin-api.mjs +319 -0
  131. package/src/channels/weixin/weixin-bridge.mjs +173 -0
  132. package/src/channels/weixin/weixin-controller.mjs +545 -0
  133. package/src/channels/weixin/weixin-runtime.mjs +204 -0
@@ -0,0 +1,192 @@
1
+ export const IM_STYLE_ID = 'xmanrui-dsh-im-settings';
2
+
3
+ const CSS = String.raw`
4
+ .dim-page {
5
+ --dim-blue: #3370ff;
6
+ --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent);
7
+ width: 100%;
8
+ max-width: 1080px;
9
+ padding: 2px 0 30px;
10
+ color: var(--dsw-alias-label-primary, #1f2329);
11
+ box-sizing: border-box;
12
+ }
13
+ .dim-page *, .dim-page *::before, .dim-page *::after { box-sizing: border-box; }
14
+ .dim-title { margin: 0 0 26px; }
15
+ .dim-title p { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 20px; white-space: nowrap; }
16
+ .dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
17
+ .dim-rail { max-height: 520px; display: grid; align-content: start; gap: 8px; overflow-y: auto; padding: 1px 4px 1px 1px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-line-border, #dfe1e5) transparent; }
18
+ .dim-rail::-webkit-scrollbar { width: 4px; }
19
+ .dim-rail::-webkit-scrollbar-thumb { border-radius: 99px; background: var(--dsw-alias-line-border, #dfe1e5); }
20
+ .dim-channel { width: 100%; min-height: 48px; display: grid; grid-template-columns: 30px minmax(0, 1fr); align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--dsw-alias-line-border, #eef0f3); border-radius: 14px; color: inherit; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 2px 8px rgb(31 35 41 / 3%); font: inherit; text-align: left; cursor: pointer; transition: border-color .16s ease, background .16s ease, box-shadow .16s ease; }
21
+ .dim-channel:hover { border-color: color-mix(in srgb, var(--dim-blue) 25%, var(--dsw-alias-line-border, #eef0f3)); background: color-mix(in srgb, var(--dim-blue) 2%, var(--dsw-alias-bg-layer-3, #fff)); box-shadow: 0 5px 16px rgb(31 35 41 / 5%); }
22
+ .dim-channel[aria-selected="true"] { border-color: color-mix(in srgb, var(--dim-blue) 43%, white); color: var(--dim-blue); background: color-mix(in srgb, var(--dim-blue) 9%, white); box-shadow: 0 3px 12px rgb(51 112 255 / 7%); }
23
+ .dim-channel:focus-visible { outline: none; border-color: color-mix(in srgb, var(--dim-blue) 72%, white); box-shadow: 0 0 0 1px color-mix(in srgb, var(--dim-blue) 24%, transparent) inset, 0 3px 12px rgb(51 112 255 / 7%); }
24
+ .dim-logo { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 9px; box-shadow: 0 1px 3px rgb(31 35 41 / 7%); }
25
+ .dim-logo svg { display: block; width: 20px; height: 20px; }
26
+ .dim-logoWeixin { color: white; background: #07c160; }
27
+ .dim-logoWeixin svg { width: 19px; height: 19px; }
28
+ .dim-logoFeishu { background: white; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); }
29
+ .dim-logoFeishu svg { width: 28px; height: 28px; }
30
+ .dim-logoDingtalk { color: white; background: #1677ff; }
31
+ .dim-logoDingtalk svg { width: 24px; height: 24px; }
32
+ .dim-logoQq { color: white; background: #1677ff; }
33
+ .dim-logoQq svg { width: 21px; height: 21px; }
34
+ .dim-logoWecom { background: white; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); }
35
+ .dim-logoWecom svg { width: 22px; height: 22px; }
36
+ .dim-logoTelegram { color: white; background: #229ed9; }
37
+ .dim-logoTelegram svg { width: 21px; height: 21px; }
38
+ .dim-logoDiscord { color: white; background: #5865f2; }
39
+ .dim-logoDiscord svg { width: 21px; height: 21px; }
40
+ .dim-channelCopy { min-width: 0; display: block; }
41
+ .dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
42
+ .dim-divider { width: 1px; min-height: 520px; background: var(--dsw-alias-line-divider, #eef0f3); }
43
+ .dim-panel { min-width: 0; container-type: inline-size; }
44
+ .dim-panel .bxf-page, .dim-panel .dxw-page, .dim-panel .ddt-page, .dim-panel .dqq-page, .dim-panel .dwecom-page { width: 100%; max-width: none; padding: 0 0 24px; }
45
+ .dim-panel .bxf-heading, .dim-panel .dxw-heading, .dim-panel .ddt-heading { justify-content: flex-end; }
46
+ .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { width: 100%; justify-content: space-between; }
47
+ .dim-panel .dim-bindActions { min-width: 0; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; }
48
+ .dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #1677ff; border-radius: 8px; color: #fff; background: #1677ff; box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; white-space: nowrap; }
49
+ .dim-panel .bxf-headingTools .dim-scanButton:hover:not(:disabled), .dim-panel .dxw-tools .dim-scanButton:hover:not(:disabled), .dim-panel .ddt-tools .dim-scanButton:hover:not(:disabled) { border-color: #0958d9; background: #0958d9; }
50
+ .dim-panel .dim-credentialButton { flex: none; min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; border: 1px solid #86909c; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-body, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 5%); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
51
+ .dim-panel .dim-actionIcon { width: 15px; height: 15px; flex: 0 0 15px; }
52
+ .dim-panel .dim-credentialButton:hover:not(:disabled) { border-color: #4e5969; background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
53
+ .dim-panel .dim-credentialButton[aria-pressed="true"] { border-color: #4e5969; background: var(--dsw-alias-fill-secondary, #f2f3f5); box-shadow: inset 0 0 0 1px rgb(78 89 105 / 8%); }
54
+ .dim-panel .bxf-headingTools .dim-onlineBadge, .dim-panel .dxw-tools .dim-onlineBadge, .dim-panel .ddt-tools .dim-onlineBadge { flex: none; min-height: 30px; display: inline-flex; align-items: center; gap: 0; padding: 0 11px; border: 0; border-radius: 999px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-fill-secondary, #f2f3f5); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
55
+ .dim-panel .dim-channelPage { width: 100%; max-width: none; display: flex; flex-direction: column; gap: 18px; padding: 0 0 24px; color: var(--dsw-alias-label-primary, #1f2329); box-sizing: border-box; }
56
+ .dim-panel .dim-surfaceCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-body, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
57
+ .dim-panel .dim-surfaceCard::before { display: none; }
58
+ .dim-panel .dim-surfaceBody { padding: 24px; }
59
+ .dim-panel .dim-credentialPanel { display: grid; gap: 18px; padding: 20px; }
60
+ .dim-panel .dim-credentialTitle { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: 1.35; font-weight: 650; }
61
+ .dim-panel .dim-credentialForm { min-width: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 12px; }
62
+ .dim-panel .dim-credentialFormSingle { grid-template-columns: minmax(0, 1fr); }
63
+ .dim-panel .dim-credentialField { min-width: 0; display: grid; gap: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; font-weight: 560; }
64
+ .dim-panel .dim-credentialField input { width: 100%; min-width: 0; height: 38px; padding: 0 11px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 8px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-body, #fff); font: 13px ui-monospace, SFMono-Regular, Menlo, monospace; transition: border-color .16s ease, box-shadow .16s ease; }
65
+ .dim-panel .dim-credentialField input:focus { border-color: #4e5969; box-shadow: 0 0 0 3px rgb(78 89 105 / 10%); }
66
+ .dim-panel .dim-credentialField input::placeholder { color: var(--dsw-alias-label-tertiary, #8f959e); font-family: inherit; }
67
+ .dim-panel .dim-credentialError, .dim-panel .dim-credentialActions { grid-column: 1 / -1; }
68
+ .dim-panel .dim-credentialError { margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.5; }
69
+ .dim-panel .dim-credentialActions { margin-top: 0; }
70
+ .dim-panel .dim-listSection { display: flex; flex-direction: column; gap: 0; }
71
+ .dim-panel .dim-listHeading { min-height: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 2px 0 9px; padding: 0; }
72
+ .dim-panel .dim-listHeading h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: normal; font-weight: 650; }
73
+ .dim-panel .dim-botList { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
74
+ .dim-panel .dim-loadingView { padding: 38px; color: var(--dsw-alias-label-secondary, #646a73); text-align: center; }
75
+ .dim-panel .dim-loadingView h3 { margin: 0 0 7px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 17px; line-height: normal; font-weight: 650; }
76
+ .dim-panel .dim-loadingView p { margin: 0; line-height: 1.6; }
77
+ .dim-panel .dim-spinner { width: 24px; height: 24px; margin: 0 auto 13px; border: 3px solid var(--dsw-alias-line-border, #e6e8eb); border-top-color: #1677ff; border-radius: 50%; animation: dim-spin .8s linear infinite; }
78
+ @keyframes dim-spin { to { transform: rotate(360deg); } }
79
+ .dim-panel .dim-emptyView { min-height: 230px; display: grid; grid-template-columns: minmax(0, 1fr) 180px; align-items: center; gap: 30px; }
80
+ .dim-panel .dim-emptyCopy { min-width: 0; }
81
+ .dim-panel .dim-emptyCopy h3 { margin: 8px 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 1.35; font-weight: 650; }
82
+ .dim-panel .dim-emptyCopy > p { max-width: 560px; margin: 0; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.65; }
83
+ .dim-panel .dim-emptyBrand { width: 110px; height: 110px; display: grid; place-items: center; justify-self: center; border-radius: 28px; box-shadow: 0 18px 45px rgb(22 119 255 / 18%); }
84
+ .dim-panel .dim-stateLabel { display: inline-flex; align-items: center; gap: 8px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; font-weight: 600; }
85
+ .dim-panel .dim-stateDot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-label-tertiary, #8f959e); box-shadow: none; }
86
+ .dim-panel .dim-stateDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); }
87
+ .dim-panel .dim-stateDot[data-tone="warning"] { background: var(--dsw-alias-state-warning-primary, #d97706); }
88
+ .dim-panel .dim-stateDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
89
+ .dim-panel .dim-viewActions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
90
+ .dim-panel .dim-viewActions .bxf-button, .dim-panel .dim-viewActions .dxw-button, .dim-panel .dim-viewActions .ddt-button { min-height: 34px; padding: 0 13px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-body, #fff); box-shadow: none; font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
91
+ .dim-panel .dim-viewActions .bxf-button[data-kind="primary"], .dim-panel .dim-viewActions .dxw-button[data-kind="primary"], .dim-panel .dim-viewActions .ddt-button[data-kind="primary"] { border-color: #1677ff; color: #fff; background: #1677ff; box-shadow: none; }
92
+ .dim-panel .dim-viewActions .bxf-button[data-kind="danger"], .dim-panel .dim-viewActions .dxw-button[data-kind="danger"], .dim-panel .dim-viewActions .ddt-button[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
93
+ .dim-panel .dim-qrLayout { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 34px; align-items: start; }
94
+ .dim-panel .dim-qrColumn { width: 100%; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 12px; }
95
+ .dim-panel .dim-qrFrame { position: relative; width: min(270px, 100%); aspect-ratio: 1; display: grid; place-items: center; overflow: hidden; padding: 10px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 16px; background: #fff; }
96
+ .dim-panel .dim-qrFrame::before { content: ""; position: absolute; inset: 7px; z-index: 0; border: 1px solid color-mix(in srgb, #1677ff 16%, var(--dsw-alias-line-border, #dfe1e5)); border-radius: 12px; pointer-events: none; }
97
+ .dim-panel .dim-qrFrame::after { display: none; }
98
+ .dim-panel .dim-qrFrame img { position: relative; z-index: 1; width: 100%; height: 100%; display: block; object-fit: contain; }
99
+ .dim-panel .dim-qrFallback { position: relative; z-index: 1; display: grid; place-items: center; gap: 8px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 13px; line-height: 1.5; text-align: center; }
100
+ .dim-panel .dim-qrExpired { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; padding: 20px; color: var(--dsw-alias-label-primary, #1f2329); background: rgb(255 255 255 / 92%); font-size: 15px; line-height: 1.6; font-weight: 650; text-align: center; white-space: pre-line; backdrop-filter: blur(3px); }
101
+ .dim-panel .dim-countdown { width: min(270px, 100%); margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
102
+ .dim-panel .dim-countdownTop { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 6px; }
103
+ .dim-panel .dim-countdownTop strong { color: var(--dsw-alias-label-primary, #1f2329); font-weight: 650; }
104
+ .dim-panel .dim-progress { height: 4px; overflow: hidden; margin: 0; border-radius: 99px; background: var(--dsw-alias-fill-secondary, #eef0f3); }
105
+ .dim-panel .dim-progress span { display: block; width: var(--bxf-progress, var(--dxw-progress, var(--ddt-progress, 0%))); height: 100%; border-radius: inherit; background: #1677ff; transition: width .25s linear; }
106
+ .dim-panel .dim-qrCopy { min-width: 0; overflow-wrap: anywhere; }
107
+ .dim-panel .dim-qrCopy h3 { margin: 9px 0 8px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 1.35; font-weight: 650; }
108
+ .dim-panel .dim-qrCopy > p { margin: 0; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.65; }
109
+ .dim-panel .dim-steps { margin: 18px 0 16px; padding: 0; list-style: none; counter-reset: dim-step; }
110
+ .dim-panel .dim-steps li { position: relative; min-height: 28px; display: flex; align-items: center; padding: 5px 0 5px 36px; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.5; counter-increment: dim-step; }
111
+ .dim-panel .dim-steps li::before { content: counter(dim-step); position: absolute; left: 0; top: 4px; width: 25px; height: 25px; display: grid; place-items: center; border-radius: 8px; color: #1677ff; background: color-mix(in srgb, #1677ff 10%, white); font-size: 12px; font-weight: 650; }
112
+ .dim-panel .dim-specialView { padding: 32px; text-align: center; }
113
+ .dim-panel .dim-statusNotice { display: flex; align-items: flex-start; gap: 10px; padding: 13px 15px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 22%, white); border-radius: 10px; color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 6%, white); font-size: 13px; line-height: 1.5; }
114
+ .dim-panel .dim-inlineError { display: flex; align-items: flex-start; flex-direction: column; gap: 10px; padding: 22px; color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 6%, white); }
115
+ .dim-panel .dim-inlineError > div { min-width: 0; }
116
+ .dim-panel .dim-inlineError h3 { margin: 0; color: inherit; font-size: 17px; line-height: 1.35; font-weight: 650; }
117
+ .dim-panel .dim-inlineError p { margin: 7px 0 0; color: inherit; line-height: 1.6; }
118
+ .dim-panel .dim-confirm { padding: 18px 24px; border-top: 1px solid var(--dsw-alias-line-divider, #eef0f3); background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
119
+ .dim-panel .dim-confirm strong, .dim-panel .dim-confirm h4 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 1.4; font-weight: 650; }
120
+ .dim-panel .dim-confirm p { margin: 7px 0 0; color: var(--dsw-alias-label-secondary, #646a73); line-height: 1.6; }
121
+ .dim-panel .dim-cardFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-line-divider, #eef0f3); }
122
+ .dim-panel .dim-cardSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font: inherit; font-size: 12px; font-weight: 400; line-height: normal; }
123
+ .dim-panel .dim-cardActions { flex: none; display: flex; align-items: center; flex-wrap: nowrap; gap: 8px; margin: 0 0 0 auto; }
124
+ .dim-panel .dim-cardActions .dim-cardAction { flex: none; min-height: 34px; padding: 0 13px; border: 1px solid var(--dsw-alias-line-border, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-body, #fff); font: inherit; font-size: 13px; font-weight: 560; line-height: normal; white-space: nowrap; }
125
+ .dim-panel .dim-cardActions .dim-cardAction:hover:not(:disabled) { border-color: #aeb3bb; background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
126
+ .dim-panel .dim-cardActions .dim-cardAction[data-kind="danger"] { color: var(--dsw-alias-state-error-primary, #d54941); }
127
+ .dim-panel .dim-botCard { position: relative; overflow: hidden; border: 1px solid var(--dsw-alias-line-border, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-body, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
128
+ .dim-panel .dim-botCard::before { display: none; }
129
+ .dim-panel .dim-botCardBody { position: relative; padding: 24px; }
130
+ .dim-panel .dim-botCardTop { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
131
+ .dim-panel .dim-botIdentity { min-width: 0; display: flex; align-items: center; gap: 12px; }
132
+ .dim-panel .dim-botAvatar { flex: none; width: 42px; height: 42px; display: grid; place-items: center; overflow: hidden; border-radius: 12px; box-shadow: none; }
133
+ .dim-panel .dim-botAvatar svg { width: 29px; height: 29px; }
134
+ .dim-panel .dim-botName { min-width: 0; }
135
+ .dim-panel .dim-botName h3 { overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; font-weight: 650; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
136
+ .dim-panel .dim-botName p { overflow: hidden; margin: 4px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
137
+ .dim-panel .dim-botCard .dim-botHealth { flex: none; min-height: 0; display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; border-radius: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
138
+ .dim-panel .dim-botCard .dim-healthDot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: #aeb3bb; box-shadow: none; }
139
+ .dim-panel .dim-botCard .dim-healthDot[data-tone="success"] { background: var(--dsw-alias-state-success-primary, #20a162); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary, #20a162) 14%, transparent); }
140
+ .dim-panel .dim-botCard .dim-healthDot[data-tone="warning"] { background: var(--dsw-alias-state-warning-primary, #d97706); }
141
+ .dim-panel .dim-botCard .dim-healthDot[data-tone="error"] { background: var(--dsw-alias-state-error-primary, #d54941); }
142
+ .dim-panel .dim-botMetrics { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 20px 0; }
143
+ .dim-panel .dim-botMetric { min-width: 0; padding: 12px; border: 0; border-radius: 9px; background: var(--dsw-alias-fill-tertiary, #f7f8fa); }
144
+ .dim-panel .dim-botMetric dt { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; font-weight: 400; line-height: normal; }
145
+ .dim-panel .dim-botMetric dd { overflow: hidden; margin: 5px 0 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 13px; font-weight: 400; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
146
+ .dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
147
+ .dim-panel .ddt-headingCopy { display: none; }
148
+ .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
149
+ @container (max-width: 680px) {
150
+ .dim-panel .dim-credentialForm { grid-template-columns: minmax(0, 1fr); }
151
+ .dim-panel .dim-credentialError, .dim-panel .dim-credentialActions { grid-column: auto; }
152
+ .dim-panel .dim-emptyView { min-height: 0; grid-template-columns: minmax(0, 1fr); }
153
+ .dim-panel .dim-emptyBrand { display: none; }
154
+ .dim-panel .dim-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; }
155
+ .dim-panel .dim-qrColumn { width: 100%; min-width: 0; }
156
+ .dim-panel .dim-qrCopy { width: 100%; min-width: 0; overflow-wrap: anywhere; }
157
+ .dim-panel .ddt-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 24px; }
158
+ .dim-panel .ddt-qrColumn { width: 100%; min-width: 0; }
159
+ .dim-panel .ddt-qrCopy { width: 100%; min-width: 0; overflow-wrap: anywhere; }
160
+ }
161
+ @media (max-width: 840px) {
162
+ .dim-title { align-items: flex-start; }
163
+ .dim-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
164
+ .dim-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
165
+ .dim-divider { display: none; }
166
+ .dim-rail { max-height: none; overflow: visible; padding-right: 1px; }
167
+ .dim-channel { min-height: 48px; }
168
+ }
169
+ @media (max-width: 720px) {
170
+ .dim-panel .dim-botCardTop { flex-direction: column; align-items: stretch; }
171
+ }
172
+ @media (max-width: 560px) {
173
+ .dim-title { flex-direction: column; gap: 10px; }
174
+ .dim-title p { white-space: normal; }
175
+ .dim-rail { grid-template-columns: minmax(0, 1fr); }
176
+ }
177
+ @media (prefers-reduced-motion: reduce) {
178
+ .dim-page * { transition-duration: .01ms !important; }
179
+ }
180
+ `;
181
+
182
+ export function installImStyles() {
183
+ if (typeof document === 'undefined') return () => {};
184
+ const existing = document.querySelector(`style[data-plugin-css="${IM_STYLE_ID}"]`);
185
+ if (existing) return () => {};
186
+ const style = document.createElement('style');
187
+ style.dataset.plugin = '@xmanrui/dsh-im';
188
+ style.dataset.pluginCss = IM_STYLE_ID;
189
+ style.textContent = CSS;
190
+ document.head.appendChild(style);
191
+ return () => style.remove();
192
+ }
@@ -0,0 +1,24 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ import { build } from 'esbuild';
6
+
7
+ const sourceDirectory = dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = resolve(sourceDirectory, '../..');
9
+ const outputPath = resolve(packageRoot, 'lib/index.js');
10
+
11
+ await mkdir(dirname(outputPath), { recursive: true });
12
+ await build({
13
+ entryPoints: [resolve(sourceDirectory, 'index.mjs')],
14
+ bundle: true,
15
+ format: 'esm',
16
+ platform: 'node',
17
+ target: ['node22'],
18
+ packages: 'external',
19
+ outfile: outputPath,
20
+ sourcemap: false,
21
+ legalComments: 'none',
22
+ });
23
+
24
+ console.log(`Wrote ${outputPath}`);
@@ -0,0 +1,130 @@
1
+ const DEFAULT_RETRY_DELAYS_MS = Object.freeze([250, 1_000, 3_000, 5_000, 10_000, 30_000]);
2
+
3
+ function retryDelays(value) {
4
+ if (!Array.isArray(value) || value.length === 0) return [...DEFAULT_RETRY_DELAYS_MS];
5
+ const valid = value.filter((delay) => Number.isFinite(delay) && delay >= 0);
6
+ return valid.length > 0 ? valid : [...DEFAULT_RETRY_DELAYS_MS];
7
+ }
8
+
9
+ export class ConnectionSupervisor {
10
+ #controller;
11
+ #harness;
12
+ #logger;
13
+ #retryDelays;
14
+ #healthyIntervalMs;
15
+ #setTimeout;
16
+ #clearTimeout;
17
+ #timer = null;
18
+ #running = null;
19
+ #retryIndex = 0;
20
+ #closed = false;
21
+ #started = false;
22
+ #ready;
23
+ #resolveReady;
24
+
25
+ constructor({
26
+ controller,
27
+ harness,
28
+ logger = console,
29
+ retryDelaysMs,
30
+ healthyIntervalMs = 15_000,
31
+ setTimeoutImpl = setTimeout,
32
+ clearTimeoutImpl = clearTimeout,
33
+ }) {
34
+ if (!controller || typeof controller.initialize !== 'function' || typeof controller.status !== 'function') {
35
+ throw new TypeError('ConnectionSupervisor requires a controller');
36
+ }
37
+ if (!harness || typeof harness.ensureRunning !== 'function') {
38
+ throw new TypeError('ConnectionSupervisor requires a Harness client');
39
+ }
40
+ this.#controller = controller;
41
+ this.#harness = harness;
42
+ this.#logger = logger;
43
+ this.#retryDelays = retryDelays(retryDelaysMs);
44
+ this.#healthyIntervalMs = Number.isFinite(healthyIntervalMs) && healthyIntervalMs >= 0
45
+ ? healthyIntervalMs
46
+ : 15_000;
47
+ this.#setTimeout = setTimeoutImpl;
48
+ this.#clearTimeout = clearTimeoutImpl;
49
+ this.#ready = new Promise((resolve) => {
50
+ this.#resolveReady = resolve;
51
+ });
52
+ }
53
+
54
+ get ready() {
55
+ return this.#ready;
56
+ }
57
+
58
+ start() {
59
+ if (this.#started || this.#closed) return this;
60
+ this.#started = true;
61
+ this.#schedule(0);
62
+ return this;
63
+ }
64
+
65
+ async close() {
66
+ if (this.#closed) return;
67
+ this.#closed = true;
68
+ if (this.#timer !== null) this.#clearTimeout(this.#timer);
69
+ this.#timer = null;
70
+ await this.#running?.catch(() => undefined);
71
+ this.#resolveReady?.(null);
72
+ this.#resolveReady = null;
73
+ }
74
+
75
+ #schedule(delayMs) {
76
+ if (this.#closed) return;
77
+ this.#timer = this.#setTimeout(() => {
78
+ this.#timer = null;
79
+ void this.#run();
80
+ }, delayMs);
81
+ this.#timer?.unref?.();
82
+ }
83
+
84
+ async #run() {
85
+ if (this.#closed || this.#running) return;
86
+ const operation = this.#reconcile();
87
+ this.#running = operation;
88
+ try {
89
+ await operation;
90
+ } finally {
91
+ if (this.#running === operation) this.#running = null;
92
+ }
93
+ }
94
+
95
+ async #reconcile() {
96
+ try {
97
+ await this.#harness.ensureRunning();
98
+ if (this.#closed) return;
99
+ const status = await this.#controller.initialize();
100
+ if (this.#closed) return;
101
+ this.#resolveReady?.(status);
102
+ this.#resolveReady = null;
103
+ const { configured, connected } = status.totals;
104
+ if (connected < configured) {
105
+ const delayMs = this.#retryDelays[Math.min(this.#retryIndex, this.#retryDelays.length - 1)];
106
+ this.#retryIndex += 1;
107
+ this.#logger.warn?.(
108
+ `[dsh-dingtalk] ${connected}/${configured} bots connected; retrying in ${delayMs}ms`,
109
+ );
110
+ this.#schedule(delayMs);
111
+ return;
112
+ }
113
+ this.#retryIndex = 0;
114
+ this.#schedule(this.#healthyIntervalMs);
115
+ } catch (error) {
116
+ if (this.#closed) return;
117
+ const delayMs = this.#retryDelays[Math.min(this.#retryIndex, this.#retryDelays.length - 1)];
118
+ this.#retryIndex += 1;
119
+ this.#logger.warn?.(
120
+ `[dsh-dingtalk] connection reconciliation failed; retrying in ${delayMs}ms`,
121
+ error,
122
+ );
123
+ this.#schedule(delayMs);
124
+ }
125
+ }
126
+ }
127
+
128
+ export function createConnectionSupervisor(options) {
129
+ return new ConnectionSupervisor(options);
130
+ }
@@ -0,0 +1,39 @@
1
+ import { createProductionController } from './production.mjs';
2
+ import { installDingtalkRpc } from './rpc.mjs';
3
+
4
+ export const name = 'dsh-dingtalk-host';
5
+ export const inject = ['connection', 'credentials', 'webServer'];
6
+
7
+ export async function apply(ctx, config = {}) {
8
+ if (config?.controller) {
9
+ return installDingtalkRpc(ctx, config.controller, config.rpcOptions, config.rpcAuthority);
10
+ }
11
+
12
+ const production = await createProductionController(ctx, config, config.internals);
13
+ const disposeRpc = installDingtalkRpc(
14
+ ctx,
15
+ production.controller,
16
+ config.rpcOptions,
17
+ config.rpcAuthority,
18
+ );
19
+ ctx.effect(() => async () => {
20
+ await production.close();
21
+ }, 'dsh-dingtalk: close bot connections');
22
+ return disposeRpc;
23
+ }
24
+
25
+ export function createDingtalkHostPlugin(config) {
26
+ return Object.freeze({ name, inject, apply: (ctx) => apply(ctx, config) });
27
+ }
28
+
29
+ export { createConnectionSupervisor, ConnectionSupervisor } from './connection-supervisor.mjs';
30
+ export { createProductionController } from './production.mjs';
31
+ export {
32
+ DINGTALK_ENDPOINTS,
33
+ DINGTALK_RPC_CHANNEL,
34
+ DINGTALK_RPC_ENDPOINTS,
35
+ createDingtalkRpcHandler,
36
+ installDingtalkRpc,
37
+ } from './rpc.mjs';
38
+ export { DingtalkController } from '../../../../src/channels/dingtalk/dingtalk-controller.mjs';
39
+ export { DingtalkRuntime } from '../../../../src/channels/dingtalk/dingtalk-runtime.mjs';
@@ -0,0 +1,122 @@
1
+ import { unlink } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join, resolve } from 'node:path';
4
+
5
+ import { DingtalkConfigStore } from '../../../../src/channels/dingtalk/config-store.mjs';
6
+ import { DingtalkDeviceAuth } from '../../../../src/channels/dingtalk/device-auth.mjs';
7
+ import { DingtalkController } from '../../../../src/channels/dingtalk/dingtalk-controller.mjs';
8
+ import { DingtalkRuntime } from '../../../../src/channels/dingtalk/dingtalk-runtime.mjs';
9
+ import { HarnessClient } from '../../../../src/channels/dingtalk/harness-client.mjs';
10
+ import { DingtalkStateStore } from '../../../../src/channels/dingtalk/state-store.mjs';
11
+ import { createConnectionSupervisor } from './connection-supervisor.mjs';
12
+
13
+ function harnessOrigin(webServer, configured) {
14
+ if (configured !== undefined) return new URL(configured);
15
+ const port = webServer?.port;
16
+ if (!Number.isInteger(port) || port < 1 || port > 65_535) {
17
+ throw new Error('dsh-dingtalk requires an initialized DSH webServer port');
18
+ }
19
+ return new URL(`http://127.0.0.1:${port}`);
20
+ }
21
+
22
+ function pluginPaths(config) {
23
+ const dshHome = resolve(config.dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh'));
24
+ const root = resolve(config.dataDir ?? join(dshHome, 'integrations', 'dsh-dingtalk'));
25
+ return {
26
+ root,
27
+ config: resolve(config.configPath ?? join(root, 'config.json')),
28
+ bots: resolve(config.botsDir ?? join(root, 'bots')),
29
+ };
30
+ }
31
+
32
+ export async function createProductionController(ctx, config = {}, internals = {}) {
33
+ if (!ctx?.credentials) throw new TypeError('dsh-dingtalk requires ctx.credentials');
34
+ if (!ctx?.webServer) throw new TypeError('dsh-dingtalk requires ctx.webServer');
35
+
36
+ const ConfigStore = internals.ConfigStore ?? DingtalkConfigStore;
37
+ const DeviceAuth = internals.DeviceAuth ?? DingtalkDeviceAuth;
38
+ const StateStore = internals.StateStore ?? DingtalkStateStore;
39
+ const Harness = internals.HarnessClient ?? HarnessClient;
40
+ const Controller = internals.Controller ?? DingtalkController;
41
+ const Runtime = internals.Runtime ?? DingtalkRuntime;
42
+ const createSupervisor = internals.createConnectionSupervisor ?? createConnectionSupervisor;
43
+ const logger = typeof ctx.logger === 'function'
44
+ ? ctx.logger('dsh-dingtalk')
45
+ : (ctx.logger ?? console);
46
+ const paths = pluginPaths(config);
47
+ const configStore = await new ConfigStore(paths.config).load();
48
+ const deviceAuth = internals.deviceAuth ?? new DeviceAuth({
49
+ baseUrl: config.registrationBaseUrl,
50
+ });
51
+ const stateStores = new Map();
52
+
53
+ const statePath = (botId) => resolve(paths.bots, botId, 'state.json');
54
+ const stateFor = async (botId) => {
55
+ let state = stateStores.get(botId);
56
+ if (!state) {
57
+ state = await new StateStore(statePath(botId)).load();
58
+ stateStores.set(botId, state);
59
+ }
60
+ return state;
61
+ };
62
+ const harness = new Harness({
63
+ baseUrl: harnessOrigin(ctx.webServer, config.harnessBaseUrl),
64
+ workspace: resolve(config.workspace ?? process.cwd()),
65
+ agentPreset: config.agentPreset ?? 'standard',
66
+ autostart: false,
67
+ dshBin: config.dshBin ?? 'dsh',
68
+ });
69
+ const controller = new Controller({
70
+ deviceAuth,
71
+ credentials: ctx.credentials,
72
+ configStore,
73
+ logger,
74
+ createRuntime: async ({ botId, config: botConfig, clientSecret }) => {
75
+ const state = await stateFor(botId);
76
+ return new Runtime({
77
+ config: botConfig,
78
+ clientSecret,
79
+ harness,
80
+ state,
81
+ replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
82
+ maxMessageChars: config.maxMessageChars ?? 4_000,
83
+ connectTimeoutMs: config.connectTimeoutMs ?? 15_000,
84
+ logger: {
85
+ error: (...args) => logger.error?.(`[${botId}]`, ...args),
86
+ warn: (...args) => logger.warn?.(`[${botId}]`, ...args),
87
+ info: (...args) => logger.info?.(`[${botId}]`, ...args),
88
+ debug: (...args) => logger.debug?.(`[${botId}]`, ...args),
89
+ },
90
+ });
91
+ },
92
+ deleteState: async ({ botId }) => {
93
+ const state = stateStores.get(botId);
94
+ stateStores.delete(botId);
95
+ if (state && typeof state.remove === 'function') {
96
+ await state.remove();
97
+ return;
98
+ }
99
+ try {
100
+ await unlink(statePath(botId));
101
+ } catch (error) {
102
+ if (error?.code !== 'ENOENT') throw error;
103
+ }
104
+ },
105
+ });
106
+ const supervisor = createSupervisor({
107
+ controller,
108
+ harness,
109
+ logger,
110
+ retryDelaysMs: config.retryDelaysMs,
111
+ healthyIntervalMs: config.healthyIntervalMs,
112
+ }).start();
113
+ return {
114
+ controller,
115
+ ready: supervisor.ready,
116
+ async close() {
117
+ await supervisor.close();
118
+ await controller.close();
119
+ harness.stopManagedProcess();
120
+ },
121
+ };
122
+ }