@krischoichoi/channel-dingtalk 0.6.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 (70) hide show
  1. package/README.md +139 -0
  2. package/lib/adapter.d.ts +123 -0
  3. package/lib/adapter.d.ts.map +1 -0
  4. package/lib/adapter.js +355 -0
  5. package/lib/adapter.js.map +1 -0
  6. package/lib/ai-card.d.ts +64 -0
  7. package/lib/ai-card.d.ts.map +1 -0
  8. package/lib/ai-card.js +150 -0
  9. package/lib/ai-card.js.map +1 -0
  10. package/lib/auth/device-registration.d.ts +18 -0
  11. package/lib/auth/device-registration.d.ts.map +1 -0
  12. package/lib/auth/device-registration.js +147 -0
  13. package/lib/auth/device-registration.js.map +1 -0
  14. package/lib/config.d.ts +119 -0
  15. package/lib/config.d.ts.map +1 -0
  16. package/lib/config.js +75 -0
  17. package/lib/config.js.map +1 -0
  18. package/lib/definition.d.ts +53 -0
  19. package/lib/definition.d.ts.map +1 -0
  20. package/lib/definition.js +227 -0
  21. package/lib/definition.js.map +1 -0
  22. package/lib/image-hydrator.d.ts +213 -0
  23. package/lib/image-hydrator.d.ts.map +1 -0
  24. package/lib/image-hydrator.js +244 -0
  25. package/lib/image-hydrator.js.map +1 -0
  26. package/lib/inbound.d.ts +38 -0
  27. package/lib/inbound.d.ts.map +1 -0
  28. package/lib/inbound.js +158 -0
  29. package/lib/inbound.js.map +1 -0
  30. package/lib/index.d.ts +49 -0
  31. package/lib/index.d.ts.map +1 -0
  32. package/lib/index.js +123 -0
  33. package/lib/index.js.map +1 -0
  34. package/lib/manifest.d.ts +23 -0
  35. package/lib/manifest.d.ts.map +1 -0
  36. package/lib/manifest.js +34 -0
  37. package/lib/manifest.js.map +1 -0
  38. package/lib/mapper.d.ts +36 -0
  39. package/lib/mapper.d.ts.map +1 -0
  40. package/lib/mapper.js +175 -0
  41. package/lib/mapper.js.map +1 -0
  42. package/lib/media-mime.d.ts +14 -0
  43. package/lib/media-mime.d.ts.map +1 -0
  44. package/lib/media-mime.js +32 -0
  45. package/lib/media-mime.js.map +1 -0
  46. package/lib/official-upstream.d.ts +143 -0
  47. package/lib/official-upstream.d.ts.map +1 -0
  48. package/lib/official-upstream.js +425 -0
  49. package/lib/official-upstream.js.map +1 -0
  50. package/lib/openapi-port.d.ts +190 -0
  51. package/lib/openapi-port.d.ts.map +1 -0
  52. package/lib/openapi-port.js +2 -0
  53. package/lib/openapi-port.js.map +1 -0
  54. package/lib/outbound.d.ts +69 -0
  55. package/lib/outbound.d.ts.map +1 -0
  56. package/lib/outbound.js +148 -0
  57. package/lib/outbound.js.map +1 -0
  58. package/lib/stream-upstream.d.ts +77 -0
  59. package/lib/stream-upstream.d.ts.map +1 -0
  60. package/lib/stream-upstream.js +303 -0
  61. package/lib/stream-upstream.js.map +1 -0
  62. package/lib/transport.d.ts +37 -0
  63. package/lib/transport.d.ts.map +1 -0
  64. package/lib/transport.js +73 -0
  65. package/lib/transport.js.map +1 -0
  66. package/lib/upstream.d.ts +60 -0
  67. package/lib/upstream.d.ts.map +1 -0
  68. package/lib/upstream.js +61 -0
  69. package/lib/upstream.js.map +1 -0
  70. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # @krischoichoi/channel-dingtalk
