dsh-connect 0.7.2 → 0.9.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 (153) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +196 -43
  3. package/README.zh.md +171 -42
  4. package/client/client.js +817 -0
  5. package/client/client.js.map +7 -0
  6. package/client/locale.mjs +227 -0
  7. package/client/panel-state.mjs +54 -0
  8. package/client/settings-client.mjs +389 -0
  9. package/docs/images/settings-advanced-en.png +0 -0
  10. package/docs/images/settings-advanced-zh.png +0 -0
  11. package/docs/images/settings-defaults-en.png +0 -0
  12. package/docs/images/settings-defaults-zh.png +0 -0
  13. package/docs/images/settings-overview-en.png +0 -0
  14. package/docs/images/settings-overview-zh.png +0 -0
  15. package/examples/minimal.config.json +16 -0
  16. package/lib/binding.d.ts.map +1 -1
  17. package/lib/binding.js +2 -2
  18. package/lib/binding.js.map +1 -1
  19. package/lib/channels/dingtalk/adapter.d.ts +59 -0
  20. package/lib/channels/dingtalk/adapter.d.ts.map +1 -0
  21. package/lib/channels/dingtalk/adapter.js +123 -0
  22. package/lib/channels/dingtalk/adapter.js.map +1 -0
  23. package/lib/channels/dingtalk/i18n.d.ts +14 -0
  24. package/lib/channels/dingtalk/i18n.d.ts.map +1 -0
  25. package/lib/channels/dingtalk/i18n.js +16 -0
  26. package/lib/channels/dingtalk/i18n.js.map +1 -0
  27. package/lib/channels/dingtalk/index.d.ts +127 -0
  28. package/lib/channels/dingtalk/index.d.ts.map +1 -0
  29. package/lib/channels/dingtalk/index.js +144 -0
  30. package/lib/channels/dingtalk/index.js.map +1 -0
  31. package/lib/channels/dingtalk/message.d.ts +49 -0
  32. package/lib/channels/dingtalk/message.d.ts.map +1 -0
  33. package/lib/channels/dingtalk/message.js +50 -0
  34. package/lib/channels/dingtalk/message.js.map +1 -0
  35. package/lib/channels/dingtalk/stomp.d.ts +36 -0
  36. package/lib/channels/dingtalk/stomp.d.ts.map +1 -0
  37. package/lib/channels/dingtalk/stomp.js +107 -0
  38. package/lib/channels/dingtalk/stomp.js.map +1 -0
  39. package/lib/channels/dingtalk/stream.d.ts +53 -0
  40. package/lib/channels/dingtalk/stream.d.ts.map +1 -0
  41. package/lib/channels/dingtalk/stream.js +199 -0
  42. package/lib/channels/dingtalk/stream.js.map +1 -0
  43. package/lib/channels/dingtalk/webhook.d.ts +84 -0
  44. package/lib/channels/dingtalk/webhook.d.ts.map +1 -0
  45. package/lib/channels/dingtalk/webhook.js +143 -0
  46. package/lib/channels/dingtalk/webhook.js.map +1 -0
  47. package/lib/channels/feishu/adapter.d.ts +133 -0
  48. package/lib/channels/feishu/adapter.d.ts.map +1 -0
  49. package/lib/channels/feishu/adapter.js +662 -0
  50. package/lib/channels/feishu/adapter.js.map +1 -0
  51. package/lib/channels/feishu/i18n.d.ts +30 -0
  52. package/lib/channels/feishu/i18n.d.ts.map +1 -0
  53. package/lib/channels/feishu/i18n.js +42 -0
  54. package/lib/channels/feishu/i18n.js.map +1 -0
  55. package/lib/channels/feishu/index.d.ts +82 -0
  56. package/lib/channels/feishu/index.d.ts.map +1 -0
  57. package/lib/channels/feishu/index.js +103 -0
  58. package/lib/channels/feishu/index.js.map +1 -0
  59. package/lib/channels/feishu/onboard.d.ts +16 -0
  60. package/lib/channels/feishu/onboard.d.ts.map +1 -0
  61. package/lib/channels/feishu/onboard.js +86 -0
  62. package/lib/channels/feishu/onboard.js.map +1 -0
  63. package/lib/channels/telegram/adapter.d.ts +70 -0
  64. package/lib/channels/telegram/adapter.d.ts.map +1 -0
  65. package/lib/channels/telegram/adapter.js +460 -0
  66. package/lib/channels/telegram/adapter.js.map +1 -0
  67. package/lib/channels/telegram/client.d.ts +115 -0
  68. package/lib/channels/telegram/client.d.ts.map +1 -0
  69. package/lib/channels/telegram/client.js +198 -0
  70. package/lib/channels/telegram/client.js.map +1 -0
  71. package/lib/channels/telegram/i18n.d.ts +14 -0
  72. package/lib/channels/telegram/i18n.d.ts.map +1 -0
  73. package/lib/channels/telegram/i18n.js +18 -0
  74. package/lib/channels/telegram/i18n.js.map +1 -0
  75. package/lib/channels/telegram/index.d.ts +46 -0
  76. package/lib/channels/telegram/index.d.ts.map +1 -0
  77. package/lib/channels/telegram/index.js +54 -0
  78. package/lib/channels/telegram/index.js.map +1 -0
  79. package/lib/channels/web/adapter.d.ts +103 -0
  80. package/lib/channels/web/adapter.d.ts.map +1 -0
  81. package/lib/channels/web/adapter.js +161 -0
  82. package/lib/channels/web/adapter.js.map +1 -0
  83. package/lib/channels/web/index.d.ts +47 -0
  84. package/lib/channels/web/index.d.ts.map +1 -0
  85. package/lib/channels/web/index.js +59 -0
  86. package/lib/channels/web/index.js.map +1 -0
  87. package/lib/chat-key.d.ts +29 -0
  88. package/lib/chat-key.d.ts.map +1 -0
  89. package/lib/chat-key.js +38 -0
  90. package/lib/chat-key.js.map +1 -0
  91. package/lib/index.d.ts +78 -11
  92. package/lib/index.d.ts.map +1 -1
  93. package/lib/index.js +312 -9
  94. package/lib/index.js.map +1 -1
  95. package/lib/runner.d.ts +55 -2
  96. package/lib/runner.d.ts.map +1 -1
  97. package/lib/runner.js +158 -23
  98. package/lib/runner.js.map +1 -1
  99. package/lib/scheduler.d.ts.map +1 -1
  100. package/lib/scheduler.js +2 -2
  101. package/lib/scheduler.js.map +1 -1
  102. package/lib/service.d.ts +17 -1
  103. package/lib/service.d.ts.map +1 -1
  104. package/lib/service.js +47 -3
  105. package/lib/service.js.map +1 -1
  106. package/lib/settings/channel-runtime.d.ts +75 -0
  107. package/lib/settings/channel-runtime.d.ts.map +1 -0
  108. package/lib/settings/channel-runtime.js +165 -0
  109. package/lib/settings/channel-runtime.js.map +1 -0
  110. package/lib/settings/channels.d.ts +62 -0
  111. package/lib/settings/channels.d.ts.map +1 -0
  112. package/lib/settings/channels.js +132 -0
  113. package/lib/settings/channels.js.map +1 -0
  114. package/lib/settings/credential-store.d.ts +85 -0
  115. package/lib/settings/credential-store.d.ts.map +1 -0
  116. package/lib/settings/credential-store.js +142 -0
  117. package/lib/settings/credential-store.js.map +1 -0
  118. package/lib/settings/index.d.ts +14 -0
  119. package/lib/settings/index.d.ts.map +1 -0
  120. package/lib/settings/index.js +14 -0
  121. package/lib/settings/index.js.map +1 -0
  122. package/lib/settings/namespace.d.ts +175 -0
  123. package/lib/settings/namespace.d.ts.map +1 -0
  124. package/lib/settings/namespace.js +200 -0
  125. package/lib/settings/namespace.js.map +1 -0
  126. package/lib/settings/rpc-client.d.ts +36 -0
  127. package/lib/settings/rpc-client.d.ts.map +1 -0
  128. package/lib/settings/rpc-client.js +45 -0
  129. package/lib/settings/rpc-client.js.map +1 -0
  130. package/lib/settings/secret-disclosure.d.ts +50 -0
  131. package/lib/settings/secret-disclosure.d.ts.map +1 -0
  132. package/lib/settings/secret-disclosure.js +132 -0
  133. package/lib/settings/secret-disclosure.js.map +1 -0
  134. package/lib/settings/settings-model.d.ts +124 -0
  135. package/lib/settings/settings-model.d.ts.map +1 -0
  136. package/lib/settings/settings-model.js +118 -0
  137. package/lib/settings/settings-model.js.map +1 -0
  138. package/lib/settings/settings-rpc.d.ts +130 -0
  139. package/lib/settings/settings-rpc.d.ts.map +1 -0
  140. package/lib/settings/settings-rpc.js +233 -0
  141. package/lib/settings/settings-rpc.js.map +1 -0
  142. package/lib/settings/settings-service.d.ts +35 -0
  143. package/lib/settings/settings-service.d.ts.map +1 -0
  144. package/lib/settings/settings-service.js +171 -0
  145. package/lib/settings/settings-service.js.map +1 -0
  146. package/lib/state-dir.d.ts +28 -0
  147. package/lib/state-dir.d.ts.map +1 -0
  148. package/lib/state-dir.js +33 -0
  149. package/lib/state-dir.js.map +1 -0
  150. package/lib/stream.d.ts.map +1 -1
  151. package/lib/stream.js +3 -2
  152. package/lib/stream.js.map +1 -1
  153. package/package.json +56 -12
