@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.
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/bin/dsh-im.mjs +99 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +6211 -0
- package/lib/index.js +13339 -0
- package/package.json +73 -0
- package/plugin-src/client/build.mjs +38 -0
- package/plugin-src/client/channel-logos.js +107 -0
- package/plugin-src/client/channels/dingtalk/api.js +203 -0
- package/plugin-src/client/channels/dingtalk/index.js +709 -0
- package/plugin-src/client/channels/dingtalk/styles.js +140 -0
- package/plugin-src/client/channels/discord/api.js +11 -0
- package/plugin-src/client/channels/discord/index.js +25 -0
- package/plugin-src/client/channels/discord/styles.js +19 -0
- package/plugin-src/client/channels/feishu/api.js +325 -0
- package/plugin-src/client/channels/feishu/index.js +986 -0
- package/plugin-src/client/channels/feishu/styles.js +505 -0
- package/plugin-src/client/channels/qq/api.js +119 -0
- package/plugin-src/client/channels/qq/index.js +402 -0
- package/plugin-src/client/channels/qq/styles.js +19 -0
- package/plugin-src/client/channels/shared/token-api.js +88 -0
- package/plugin-src/client/channels/shared/token-channel.js +277 -0
- package/plugin-src/client/channels/telegram/api.js +11 -0
- package/plugin-src/client/channels/telegram/index.js +25 -0
- package/plugin-src/client/channels/telegram/styles.js +19 -0
- package/plugin-src/client/channels/wecom/api.js +119 -0
- package/plugin-src/client/channels/wecom/index.js +402 -0
- package/plugin-src/client/channels/wecom/styles.js +19 -0
- package/plugin-src/client/channels/weixin/api.js +156 -0
- package/plugin-src/client/channels/weixin/index.js +577 -0
- package/plugin-src/client/channels/weixin/styles.js +120 -0
- package/plugin-src/client/credential-binding.js +117 -0
- package/plugin-src/client/index.js +192 -0
- package/plugin-src/client/lifecycle.js +86 -0
- package/plugin-src/client/styles.js +192 -0
- package/plugin-src/host/build.mjs +24 -0
- package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
- package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
- package/plugin-src/host/channels/discord/index.mjs +30 -0
- package/plugin-src/host/channels/discord/production.mjs +17 -0
- package/plugin-src/host/channels/discord/rpc.mjs +21 -0
- package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
- package/plugin-src/host/channels/feishu/controller.mjs +172 -0
- package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
- package/plugin-src/host/channels/feishu/index.mjs +73 -0
- package/plugin-src/host/channels/feishu/production.mjs +138 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
- package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/qq/index.mjs +30 -0
- package/plugin-src/host/channels/qq/production.mjs +109 -0
- package/plugin-src/host/channels/qq/rpc.mjs +146 -0
- package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
- package/plugin-src/host/channels/shared/production.mjs +104 -0
- package/plugin-src/host/channels/shared/rpc.mjs +119 -0
- package/plugin-src/host/channels/telegram/index.mjs +30 -0
- package/plugin-src/host/channels/telegram/production.mjs +17 -0
- package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
- package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/wecom/index.mjs +36 -0
- package/plugin-src/host/channels/wecom/production.mjs +113 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
- package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
- package/plugin-src/host/channels/weixin/index.mjs +39 -0
- package/plugin-src/host/channels/weixin/production.mjs +119 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
- package/plugin-src/host/index.mjs +44 -0
- package/plugin-src/host/rpc-authority.mjs +11 -0
- package/scripts/verify-package.mjs +97 -0
- package/src/channels/dingtalk/config-store.mjs +282 -0
- package/src/channels/dingtalk/device-auth.mjs +237 -0
- package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
- package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
- package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
- package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
- package/src/channels/dingtalk/harness-client.mjs +299 -0
- package/src/channels/dingtalk/state-store.mjs +212 -0
- package/src/channels/discord/config-store.mjs +24 -0
- package/src/channels/discord/discord-api.mjs +153 -0
- package/src/channels/discord/discord-bridge.mjs +15 -0
- package/src/channels/discord/discord-controller.mjs +16 -0
- package/src/channels/discord/discord-runtime.mjs +460 -0
- package/src/channels/discord/harness-client.mjs +3 -0
- package/src/channels/discord/state-store.mjs +3 -0
- package/src/channels/feishu/bridge.mjs +216 -0
- package/src/channels/feishu/config.mjs +71 -0
- package/src/channels/feishu/feishu-app.mjs +51 -0
- package/src/channels/feishu/feishu-channel.mjs +153 -0
- package/src/channels/feishu/feishu-runtime.mjs +222 -0
- package/src/channels/feishu/harness-client.mjs +268 -0
- package/src/channels/feishu/message-utils.mjs +51 -0
- package/src/channels/feishu/multi-bot-controller.mjs +686 -0
- package/src/channels/feishu/plugin-config-store.mjs +204 -0
- package/src/channels/feishu/plugin-controller.mjs +248 -0
- package/src/channels/feishu/registration-manager.mjs +345 -0
- package/src/channels/feishu/state-store.mjs +71 -0
- package/src/channels/qq/config-store.mjs +171 -0
- package/src/channels/qq/harness-client.mjs +3 -0
- package/src/channels/qq/qq-bridge.mjs +177 -0
- package/src/channels/qq/qq-controller.mjs +452 -0
- package/src/channels/qq/qq-runtime.mjs +208 -0
- package/src/channels/qq/qr-auth.mjs +24 -0
- package/src/channels/qq/state-store.mjs +96 -0
- package/src/channels/shared/conversation-state-store.mjs +107 -0
- package/src/channels/shared/editable-message-stream.mjs +81 -0
- package/src/channels/shared/text-harness-bridge.mjs +180 -0
- package/src/channels/shared/token-bot-controller.mjs +298 -0
- package/src/channels/shared/token-config-store.mjs +180 -0
- package/src/channels/telegram/config-store.mjs +24 -0
- package/src/channels/telegram/harness-client.mjs +3 -0
- package/src/channels/telegram/state-store.mjs +3 -0
- package/src/channels/telegram/telegram-api.mjs +120 -0
- package/src/channels/telegram/telegram-bridge.mjs +15 -0
- package/src/channels/telegram/telegram-controller.mjs +16 -0
- package/src/channels/telegram/telegram-runtime.mjs +278 -0
- package/src/channels/wecom/config-store.mjs +170 -0
- package/src/channels/wecom/harness-client.mjs +3 -0
- package/src/channels/wecom/qr-auth.mjs +95 -0
- package/src/channels/wecom/state-store.mjs +90 -0
- package/src/channels/wecom/wecom-bridge.mjs +243 -0
- package/src/channels/wecom/wecom-controller.mjs +474 -0
- package/src/channels/wecom/wecom-runtime.mjs +209 -0
- package/src/channels/weixin/config-store.mjs +182 -0
- package/src/channels/weixin/harness-client.mjs +259 -0
- package/src/channels/weixin/state-store.mjs +112 -0
- package/src/channels/weixin/weixin-api.mjs +319 -0
- package/src/channels/weixin/weixin-bridge.mjs +173 -0
- package/src/channels/weixin/weixin-controller.mjs +545 -0
- 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
|
+
}
|