2
+
3
+ DingTalk / 钉钉 channel adapter for DeepSeek Harness.
4
+
5
+ Maps DingTalk to the stable Channel Contract with two selectable upstream
6
+ drivers:
7
+
8
+ - **`sdk`** — inbound via the official `dingtalk-stream` SDK (WebSocket stream
9
+ mode); outbound via `sessionWebhook` and DingTalk AI Card OpenAPI.
10
+ - **`gateway`** — self-hosted HTTP gateway long-poll driver (legacy).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @krischoichoi/channel-dingtalk
16
+ ```
17
+
18
+ Or install the whole bundle:
19
+
20
+ ```bash
21
+ npx @deepseek-ai/dsh plugin --profile web add -w @krischoichoi/dsh-channels@latest
22
+ ```
23
+
24
+ ## Configuration
25
+
26
+ SDK mode:
27
+
28
+ ```yaml
29
+ - id: channels-dingtalk
30
+ name: '@krischoichoi/channel-dingtalk'
31
+ inject: [channels, credentials, channelControl]
32
+ config:
33
+ enabled: true
34
+ accountId: main
35
+ upstream:
36
+ mode: sdk
37
+ clientId: "ding-xxx" # AppKey(非机密)
38
+ # clientSecretRef 默认 DSH_CHANNEL_DINGTALK_MAIN_CLIENT_SECRET
39
+ # 真实 AppSecret 只存 ctx.credentials
40
+ card:
41
+ createOnFirstDelta: true
42
+ # 问答按钮是**可选**能力:必须填你自己卡片平台里已发布、且含 text/actions
43
+ # 变量的模板 ID。留空(默认)= 不声明 interactiveActions,问题走编号文字回复
44
+ # (与微信一致)。填了但模板未发布/无权限,Harness 会自动降级为文字回复。
45
+ # interactiveTemplateId: "dac1dbec-55af-40c3-be91-6d6882ef3b66.schema"
46
+ # 下面两个变量名必须与模板中的变量绑定一致
47
+ interactiveTextParam: text
48
+ interactiveActionsParam: actions
49
+ ```
50
+
51
+ Gateway mode (legacy, self-hosted HTTP gateway):
52
+
53
+ ```yaml
54
+ - id: channels-dingtalk
55
+ name: '@krischoichoi/channel-dingtalk'
56
+ inject: [channels, credentials, channelControl]
57
+ config:
58
+ enabled: true
59
+ accountId: main
60
+ upstream:
61
+ mode: gateway
62
+ baseUrl: http://127.0.0.1:9100
63
+ longPollTimeoutMs: 25000
64
+ ```
65
+
66
+ The AppSecret is resolved through `ctx.credentials` at startup and injected as
67
+ `deps.clientSecret`. A legacy plaintext `upstream.clientSecret` is migrated into
68
+ the credentials seam once and then deleted.
69
+
70
+ ## Streaming
71
+
72
+ DingTalk uses `edit` streaming: the adapter creates an AI Card, updates it with
73
+ each delta, and finalizes (or marks it failed) at turn end.
74
+
75
+ ## Interactive buttons (opt-in)
76
+
77
+ `OutboundMessage.actions` uses the official interactive-card APIs:
78
+
79
+ - `POST /v1.0/im/interactiveCards/send`
80
+ - `PUT /v1.0/im/interactiveCards`
81
+ - Stream callback topic `/v1.0/card/instances/callback` (the `TOPIC_CARD`
82
+ constant of `dingtalk-stream`)
83
+
84
+ This is not the legacy webhook `actionCard` API, whose buttons only open URLs
85
+ and cannot return a selection to the Harness.
86
+
87
+ Buttons are **opt-in and off by default**. A card only renders interactive
88
+ components when its template is published in *your* organization's Card
89
+ Platform, exposes the configured `text`/`actions` variables, and its buttons
90
+ return `cardPrivateData.params.action` (the action id). A built-in third-party
91
+ template id cannot satisfy that, so `card.interactiveTemplateId` has no default:
92
+ without it the adapter reports `interactiveActions: false` and Harness questions
93
+ use the numbered-text reply path (same as Weixin). When the template *is*
94
+ configured but the send still fails (unpublished template, missing permission),
95
+ `channel-harness` degrades that question to numbered text instead of cancelling
96
+ it.
97
+
98
+ LIVE-REQUIRED before advertising buttons as verified: a real-account round trip
99
+ that the card renders, that a click arrives on the STREAM callback topic, and
100
+ that the answer returns to the agent. The official troubleshooting note
101
+ requires `callbackType="STREAM"` at card creation *and* the registered callback
102
+ topic ([钉钉卡片示例 · 注意事项](https://github.com/open-dingtalk/dingtalk-card-examples));
103
+ whether `POST /v1.0/im/interactiveCards/send` honors `callbackType` is still
104
+ unverified here and is the first item to check in that gate.
105
+
106
+ ## Capabilities
107
+
108
+ | Capability | Value |
109
+ | --- | --- |
110
+ | text / image / file / audio | ✅ |
111
+ | markdown / cards | ✅ (AI Card streaming replies) |
112
+ | interactive actions | ⚙️ opt-in: SDK mode **and** `card.interactiveTemplateId` set |
113
+ | video / reactions / threads | ❌ |
114
+ | streaming | `edit` (AI Card) |
115
+
116
+ ## Upstream
117
+
118
+ | Field | Value |
119
+ | --- | --- |
120
+ | SDK | `dingtalk-stream` |
121
+ | Tested version | `2.1.5` |
122
+ | Status | `tested` (offline contract + fixture + SDK-mode E2E suites) |
123
+
124
+ ## Development
125
+
126
+ ```bash
127
+ pnpm --filter @krischoichoi/channel-dingtalk build
128
+ pnpm --filter @krischoichoi/channel-dingtalk typecheck
129
+ pnpm --filter @krischoichoi/channel-dingtalk test
130
+ ```
131
+
132
+ ## Related
133
+
134
+ - [Repository root](../../README.md)
135
+ - [Adapter authoring guide](../../docs/adapter-authoring.md)
136
+
137
+ ## License
138
+
139
+ [MIT](../../LICENSE)
@@ -0,0 +1,123 @@
1
+ /**
2
+ * DingTalk channel adapter.
3
+ *
4
+ * Maps the DingTalk platform (through the upstream driver) to the Channel
5
+ * Contract. All network/lifecycle resources live behind the upstream driver
6
+ * and are aborted via the adapter context signal. The adapter never touches
7
+ * Harness Agent APIs.
8
+ *
9
+ * The upstream driver is selected by `config.upstream.mode`:
10
+ * - 'sdk' → `DingTalkStreamUpstream`: inbound via the official
11
+ * `dingtalk-stream` SDK, outbound via DingTalk's official HTTP APIs. The stream
12
+ * client comes from `deps.sdkClient` / `deps.sdkClientFactory`, defaulting
13
+ * to a real `DWClient` built from `upstream.clientId` (AppKey) and the
14
+ * resolved AppSecret at start time (missing credentials fail start loudly).
15
+ * The AppSecret is resolved via `ctx.credentials` and injected as
16
+ * `deps.clientSecret` — it never lives in config and is never read from
17
+ * `config.upstream` (mirrors how channel-qq injects `deps.appSecret`).
18
+ * - 'gateway' → `HttpDingTalkUpstream` over the transport (legacy).
19
+ *
20
+ * Auth is connection-state driven: the upstream driver owns the platform
21
+ * credentials, so the adapter derives its auth state from the connection
22
+ * (connected → authenticated). `beginAuth`/`pollAuth` are intentionally not
23
+ * implemented in M2 — a real QR/token flow can slot in later.
24
+ */
25
+ import type { ChannelAdapter, ChannelAdapterContext, ChannelCapabilities, ChannelHealth, ChannelTarget, CreateReplyOptions, OutboundMessage, ReplyHandle, SendResult } from '@krischoichoi/channel-core';
26
+ import type { RemoteMediaFetchLike } from './image-hydrator.js';
27
+ import type { DingTalkConfig } from './config.js';
28
+ import { type HttpTransport } from './transport.js';
29
+ import type { MediaResolverLike, OutboxCapabilities } from './openapi-port.js';
30
+ import { type DingTalkStreamClient } from './stream-upstream.js';
31
+ import { type DingTalkManifest } from './manifest.js';
32
+ export interface DingTalkAdapterDeps {
33
+ transport?: HttpTransport;
34
+ /**
35
+ * Resolved DingTalk AppSecret (SDK mode). Never from config — the plugin
36
+ * resolves it via `ctx.credentials` (`upstream.clientSecretRef`) and
37
+ * injects it here. Used only to build the default `DWClient`.
38
+ */
39
+ clientSecret?: string;
40
+ /**
41
+ * Pre-built stream client for SDK mode (offline tests). Overrides
42
+ * `sdkClientFactory`; when neither is given a real `DWClient` is built
43
+ * from `config.upstream.clientId` and `deps.clientSecret` at start time.
44
+ */
45
+ sdkClient?: DingTalkStreamClient;
46
+ /** Lazy stream client factory for SDK mode; overrides the default DWClient. */
47
+ sdkClientFactory?: (config: DingTalkConfig) => DingTalkStreamClient;
48
+ /**
49
+ * Secure remote media fetcher used to hydrate inbound image URLs (plan
50
+ * §32A). Injectable for offline tests; defaults to a real
51
+ * `SecureRemoteMediaFetcher` bound to the global fetch.
52
+ */
53
+ secureFetch?: RemoteMediaFetchLike;
54
+ /**
55
+ * DingTalk OpenAPI media resolver used to hydrate opaque file mediaIds into
56
+ * trusted bytes during inbound file ingress. Injectable for
57
+ * offline tests; defaults to the running official port in sdk mode.
58
+ */
59
+ resolveMedia?: MediaResolverLike;
60
+ /** Injectable clock (tests). */
61
+ now?: () => number;
62
+ }
63
+ export declare class DingTalkAdapter implements ChannelAdapter {
64
+ private readonly config;
65
+ readonly id = "dingtalk";
66
+ /** Upstream compatibility manifest (read structurally by `channels doctor`). */
67
+ readonly manifest: DingTalkManifest;
68
+ readonly capabilities: ChannelCapabilities;
69
+ private static readonly BASE_CAPABILITIES;
70
+ private ctx?;
71
+ /** Built in `start()` (driver selection needs the resolved deps/credentials). */
72
+ private upstream;
73
+ private readonly transport;
74
+ private readonly deps;
75
+ private readonly secureFetch;
76
+ private inbound;
77
+ private outbound;
78
+ /**
79
+ * Official OpenAPI port built in sdk mode (proactive path for the outbox).
80
+ * Undefined in gateway mode — proactive capabilities then fail CLOSED.
81
+ */
82
+ private officialPort?;
83
+ /**
84
+ * Proactive outbox capabilities. Read by the harness outbox;
85
+ * fails CLOSED (all-false) in gateway mode / when not wired.
86
+ */
87
+ private capProactive;
88
+ private started;
89
+ private stopped;
90
+ private receiveLoop?;
91
+ private connected;
92
+ /** Connection-state-driven auth; the upstream driver owns credentials. */
93
+ private authState;
94
+ private readonly now;
95
+ /** Internal stop signal merged with the context signal for prompt teardown. */
96
+ private stopController?;
97
+ private receiveSignal?;
98
+ constructor(config: DingTalkConfig, deps?: DingTalkAdapterDeps);
99
+ start(ctx: ChannelAdapterContext): Promise<void>;
100
+ stop(): Promise<void>;
101
+ send(target: ChannelTarget, message: OutboundMessage): Promise<SendResult>;
102
+ edit(target: ChannelTarget, messageId: string, message: OutboundMessage): Promise<SendResult>;
103
+ createReply(target: ChannelTarget, _options?: CreateReplyOptions): Promise<ReplyHandle>;
104
+ getHealth(): Promise<ChannelHealth>;
105
+ /** Long-poll receive loop with exponential backoff on failure. */
106
+ startReceiveLoop(): void;
107
+ /** Select and build the upstream driver for the configured mode. */
108
+ private buildUpstream;
109
+ /**
110
+ * Proactive outbox capabilities exposed for the harness outbox.
111
+ * Fails CLOSED: all-false in gateway mode / when not wired.
112
+ */
113
+ get outboxCapabilities(): OutboxCapabilities;
114
+ private resolveStreamClient;
115
+ private runReceiveLoop;
116
+ /** Flip connection/auth state once the upstream proves reachable. */
117
+ private markConnected;
118
+ private aborted;
119
+ private setAuth;
120
+ private emitAuth;
121
+ private emitConnection;
122
+ }
123
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
package/lib/adapter.js ADDED
@@ -0,0 +1,355 @@
1
+ import { ChannelError, SecureRemoteMediaFetcher } from '@krischoichoi/channel-core';
2
+ import { DWClient } from 'dingtalk-stream';
3
+ import { FetchTransport } from './transport.js';
4
+ import { HttpDingTalkUpstream } from './upstream.js';
5
+ import { DingTalkOpenApiPortImpl } from './official-upstream.js';
6
+ import { DingTalkStreamUpstream, } from './stream-upstream.js';
7
+ import { InboundProcessor } from './inbound.js';
8
+ import { OutboundSender } from './outbound.js';
9
+ import { DingTalkCardReply } from './ai-card.js';
10
+ import { manifest as dingTalkManifest } from './manifest.js';
11
+ export class DingTalkAdapter {
12
+ config;
13
+ id = 'dingtalk';
14
+ /** Upstream compatibility manifest (read structurally by `channels doctor`). */
15
+ manifest = dingTalkManifest;
16
+ capabilities;
17
+ static BASE_CAPABILITIES = {
18
+ text: true,
19
+ image: true,
20
+ file: true,
21
+ audio: true,
22
+ video: false,
23
+ markdown: true,
24
+ cards: true,
25
+ reactions: false,
26
+ threads: false,
27
+ streaming: 'edit',
28
+ // Directional media precision. Inbound
29
+ // image/file have an established official download path. Audio/video are
30
+ // conservatively declared as locators until official callback/download
31
+ // documentation and a real-account live gate prove byte hydration; the
32
+ // inbound processor may still hydrate them as a best-effort enhancement.
33
+ // Outbound: only
34
+ // image/file bytes are actually sendable (proactive uploadMedia + sendMedia,
35
+ // msgKey sampleImageMsg/sampleFile); there is no robot audio/video send
36
+ // path, so outbound audio/video are 'unsupported' (the legacy coarse
37
+ // `audio: true` / `video: false` flags remain unchanged).
38
+ media: {
39
+ inbound: {
40
+ image: 'bytes',
41
+ file: 'bytes',
42
+ audio: 'locator',
43
+ video: 'locator',
44
+ },
45
+ outbound: {
46
+ image: 'bytes',
47
+ file: 'bytes',
48
+ audio: 'unsupported',
49
+ video: 'unsupported',
50
+ },
51
+ },
52
+ };
53
+ ctx;
54
+ /** Built in `start()` (driver selection needs the resolved deps/credentials). */
55
+ upstream;
56
+ transport;
57
+ deps;
58
+ secureFetch;
59
+ inbound;
60
+ outbound;
61
+ /**
62
+ * Official OpenAPI port built in sdk mode (proactive path for the outbox).
63
+ * Undefined in gateway mode — proactive capabilities then fail CLOSED.
64
+ */
65
+ officialPort;
66
+ /**
67
+ * Proactive outbox capabilities. Read by the harness outbox;
68
+ * fails CLOSED (all-false) in gateway mode / when not wired.
69
+ */
70
+ capProactive = { proactiveText: false, proactiveMedia: false };
71
+ started = false;
72
+ stopped = false;
73
+ receiveLoop;
74
+ connected = false;
75
+ /** Connection-state-driven auth; the upstream driver owns credentials. */
76
+ authState = 'unknown';
77
+ now;
78
+ /** Internal stop signal merged with the context signal for prompt teardown. */
79
+ stopController;
80
+ receiveSignal;
81
+ constructor(config, deps = {}) {
82
+ this.config = config;
83
+ this.capabilities = {
84
+ ...DingTalkAdapter.BASE_CAPABILITIES,
85
+ // Declared ONLY when the operator opted into a card template that must
86
+ // already be published in their own Card Platform with matching
87
+ // `text`/`actions` variables. A capability claimed without that
88
+ // precondition is a lie the question presenter pays for.
89
+ interactiveActions: config.upstream.mode === 'sdk' && Boolean(config.card.interactiveTemplateId?.trim()),
90
+ };
91
+ this.now = deps.now ?? Date.now;
92
+ this.deps = deps;
93
+ this.secureFetch = deps.secureFetch ?? new SecureRemoteMediaFetcher();
94
+ this.transport = deps.transport ?? new FetchTransport(config.baseUrl, { timeoutMs: config.timeoutMs });
95
+ }
96
+ async start(ctx) {
97
+ if (this.started)
98
+ return;
99
+ this.ctx = ctx;
100
+ this.stopped = false;
101
+ this.buildUpstream();
102
+ this.stopController = new AbortController();
103
+ this.receiveSignal = AbortSignal.any([ctx.signal, this.stopController.signal]);
104
+ this.inbound = new InboundProcessor({
105
+ ctx,
106
+ meta: { channel: this.id, accountId: this.config.accountId },
107
+ dedupEnabled: this.config.dedup.enabled,
108
+ dedupWindowMs: this.config.dedup.windowMs,
109
+ now: this.now,
110
+ secureFetch: this.secureFetch,
111
+ resolveMedia: this.deps.resolveMedia ?? this.officialPort,
112
+ });
113
+ // REPLY keeps the sessionWebhook upstream; PROACTIVE uses the official port.
114
+ this.outbound = new OutboundSender({
115
+ reply: this.upstream,
116
+ logger: ctx.logger,
117
+ proactive: this.officialPort,
118
+ capabilities: this.capProactive,
119
+ interactiveTemplateId: this.config.card.interactiveTemplateId,
120
+ interactiveTextParam: this.config.card.interactiveTextParam,
121
+ interactiveActionsParam: this.config.card.interactiveActionsParam,
122
+ });
123
+ this.connected = false;
124
+ this.authState = 'unknown';
125
+ this.emitAuth(this.authState);
126
+ this.emitConnection('connecting');
127
+ this.startReceiveLoop();
128
+ this.started = true;
129
+ }
130
+ async stop() {
131
+ if (!this.started || this.stopped)
132
+ return;
133
+ this.stopped = true;
134
+ this.started = false;
135
+ this.connected = false;
136
+ // The owning fiber aborts the context signal first; the loop also exits
137
+ // on our internal stop signal so stop() never depends on an external
138
+ // abort (contract tests stop without disposing the context).
139
+ this.stopController?.abort();
140
+ const loop = this.receiveLoop;
141
+ this.receiveLoop = undefined;
142
+ if (loop)
143
+ await loop.catch(() => undefined);
144
+ this.emitConnection('closed');
145
+ }
146
+ async send(target, message) {
147
+ if (!this.started || !this.outbound) {
148
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'dingtalk adapter is not started');
149
+ }
150
+ return this.outbound.send(target, message);
151
+ }
152
+ async edit(target, messageId, message) {
153
+ if (!this.started || !this.outbound) {
154
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'dingtalk adapter is not started');
155
+ }
156
+ return this.outbound.edit(target, messageId, message);
157
+ }
158
+ async createReply(target, _options) {
159
+ if (!this.started || !this.ctx) {
160
+ throw new ChannelError('CHANNEL_NOT_STARTED', 'dingtalk adapter is not started');
161
+ }
162
+ return new DingTalkCardReply({
163
+ upstream: this.upstream,
164
+ target,
165
+ logger: this.ctx.logger,
166
+ createOnFirstDelta: this.config.card.createOnFirstDelta,
167
+ now: this.now,
168
+ });
169
+ }
170
+ async getHealth() {
171
+ if (!this.started) {
172
+ return { status: 'down', detail: 'dingtalk adapter is not started', authenticated: false };
173
+ }
174
+ if (this.connected) {
175
+ return { status: 'ok', detail: 'connected', connection: 'connected', authenticated: true };
176
+ }
177
+ return {
178
+ status: 'degraded',
179
+ detail: 'receive loop down',
180
+ connection: 'disconnected',
181
+ authenticated: false,
182
+ };
183
+ }
184
+ /** Long-poll receive loop with exponential backoff on failure. */
185
+ startReceiveLoop() {
186
+ if (this.receiveLoop)
187
+ return;
188
+ this.receiveLoop = this.runReceiveLoop();
189
+ }
190
+ /** Select and build the upstream driver for the configured mode. */
191
+ buildUpstream() {
192
+ const httpUpstream = new HttpDingTalkUpstream({
193
+ transport: this.transport,
194
+ longPollTimeoutMs: this.config.longPollTimeoutMs,
195
+ });
196
+ if (this.config.upstream.mode !== 'sdk') {
197
+ this.upstream = httpUpstream;
198
+ // Gateway mode has no official proactive path — capabilities fail CLOSED.
199
+ this.officialPort = undefined;
200
+ this.capProactive = { proactiveText: false, proactiveMedia: false };
201
+ return;
202
+ }
203
+ // The official OpenAPI port implements BOTH the `DingTalkUpstream` outbound
204
+ // surface (sessionWebhook reply) and the `DingTalkOpenApiPort`
205
+ // proactive/media surface. Proactive is implemented in sdk mode,
206
+ // so the outbox capabilities are enabled (capability true only when
207
+ // actually implemented — never faked).
208
+ this.officialPort = new DingTalkOpenApiPortImpl({
209
+ transport: this.transport,
210
+ clientId: this.config.upstream.clientId,
211
+ clientSecret: this.deps.clientSecret,
212
+ secureFetch: this.secureFetch,
213
+ now: this.now,
214
+ });
215
+ this.capProactive = { proactiveText: true, proactiveMedia: true };
216
+ const options = {
217
+ client: this.resolveStreamClient(),
218
+ outbound: this.officialPort,
219
+ onConnected: () => this.markConnected(),
220
+ };
221
+ this.upstream = new DingTalkStreamUpstream(options);
222
+ }
223
+ /**
224
+ * Proactive outbox capabilities exposed for the harness outbox.
225
+ * Fails CLOSED: all-false in gateway mode / when not wired.
226
+ */
227
+ get outboxCapabilities() {
228
+ return { ...this.capProactive };
229
+ }
230
+ resolveStreamClient() {
231
+ if (this.deps.sdkClient)
232
+ return this.deps.sdkClient;
233
+ if (this.deps.sdkClientFactory)
234
+ return this.deps.sdkClientFactory(this.config);
235
+ return createDefaultDWClient(this.config, this.deps.clientSecret);
236
+ }
237
+ async runReceiveLoop() {
238
+ let attempt = 0;
239
+ while (!this.stopped && !this.aborted()) {
240
+ try {
241
+ await this.upstream.receive(this.receiveSignal, (raw) => {
242
+ void this.inbound.handle(raw).catch((error) => {
243
+ this.ctx.logger.error('[channel-dingtalk] inbound handling failed', error);
244
+ });
245
+ });
246
+ attempt = 0;
247
+ // A completed receive cycle proves the upstream is reachable; auth
248
+ // follows the connection state (the driver owns credentials). In sdk
249
+ // mode the stream driver also reports connectivity via onConnected.
250
+ this.markConnected();
251
+ }
252
+ catch (error) {
253
+ if (this.stopped || this.aborted())
254
+ break;
255
+ attempt += 1;
256
+ this.connected = false;
257
+ this.emitConnection('reconnecting');
258
+ if (this.config.reconnect.enabled && attempt > this.config.reconnect.maxRetries) {
259
+ this.ctx.logger.warn('[channel-dingtalk] reconnect budget exhausted');
260
+ this.setAuth('failed');
261
+ this.emitConnection('disconnected');
262
+ break;
263
+ }
264
+ const delay = Math.min(this.config.reconnect.baseDelayMs * 2 ** Math.min(attempt - 1, 8), this.config.reconnect.maxDelayMs);
265
+ this.ctx.logger.warn(`[channel-dingtalk] receive loop error; retry in ${delay}ms`, error);
266
+ await sleep(delay, this.receiveSignal);
267
+ }
268
+ }
269
+ this.connected = false;
270
+ if (!this.stopped && !this.aborted()) {
271
+ this.emitConnection('disconnected');
272
+ }
273
+ }
274
+ /** Flip connection/auth state once the upstream proves reachable. */
275
+ markConnected() {
276
+ // Never report connected while stopping; an external abort is not
277
+ // sufficient (a long-poll cycle may legitimately end on the abort while
278
+ // having already proven reachability — see the gateway lifecycle tests).
279
+ if (this.stopped)
280
+ return;
281
+ if (this.connected)
282
+ return;
283
+ this.connected = true;
284
+ this.setAuth('authenticated');
285
+ this.emitConnection('connected');
286
+ }
287
+ aborted() {
288
+ return this.receiveSignal?.aborted ?? true;
289
+ }
290
+ setAuth(state) {
291
+ if (this.authState === state)
292
+ return;
293
+ this.authState = state;
294
+ this.emitAuth(state);
295
+ }
296
+ emitAuth(state) {
297
+ if (!this.ctx)
298
+ return;
299
+ void this.ctx
300
+ .emit({
301
+ type: 'auth.changed',
302
+ channel: this.id,
303
+ accountId: this.config.accountId,
304
+ state,
305
+ })
306
+ .catch(() => undefined);
307
+ }
308
+ emitConnection(state) {
309
+ if (!this.ctx)
310
+ return;
311
+ void this.ctx
312
+ .emit({
313
+ type: 'connection.changed',
314
+ channel: this.id,
315
+ accountId: this.config.accountId,
316
+ state,
317
+ })
318
+ .catch(() => undefined);
319
+ }
320
+ }
321
+ /**
322
+ * Build the real SDK client from SDK-mode credentials; never logs them.
323
+ *
324
+ * Takes the RESOLVED AppSecret (injected by the plugin via ctx.credentials)
325
+ * rather than reading it from config. A missing secret fails start loudly.
326
+ */
327
+ function createDefaultDWClient(config, clientSecret) {
328
+ const { clientId } = config.upstream;
329
+ if (!clientId || !clientSecret) {
330
+ throw new ChannelError('CHANNEL_ERROR', 'dingtalk upstream mode "sdk" requires resolved clientId and clientSecret credentials');
331
+ }
332
+ const client = new DWClient({ clientId, clientSecret, keepAlive: false });
333
+ // The adapter owns retry/backoff. The SDK otherwise swallows a failed
334
+ // connection and retries independently, which makes health unreliable.
335
+ client.getConfig().autoReconnect = false;
336
+ return client;
337
+ }
338
+ function sleep(ms, signal) {
339
+ return new Promise((resolve) => {
340
+ if (signal?.aborted) {
341
+ resolve();
342
+ return;
343
+ }
344
+ const timer = setTimeout(() => {
345
+ signal?.removeEventListener('abort', onAbort);
346
+ resolve();
347
+ }, ms);
348
+ const onAbort = () => {
349
+ clearTimeout(timer);
350
+ resolve();
351
+ };
352
+ signal?.addEventListener('abort', onAbort, { once: true });
353
+ });
354
+ }
355
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}