@@ -0,0 +1,389 @@
1
+ /**
2
+ * dsh-connect client settings plugin.
3
+ *
4
+ * Registers a "settings.section" in the DeepSeek Harness web UI, mirroring
5
+ * xmanrui/dsh-im (plugin-src/client/index.js). The pane talks to the host over
6
+ * the `/dsh-connect` channel via `ctx.connection.rpc.call`. All the data logic
7
+ * (snapshot<->form mapping, save-payload building) lives in the tested
8
+ * `settings-model.js` / `rpc-client.js` helpers; this component is a thin
9
+ * renderer. Build the lib first (`pnpm --filter dsh-connect build`), then
10
+ * bundle this in a `dsh web` shell (Vite).
11
+ *
12
+ * The pane is an accordion, not a flat list. Four channels with every field
13
+ * visible is ~2000px of content in a ~690px scroller, so the channel cards
14
+ * collapse, rarely-touched fields hide behind a second-level "advanced" fold,
15
+ * and a tab strip at the top jumps to (and opens) a channel. The rules for
16
+ * which cards start open live in `panel-state.mjs` — pure, and testable without
17
+ * a DOM, which matters because the bundle test's React stub can only supply a
18
+ * component's hook values, never observe what it initializes them to.
19
+ */
20
+ import * as React from 'react';
21
+
22
+ import { SETTINGS_RPC_CHANNEL } from '../lib/settings/settings-rpc.js';
23
+ import { loadSettings, saveSettings, saveCredentials } from '../lib/settings/rpc-client.js';
24
+ import { snapshotToForm, buildConfigSave, buildCredentialSaves, CHANNEL_SECRET_FIELDS, CHANNEL_CONFIG_FIELDS, CHANNEL_DEFAULT_FIELDS, coerceConfigValue } from '../lib/settings/settings-model.js';
25
+ // Shared with the host, so the pane and the masking it displays can never
26
+ // disagree about which keys are confidential.
27
+ import { isMaskedSecret } from '../lib/settings/secret-disclosure.js';
28
+ import { LOCALES, tr, optionalText } from './locale.mjs';
29
+ import { initialOpenChannels, toggleInSet, isAdvanced } from './panel-state.mjs';
30
+
31
+ export const name = 'dsh-connect-settings';
32
+ export const inject = ['slots', 'connection', 'locale'];
33
+ export const NS = 'dsh-connect';
34
+
35
+ export const locale = LOCALES;
36
+
37
+ const h = React.createElement;
38
+
39
+ /** The channels the pane knows about, in the order the cards are rendered. */
40
+ const ALL_CHANNELS = Object.keys(CHANNEL_SECRET_FIELDS);
41
+
42
+ // Self-contained scoped stylesheet for the pane (injected once). The host's own
43
+ // settings panes use per-feature CSS modules we can't import into this separate
44
+ // bundle, so we ship our own consistent, theme-aware styles instead. Everything
45
+ // is scoped under `.dsh-connect-settings` so nothing leaks into the shell.
46
+ //
47
+ // Two things here are load-bearing rather than cosmetic:
48
+ //
49
+ // 1. The `box-sizing` reset. Neither this bundle nor the host shell ships a
50
+ // global one, so under the default `content-box` a `.ds-input` with
51
+ // `width:100%` is ~20px (padding + border) *wider* than the grid column it
52
+ // sits in, and paints over the neighbouring column's text. That overflow is
53
+ // what "文字跟窗口重叠" actually was.
54
+ // 2. The host's theme tokens, with our old hex values as fallbacks. The host
55
+ // puts `--dsw-alias-*` on `body` / `body[data-ds-dark-theme]`, so inheriting
56
+ // them follows the user's in-app theme. A `prefers-color-scheme` block — what
57
+ // used to be here — ignores that choice entirely and repaints the pane black
58
+ // on a light shell for anyone whose OS is dark. Do not bring it back.
59
+ const STYLE = `
60
+ .dsh-connect-settings,.dsh-connect-settings *,.dsh-connect-settings *::before,.dsh-connect-settings *::after{box-sizing:border-box}
61
+ .dsh-connect-settings{--ds-bg:var(--dsw-alias-bg-layer-3,#ffffff);--ds-bg-sub:var(--dsw-alias-bg-layer-1,#f6f7f9);--ds-text:var(--dsw-alias-label-primary,#1f2329);--ds-muted:var(--dsw-alias-label-tertiary,#646a73);--ds-border:var(--dsw-alias-border-l2,#e2e4e8);--ds-border-2:var(--dsw-alias-border-l3,#c8cbd0);--ds-accent:var(--dsw-alias-state-business-primary,#3b82f6);--ds-hover:var(--dsw-alias-interactive-bg-hover,#2631480f);display:flex;flex-direction:column;gap:12px;max-width:760px;color:var(--ds-text);font:13px/1.6 -apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}
62
+ .dsh-connect-settings .ds-card{display:flex;flex-direction:column;gap:10px;border:1px solid var(--ds-border);border-radius:10px;background:var(--ds-bg);padding:12px 14px}
63
+ .dsh-connect-settings .ds-card-title{margin:0;font-size:13px;font-weight:600}
64
+ .dsh-connect-settings .ds-note{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted)}
65
+ .dsh-connect-settings .ds-tabs{display:flex;flex-wrap:wrap;gap:6px}
66
+ .dsh-connect-settings .ds-tab{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:1px solid var(--ds-border);border-radius:14px;background:transparent;color:var(--ds-text);font:inherit;font-size:12px;cursor:pointer}
67
+ .dsh-connect-settings .ds-tab:hover{background:var(--ds-hover)}
68
+ .dsh-connect-settings .ds-tab[aria-expanded=true]{border-color:var(--ds-accent);color:var(--ds-accent)}
69
+ .dsh-connect-settings .ds-dot{width:6px;height:6px;border-radius:50%;background:var(--ds-border-2);flex:none}
70
+ .dsh-connect-settings .ds-dot[data-on="1"]{background:var(--ds-accent)}
71
+ .dsh-connect-settings .ds-channel{border:1px solid var(--ds-border);border-radius:8px;background:var(--ds-bg-sub)}
72
+ .dsh-connect-settings .ds-channel-head{display:flex;align-items:center;gap:8px;padding:5px 10px}
73
+ .dsh-connect-settings .ds-enable{display:flex;align-items:center;flex:none}
74
+ .dsh-connect-settings .ds-channel-toggle{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:5px 6px;border:0;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;font-weight:600;text-align:left;cursor:pointer}
75
+ .dsh-connect-settings .ds-channel-toggle:hover{background:var(--ds-hover);color:var(--ds-accent)}
76
+ .dsh-connect-settings .ds-channel-name{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
77
+ .dsh-connect-settings .ds-channel-body{display:flex;flex-direction:column;gap:8px;padding:0 10px 10px}
78
+ .dsh-connect-settings .ds-chevron{width:7px;height:7px;margin:-3px 4px 0 auto;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg);transition:transform .15s ease;flex:none}
79
+ .dsh-connect-settings [aria-expanded=true]>.ds-chevron,.dsh-connect-settings [aria-expanded=true] .ds-chevron{transform:rotate(-135deg);margin-top:2px}
80
+ .dsh-connect-settings .ds-badge{flex:none;font-size:11px;font-weight:500;padding:1px 8px;border-radius:99px;background:var(--ds-bg);color:var(--ds-muted);border:1px solid var(--ds-border-2)}
81
+ .dsh-connect-settings .ds-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px 14px}
82
+ .dsh-connect-settings .ds-field{display:flex;flex-direction:column;gap:4px;min-width:0}
83
+ .dsh-connect-settings .ds-control{display:flex;flex-direction:column;gap:4px;font-size:12px;color:var(--ds-muted)}
84
+ .dsh-connect-settings .ds-control.ds-check-field{flex-direction:row;align-items:center;gap:6px}
85
+ .dsh-connect-settings .ds-hint{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted)}
86
+ .dsh-connect-settings .ds-preview{margin:0;font-size:11px;line-height:1.5;color:var(--ds-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;word-break:break-all}
87
+ .dsh-connect-settings .ds-preview b{font-weight:500;font-family:inherit;opacity:.75}
88
+ .dsh-connect-settings .ds-check{flex:none;width:16px;height:16px;accent-color:var(--ds-accent)}
89
+ .dsh-connect-settings .ds-input{height:30px;width:100%;min-width:0;padding:0 9px;border:1px solid var(--ds-border-2);border-radius:6px;background:var(--ds-bg);color:var(--ds-text);font:inherit}
90
+ .dsh-connect-settings select.ds-input{cursor:pointer}
91
+ .dsh-connect-settings .ds-input:focus{outline:none;border-color:var(--ds-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--ds-accent) 25%,transparent)}
92
+ .dsh-connect-settings .ds-adv{display:flex;flex-direction:column;gap:8px}
93
+ .dsh-connect-settings .ds-advanced-toggle{align-self:flex-start;display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border:1px dashed var(--ds-border-2);border-radius:99px;background:transparent;color:var(--ds-muted);font:inherit;font-size:11px;cursor:pointer}
94
+ .dsh-connect-settings .ds-advanced-toggle:hover{background:var(--ds-hover);color:var(--ds-text)}
95
+ .dsh-connect-settings .ds-footer{position:sticky;bottom:0;z-index:1;display:flex;align-items:center;gap:12px;margin:0 -14px -12px;padding:10px 14px;border-top:1px solid var(--ds-border);border-radius:0 0 9px 9px;background:var(--ds-bg)}
96
+ .dsh-connect-settings .ds-btn{height:32px;padding:0 18px;border:0;border-radius:6px;background:var(--dsw-alias-button-primary-fill,var(--ds-accent));color:var(--dsw-alias-label-primary-foreground,#ffffff);font:inherit;font-weight:500;cursor:pointer}
97
+ .dsh-connect-settings .ds-btn:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--ds-accent))}
98
+ .dsh-connect-settings .ds-btn:disabled{opacity:.55;cursor:default}
99
+ .dsh-connect-settings .ds-status{font-size:12px;color:var(--ds-muted)}
100
+ `;
101
+
102
+ function injectStyles() {
103
+ if (typeof document === 'undefined') return;
104
+ if (document.getElementById('dsh-connect-settings-style')) return;
105
+ const el = document.createElement('style');
106
+ el.id = 'dsh-connect-settings-style';
107
+ el.textContent = STYLE;
108
+ document.head.appendChild(el);
109
+ }
110
+
111
+ // Render one non-secret config field: its control, then its explanation.
112
+ //
113
+ // Label and hint are looked up by *config key* (`f.<key>`), so a field added
114
+ // without its text shows a legible fallback (`field.label`) instead of a raw
115
+ // identifier — and the locale-coverage test fails, which is the real guard.
116
+ function renderConfigField(field, value, onChange, t) {
117
+ const label = tr(t, `f.${field.key}`, field.label ?? field.key);
118
+ const hint = optionalText(t, `f.${field.key}.hint`);
119
+ let control;
120
+ if (field.kind === 'boolean') {
121
+ control = h('label', { className: 'ds-control ds-check-field' },
122
+ h('input', { className: 'ds-check', type: 'checkbox', checked: !!value, onChange: (e) => onChange(e.target.checked) }),
123
+ ' ' + label);
124
+ } else if (field.kind === 'select') {
125
+ control = h('label', { className: 'ds-control' }, label + ' ',
126
+ h('select', { className: 'ds-input', value: value ?? '', onChange: (e) => onChange(e.target.value) },
127
+ h('option', { value: '' }, tr(t, 'o.default', label)),
128
+ ...(field.options ?? []).map((opt) => h('option', { key: opt, value: opt }, tr(t, `o.${opt}`, opt)))));
129
+ } else if (field.kind === 'number') {
130
+ control = h('label', { className: 'ds-control' }, label + ' ',
131
+ h('input', { className: 'ds-input', type: 'number', value: value ?? '', onChange: (e) => onChange(e.target.value) }));
132
+ } else {
133
+ control = h('label', { className: 'ds-control' }, label + ' ',
134
+ h('input', { className: 'ds-input', type: 'text', value: value ?? '', onChange: (e) => onChange(e.target.value) }));
135
+ }
136
+ return h('div', { className: 'ds-field', key: `cfg-${field.key}` },
137
+ control,
138
+ hint ? h('p', { className: 'ds-hint' }, hint) : null);
139
+ }
140
+
141
+ // Render one secret field: a write-only input, plus a read-only preview of the
142
+ // stored value so the user can confirm what they configured.
143
+ //
144
+ // The preview is plain text, never the input's `value`. The host already masked
145
+ // it, and seeding an input with a mask would let a save write the mask *back* as
146
+ // the credential. Keeping the input blank also keeps "empty" meaning "leave the
147
+ // stored value alone", which is what a password field is expected to do.
148
+ function renderSecretField(ch, field, form, onChange, t) {
149
+ const preview = form.secretPreviews?.[ch]?.[field] ?? '';
150
+ const hint = optionalText(t, `s.${ch}.${field}.hint`);
151
+ return h('div', { className: 'ds-field', key: `sec-${ch}-${field}` },
152
+ h('label', { className: 'ds-control' },
153
+ tr(t, `s.${ch}.${field}`, field),
154
+ h('input', {
155
+ className: 'ds-input',
156
+ // Confidential keys stay masked while typing; identifiers (appId,
157
+ // clientId) don't, so a typo is visible before it is ever saved.
158
+ type: isMaskedSecret(field) ? 'password' : 'text',
159
+ autoComplete: 'off',
160
+ placeholder: preview ? t('configured') : t('notConfigured'),
161
+ value: form.secrets?.[ch]?.[field] ?? '',
162
+ onChange: (e) => onChange(e.target.value),
163
+ })),
164
+ h('p', { className: 'ds-preview' },
165
+ h('b', null, t('current')),
166
+ preview === '' ? t('notConfigured') : preview),
167
+ hint ? h('p', { className: 'ds-hint' }, hint) : null);
168
+ }
169
+
170
+ // One channel card: an enable checkbox, a header that folds the card, and — when
171
+ // open — its credentials and settings.
172
+ //
173
+ // A plain function rather than a child component, deliberately: the bundle test
174
+ // supplies hook values *positionally*, so every `useState` in the tree has to
175
+ // live in `ConnectSettingsTab` in a fixed order. Nothing in here may call a hook.
176
+ function renderChannel(ch, ctx) {
177
+ const { form, creds, t, open, advOverride, setChannels, setField, setChannelConfig, toggleOpen, toggleAdvanced } = ctx;
178
+ const name = tr(t, `channel.${ch}`, ch);
179
+ const channelHint = optionalText(t, `channel.${ch}.hint`);
180
+ const isOpen = open.has(ch);
181
+ const advOpen = advOverride?.has(ch) ?? false;
182
+ const configFields = CHANNEL_CONFIG_FIELDS[ch] ?? [];
183
+ // The split is what keeps a common case on one screen: two credentials and the
184
+ // behavioural switches, with the set-once fields one disclosure deeper.
185
+ const common = configFields.filter((f) => !isAdvanced(ch, f.key));
186
+ const advanced = configFields.filter((f) => isAdvanced(ch, f.key));
187
+
188
+ const configField = (field) => renderConfigField(
189
+ field,
190
+ form.channelConfigs?.[ch]?.[field.key],
191
+ (raw) => setChannelConfig(ch, field.key, raw),
192
+ t,
193
+ );
194
+
195
+ return h('div', { className: 'ds-channel', key: ch, id: `ds-ch-${ch}` },
196
+ // The checkbox sits beside the header button, not inside it: a button may not
197
+ // contain another interactive element, and browsers disagree about what to do
198
+ // when one does.
199
+ h('div', { className: 'ds-channel-head' },
200
+ h('label', { className: 'ds-enable' },
201
+ h('input', {
202
+ className: 'ds-check',
203
+ type: 'checkbox',
204
+ 'aria-label': name,
205
+ checked: form.channels.includes(ch),
206
+ onChange: (e) => setChannels(ch, e.target.checked),
207
+ })),
208
+ h('button', {
209
+ type: 'button',
210
+ className: 'ds-channel-toggle',
211
+ 'aria-expanded': isOpen,
212
+ // Only while the body exists: pointing at an id that is not in the
213
+ // document is worse than not pointing at all.
214
+ 'aria-controls': isOpen ? `ds-ch-${ch}-body` : undefined,
215
+ 'aria-label': `${name} — ${isOpen ? t('collapse') : t('expand')}`,
216
+ onClick: () => toggleOpen(ch),
217
+ },
218
+ h('span', { className: 'ds-channel-name' }, name),
219
+ h('span', { className: 'ds-badge' }, creds[ch] ? t('reachable') : t('unreachable')),
220
+ // Drawn in CSS, never a text node: the bundle test reads rendered text as
221
+ // user-visible copy, and a `▾` here would be collected as a stray string.
222
+ h('span', { className: 'ds-chevron' }))),
223
+ // Collapsed means *not rendered*, not `display:none`. The inputs are
224
+ // controlled from the parent's `form.secrets`, so folding a card cannot lose
225
+ // an unsaved secret — keeping it mounted would buy nothing.
226
+ isOpen ? h('div', { className: 'ds-channel-body', id: `ds-ch-${ch}-body` },
227
+ channelHint ? h('p', { className: 'ds-note' }, channelHint) : null,
228
+ h('div', { className: 'ds-fields' },
229
+ ...CHANNEL_SECRET_FIELDS[ch].map((field) => renderSecretField(ch, field, form, (value) => setField(ch, field, value), t)),
230
+ ...common.map(configField)),
231
+ advanced.length === 0 ? null : h('div', { className: 'ds-adv' },
232
+ h('button', {
233
+ type: 'button',
234
+ className: 'ds-advanced-toggle',
235
+ 'aria-expanded': advOpen,
236
+ 'aria-controls': advOpen ? `ds-ch-${ch}-adv` : undefined,
237
+ onClick: () => toggleAdvanced(ch),
238
+ },
239
+ h('span', null, t('advanced')),
240
+ h('span', { className: 'ds-chevron' })),
241
+ advOpen ? h('div', { className: 'ds-fields', id: `ds-ch-${ch}-adv` }, ...advanced.map(configField)) : null),
242
+ ) : null);
243
+ }
244
+
245
+ // Thin React renderer over the tested settings-model helpers.
246
+ export function ConnectSettingsTab({ rpcCall, t }) {
247
+ const [form, setForm] = React.useState(null);
248
+ const [status, setStatus] = React.useState('loading');
249
+ const [creds, setCreds] = React.useState({});
250
+ const [openOverride, setOpenOverride] = React.useState(null);
251
+ const [advOverride, setAdvOverride] = React.useState(null);
252
+ const rpc = (endpoint, payload) => rpcCall(endpoint, payload);
253
+
254
+ React.useEffect(() => {
255
+ let alive = true;
256
+ loadSettings(rpc).then((snap) => {
257
+ if (!alive) return;
258
+ setForm(snapshotToForm(snap));
259
+ setCreds(snap.credentials ?? {});
260
+ setStatus('idle');
261
+ }).catch(() => alive && setStatus('error'));
262
+ return () => { alive = false; };
263
+ }, [rpcCall]);
264
+
265
+ const onSave = async () => {
266
+ if (!form) return;
267
+ setStatus('saving');
268
+ try {
269
+ // The last snapshot in the chain wins: it is the one that reflects every
270
+ // write this save performed. Re-seeding the form from it also drops the
271
+ // typed secret values (their inputs are write-only by design) and picks up
272
+ // the new presence flags.
273
+ let snap = await saveSettings(rpc, buildConfigSave(form));
274
+ for (const c of buildCredentialSaves(form)) snap = await saveCredentials(rpc, c.channel, c.values);
275
+ setForm(snapshotToForm(snap));
276
+ setCreds(snap.credentials ?? {});
277
+ setStatus('saved');
278
+ } catch { setStatus('error'); }
279
+ };
280
+
281
+ const setChannels = (ch, on) => {
282
+ setForm((f) => ({ ...f, channels: on ? [...f.channels, ch] : f.channels.filter((x) => x !== ch) }));
283
+ // Enabling a channel opens it, and only ever opens: the user just said they
284
+ // care about this one. Ticking it back off leaves the fold alone rather than
285
+ // snapping shut under the cursor.
286
+ if (on && !open.has(ch)) setOpenOverride(new Set(open).add(ch));
287
+ };
288
+ const setField = (ch, field, value) => setForm((f) => ({ ...f, secrets: { ...f.secrets, [ch]: { ...(f.secrets[ch] ?? {}), [field]: value } } }));
289
+ const setChannelConfig = (ch, key, raw) => setForm((f) => {
290
+ const descriptor = (CHANNEL_CONFIG_FIELDS[ch] ?? []).find((x) => x.key === key);
291
+ const value = coerceConfigValue(descriptor?.kind ?? 'text', raw);
292
+ const cfg = { ...(f.channelConfigs[ch] ?? {}) };
293
+ if (value === undefined) delete cfg[key]; else cfg[key] = value;
294
+ return { ...f, channelConfigs: { ...f.channelConfigs, [ch]: cfg } };
295
+ });
296
+ const setDefault = (key, raw) => setForm((f) => {
297
+ const descriptor = CHANNEL_DEFAULT_FIELDS.find((x) => x.key === key);
298
+ const value = coerceConfigValue(descriptor?.kind ?? 'text', raw);
299
+ const defaults = { ...(f.channelDefaults ?? {}) };
300
+ if (value === undefined) delete defaults[key]; else defaults[key] = value;
301
+ return { ...f, channelDefaults: defaults };
302
+ });
303
+
304
+ if (!form) return h('div', { className: 'dsh-connect-settings' }, t('loading'));
305
+
306
+ // Which cards are open is *derived*, not stored: the first render happens
307
+ // before the host's snapshot arrives, and the test stub cannot run effects, so
308
+ // an effect here would be the only place the default could ever be set — and
309
+ // it would never run. The override records only what the user has since done,
310
+ // on top of that default (`toggleInSet(open, …)` rather than a bare toggle),
311
+ // so opening DingTalk does not silently drop the enabled Feishu the user was
312
+ // already looking at.
313
+ const open = openOverride ?? initialOpenChannels(form.channels, ALL_CHANNELS);
314
+
315
+ const toggleOpen = (ch) => setOpenOverride(toggleInSet(open, ch));
316
+ // Advanced folds default to closed for every channel, so an empty set is the
317
+ // derived default here and no counterpart to `initialOpenChannels` is needed.
318
+ const toggleAdvanced = (ch) => setAdvOverride(toggleInSet(advOverride ?? new Set(), ch));
319
+ const focusChannel = (ch) => {
320
+ // A tab expands and scrolls, but never collapses: "show me Telegram" must not
321
+ // close the channel the user is halfway through editing.
322
+ if (!open.has(ch)) setOpenOverride(new Set(open).add(ch));
323
+ if (typeof document === 'undefined' || !document.getElementById) return;
324
+ // The header is rendered whether or not the card is open, so this resolves
325
+ // before React has re-rendered, and lands in the same place either way.
326
+ document.getElementById(`ds-ch-${ch}`)?.scrollIntoView?.({ block: 'nearest' });
327
+ };
328
+
329
+ return h('div', { className: 'dsh-connect-settings' },
330
+ h('section', { className: 'ds-card' },
331
+ h('h4', { className: 'ds-card-title' }, t('channels')),
332
+ // Explains the masking before the user meets a truncated value and wonders
333
+ // whether their stored secret is corrupt.
334
+ h('p', { className: 'ds-note' }, t('previewNote')),
335
+ // A tab strip, but not `role=tablist`: several channels can be open at
336
+ // once, so there is no single "selected" tab to report. These are buttons
337
+ // that open and jump to a channel, and `aria-expanded` says so honestly.
338
+ h('nav', { className: 'ds-tabs', 'aria-label': t('tabsAria') },
339
+ ...ALL_CHANNELS.map((ch) => h('button', {
340
+ key: `tab-${ch}`,
341
+ type: 'button',
342
+ className: 'ds-tab',
343
+ 'aria-expanded': open.has(ch),
344
+ 'aria-controls': open.has(ch) ? `ds-ch-${ch}-body` : undefined,
345
+ onClick: () => focusChannel(ch),
346
+ },
347
+ h('span', { className: 'ds-dot', 'data-on': form.channels.includes(ch) ? '1' : '0' }),
348
+ tr(t, `channel.${ch}`, ch)))),
349
+ // One card per built-in channel: enable toggle + cred badge + secret + config fields.
350
+ ...ALL_CHANNELS.map((ch) => renderChannel(ch, {
351
+ form, creds, t, open, advOverride,
352
+ setChannels, setField, setChannelConfig, toggleOpen, toggleAdvanced,
353
+ }))),
354
+ h('section', { className: 'ds-card' },
355
+ h('h4', { className: 'ds-card-title' }, t('defaults')),
356
+ h('p', { className: 'ds-note' }, t('defaultsHint')),
357
+ h('div', { className: 'ds-fields' },
358
+ ...CHANNEL_DEFAULT_FIELDS.map((field) => renderConfigField(field, form.channelDefaults?.[field.key], (raw) => setDefault(field.key, raw), t)),
359
+ // Only meaningful on the fallback plane: it names the file the pane
360
+ // persists to. With the namespace live that path is not consulted (and
361
+ // is not part of the section), so showing an editable field for it would
362
+ // silently swallow edits.
363
+ form.live ? null : h('div', { className: 'ds-field' },
364
+ h('label', { className: 'ds-control' }, t('statePath'),
365
+ h('input', { className: 'ds-input', value: form.settingsStatePath ?? '', onChange: (e) => setForm((f) => ({ ...f, settingsStatePath: e.target.value })) })),
366
+ h('p', { className: 'ds-hint' }, t('statePathHint'))),
367
+ ),
368
+ h('div', { className: 'ds-status' }, form.live ? t('livePlane') : t('filePlane')),
369
+ // Sticky to the bottom of the host's scrolling region, so Save stays in
370
+ // reach however far the channel list has been unfolded.
371
+ h('div', { className: 'ds-footer' },
372
+ h('button', { className: 'ds-btn', type: 'button', onClick: onSave, disabled: status === 'saving' }, t('save')),
373
+ // Rendering `status` directly leaks the raw state ids (`idle`, `saving`)
374
+ // into the UI; every state has a locale entry instead.
375
+ h('span', { className: 'ds-status' }, tr(t, `status.${status}`, status)),
376
+ ),
377
+ ),
378
+ );
379
+ }
380
+
381
+ export function apply(ctx) {
382
+ injectStyles();
383
+ ctx.effect(() => ctx.locale.register(NS, locale), 'dsh-connect: locale');
384
+ const t = ctx.locale.bind(NS);
385
+ const rpcCall = (endpoint, payload, signal) => ctx.connection.rpc.call(SETTINGS_RPC_CHANNEL, endpoint, payload, signal);
386
+ ctx.effect(() => ctx.slots.inject('settings.section', () => ctx.slots.register({
387
+ name: 'settings.section', id: 'dsh-connect', order: 20, label: () => t('title'), locale: NS, inject: () => ({ rpcCall, t }),
388
+ }, ConnectSettingsTab)), 'dsh-connect: settings.section');
389
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "channels": [
3
+ "feishu",
4
+ "telegram"
5
+ ],
6
+ "channelDefaults": {
7
+ "language": "zh"
8
+ },
9
+ "feishu": {
10
+ "appId": "cli_xxxx",
11
+ "appSecret": "REPLACE_ME"
12
+ },
13
+ "telegram": {
14
+ "botToken": "123456:REPLACE_ME"
15
+ }
16
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAUA,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC9C,oGAAoG;IACpG,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,qFAAqF;QACrF,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,gEAAgE;IAChE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AA0BD,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;AAE5G,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;gBAExD,QAAQ,CAAC,EAAE,MAAM;IAM7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI;IAOrD;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,IAAI,gBAAgB,CAAC,WAAW,CAAC;IAIxC;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IAIrB;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAUjD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9D,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAQ/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU9C,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;CAQb"}
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAYA,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC9C,oGAAoG;IACpG,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,KAAK,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,qFAAqF;QACrF,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,gEAAgE;IAChE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AA0BD,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;AAE5G,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;gBAExD,QAAQ,CAAC,EAAE,MAAM;IAK7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI;IAOrD;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB;;;OAGG;IACH,OAAO,IAAI,gBAAgB,CAAC,WAAW,CAAC;IAIxC;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IAIrB;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAUjD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9D,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAQ/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU9C,OAAO,CAAC,IAAI;IAoCZ,OAAO,CAAC,IAAI;CAQb"}
package/lib/binding.js CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { dirname, resolve } from "node:path";
10
+ import { resolveStateDir } from "./state-dir.js";
10
11
  function keyOf(channel, chatKey) {
11
12
  return `${channel}\u0000${chatKey}`;
12
13
  }
@@ -35,8 +36,7 @@ export class BindingStore {
35
36
  file;
36
37
  changeListeners = new Set();
37
38
  constructor(stateDir) {
38
- const dir = stateDir ?? process.env.DSH_CONNECT_STATE_DIR ?? ".dsh-connect";
39
- this.file = resolve(dir, "bindings.json");
39
+ this.file = resolve(resolveStateDir({ stateDir }), "bindings.json");
40
40
  this.load();
41
41
  }
42
42
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * DingTalk stream-mode channel adapter: bidirectional bot over the stream
3
+ * gateway. Inbound messages arrive via STOMP push, replies are SENT back to
4
+ * the triggering message's msgId.
5
+ *
6
+ * Capability notes (honest scope):
7
+ * - Group replies require an @-mention (requireMention, default true) — the
8
+ * gateway pushes every group message, so the adapter gates on isInAtList.
9
+ * - Interactive menus render as a numbered text list: the user answers with a
10
+ * number and the adapter resolves the choice. Real action-card buttons are
11
+ * out of scope for this release.
12
+ * - `streamText` accumulates the full reply and sends it once (the stream
13
+ * gateway has no progressive card editing).
14
+ * - Proactive pushes (reminders / broadcasts) still use the webhook service;
15
+ * the stream adapter can only reply to inbound messages.
16
+ * @module dsh-connect/channels/dingtalk/adapter
17
+ */
18
+ import type { ChannelAdapter, ChoicePrompt, ChoiceResult, InboundMessage, OutboundTarget, SummaryCard } from "../../types.js";
19
+ import type { Language } from "../../i18n.js";
20
+ export interface DingtalkStreamAdapterConfig {
21
+ clientId: string;
22
+ clientSecret: string;
23
+ /** Gateway URL override (default wss://api.dingtalk.com/connect). */
24
+ url?: string;
25
+ /** Group messages must @-mention the bot (default true). */
26
+ requireMention?: boolean;
27
+ language?: Language;
28
+ }
29
+ /**
30
+ * Bidirectional DingTalk adapter (stream mode). Deliberately text-forward:
31
+ * no card editing, no button callbacks — just normalized text both ways.
32
+ */
33
+ export declare class DingtalkStreamAdapter implements ChannelAdapter {
34
+ readonly id = "dingtalk";
35
+ private readonly client;
36
+ private readonly t;
37
+ private readonly requireMention;
38
+ private handler?;
39
+ private readonly pendingMenus;
40
+ constructor(config: DingtalkStreamAdapterConfig, logger?: {
41
+ warn?: (...args: unknown[]) => void;
42
+ });
43
+ start(): Promise<void>;
44
+ stop(): Promise<void>;
45
+ onInbound(handler: (msg: InboundMessage) => void | Promise<void>): void;
46
+ /** Gate + normalize + menu-answer routing, then hand to the core. */
47
+ private dispatch;
48
+ /**
49
+ * A pending text menu is answered when the user replies with a number.
50
+ * Returns true when the message was consumed as a menu answer.
51
+ */
52
+ private tryResolveMenu;
53
+ sendText(target: OutboundTarget, text: string): Promise<void>;
54
+ sendCard(target: OutboundTarget, card: SummaryCard): Promise<void>;
55
+ streamText(target: OutboundTarget, chunks: AsyncIterable<string>): Promise<void>;
56
+ promptChoice(target: OutboundTarget, prompt: ChoicePrompt, _updateMessageId?: string): Promise<ChoiceResult>;
57
+ closeMenu(_messageId: string, _summary: string): Promise<void>;
58
+ }
59
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/channels/dingtalk/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EACV,cAAc,EAEd,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAW9C,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAYD;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,QAAQ,CAAC,EAAE,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAmB;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,OAAO,CAAC,CAAgD;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;gBAEnD,MAAM,EAAE,2BAA2B,EAAE,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE;IAW3F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIvE,qEAAqE;YACvD,QAAQ;IAQtB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAgBhB,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7D,QAAQ,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlE,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhF,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA4B5G,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIrE"}