@clowder-ai/connector-dingtalk 0.0.0-stage → 0.1.0-alpha.7
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/README.en.md +54 -0
- package/README.md +24 -2
- package/assets/icon.svg +4 -0
- package/dist/DingTalkAdapter.d.ts +251 -0
- package/dist/DingTalkAdapter.d.ts.map +1 -0
- package/dist/DingTalkAdapter.js +821 -0
- package/dist/DingTalkAdapter.js.map +1 -0
- package/dist/command-ingress.d.ts +10 -0
- package/dist/command-ingress.d.ts.map +1 -0
- package/dist/command-ingress.js +15 -0
- package/dist/command-ingress.js.map +1 -0
- package/dist/execution-status.d.ts +27 -0
- package/dist/execution-status.d.ts.map +1 -0
- package/dist/execution-status.js +48 -0
- package/dist/execution-status.js.map +1 -0
- package/dist/inbound-download.d.ts +13 -0
- package/dist/inbound-download.d.ts.map +1 -0
- package/dist/inbound-download.js +70 -0
- package/dist/inbound-download.js.map +1 -0
- package/dist/inbound-media-source.d.ts +25 -0
- package/dist/inbound-media-source.d.ts.map +1 -0
- package/dist/inbound-media-source.js +115 -0
- package/dist/inbound-media-source.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle-action.d.ts +30 -0
- package/dist/lifecycle-action.d.ts.map +1 -0
- package/dist/lifecycle-action.js +457 -0
- package/dist/lifecycle-action.js.map +1 -0
- package/dist/materialize-media.d.ts +7 -0
- package/dist/materialize-media.d.ts.map +1 -0
- package/dist/materialize-media.js +35 -0
- package/dist/materialize-media.js.map +1 -0
- package/dist/media-notice.d.ts +6 -0
- package/dist/media-notice.d.ts.map +1 -0
- package/dist/media-notice.js +50 -0
- package/dist/media-notice.js.map +1 -0
- package/dist/plugin-entrypoint.d.ts +7 -0
- package/dist/plugin-entrypoint.d.ts.map +1 -0
- package/dist/plugin-entrypoint.js +282 -0
- package/dist/plugin-entrypoint.js.map +1 -0
- package/dist/reply-sender-map.d.ts +10 -0
- package/dist/reply-sender-map.d.ts.map +1 -0
- package/dist/reply-sender-map.js +123 -0
- package/dist/reply-sender-map.js.map +1 -0
- package/dist/runtime.d.ts +68 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +167 -0
- package/dist/runtime.js.map +1 -0
- package/dist/thread-mappings.d.ts +46 -0
- package/dist/thread-mappings.d.ts.map +1 -0
- package/dist/thread-mappings.js +62 -0
- package/dist/thread-mappings.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/npm-shrinkwrap.json +485 -0
- package/package.json +55 -4
- package/plugin.yaml +63 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { decideLifecycleTransition, defineLifecycleAction, lifecycleRejectReason } from './execution-status.js';
|
|
2
|
+
import { threadMappings } from './thread-mappings.js';
|
|
3
|
+
const STARTED_TEXT = '🤔 思考中...';
|
|
4
|
+
const CATCHING_UP_TEXT = '🔄 收到新消息,正在重新整理回复…';
|
|
5
|
+
const LIFECYCLE_KEY_PREFIX = 'lifecycle/';
|
|
6
|
+
// A settled tombstone only answers Host redeliveries with replay/reject; a
|
|
7
|
+
// day is far beyond any sane redelivery horizon, so keeping it longer only
|
|
8
|
+
// accumulates dead keys.
|
|
9
|
+
const LIFECYCLE_TOMBSTONE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
// Records that never settled are Host-crash orphans: their placeholder text
|
|
11
|
+
// is already terminal. The TTL must exceed the longest legitimate in-flight
|
|
12
|
+
// turn (cat work can span many hours), so 24h bounds orphans without
|
|
13
|
+
// deleting lifecycles that are still actively running.
|
|
14
|
+
const LIFECYCLE_STRANDED_TTL_MS = 24 * 60 * 60 * 1000;
|
|
15
|
+
const SWEEP_EVERY_WRITES = 25;
|
|
16
|
+
function object(value) {
|
|
17
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function isPendingEffect(value) {
|
|
20
|
+
return object(value)
|
|
21
|
+
&& value.v === 1
|
|
22
|
+
&& (value.state === 'blocked' || value.state === 'settled')
|
|
23
|
+
&& typeof value.recoveryText === 'string';
|
|
24
|
+
}
|
|
25
|
+
function lifecycleError(code, message) {
|
|
26
|
+
return Object.assign(new Error(message), { code });
|
|
27
|
+
}
|
|
28
|
+
function storedLifecycle(value) {
|
|
29
|
+
if (!object(value)
|
|
30
|
+
|| (value.version !== 1 && value.version !== 2)
|
|
31
|
+
|| !Array.isArray(value.history)
|
|
32
|
+
|| typeof value.actorDisplayName !== 'string'
|
|
33
|
+
|| (value.platformMessageId !== undefined && typeof value.platformMessageId !== 'string')
|
|
34
|
+
|| (value.platformMessageByKey !== undefined
|
|
35
|
+
&& (!object(value.platformMessageByKey) || Object.values(value.platformMessageByKey).some(id => typeof id !== 'string')))
|
|
36
|
+
|| (value.updatedAt !== undefined && typeof value.updatedAt !== 'number')
|
|
37
|
+
|| (value.tombstone !== undefined && typeof value.tombstone !== 'boolean')
|
|
38
|
+
|| (value.deliveryIds !== undefined && (!Array.isArray(value.deliveryIds) || value.deliveryIds.some(id => typeof id !== 'string')))
|
|
39
|
+
|| (value.settledAt !== undefined && typeof value.settledAt !== 'number')
|
|
40
|
+
|| (value.pendingEffect !== undefined && !isPendingEffect(value.pendingEffect))) {
|
|
41
|
+
throw lifecycleError('PLUGIN_INTERNAL', 'stored lifecycle state is invalid');
|
|
42
|
+
}
|
|
43
|
+
return structuredClone(value);
|
|
44
|
+
}
|
|
45
|
+
function recoveryText(event) {
|
|
46
|
+
return `⚠️ 未能完成最新消息重读(${event.reason})。请打开 Clowder AI 重试${event.recoveryUrl ? `:${event.recoveryUrl}` : '。'}`;
|
|
47
|
+
}
|
|
48
|
+
export function createConnectorLifecycleAction(context, callbacks) {
|
|
49
|
+
const tails = new Map();
|
|
50
|
+
let writesSinceSweep = 0;
|
|
51
|
+
let sweepInFlight = false;
|
|
52
|
+
const sweep = async () => {
|
|
53
|
+
let listed;
|
|
54
|
+
try {
|
|
55
|
+
listed = await context.storage.list();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
context.log('warn', 'Connector lifecycle sweep failed', {
|
|
59
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
for (const [key, item] of Object.entries(listed)) {
|
|
65
|
+
if (!key.startsWith(LIFECYCLE_KEY_PREFIX))
|
|
66
|
+
continue;
|
|
67
|
+
let record;
|
|
68
|
+
try {
|
|
69
|
+
record = storedLifecycle(item?.value);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (record.tombstone === true) {
|
|
75
|
+
const settledAt = record.settledAt ?? record.updatedAt;
|
|
76
|
+
if (settledAt !== undefined && settledAt + LIFECYCLE_TOMBSTONE_TTL_MS < now) {
|
|
77
|
+
await context.storage.delete(key, item.revision).catch(() => undefined);
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// v1 records carry no updatedAt, so their age is unknown; the v2 write
|
|
82
|
+
// path re-binds them with a timestamp on the next transition.
|
|
83
|
+
if (record.updatedAt !== undefined && record.updatedAt + LIFECYCLE_STRANDED_TTL_MS < now) {
|
|
84
|
+
await context.storage.delete(key, item.revision).catch(() => undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
// Single-flight background sweep, mirroring reply-sender-map: the hot path
|
|
89
|
+
// only counts writes. console.warn (not context.log) is deliberate here —
|
|
90
|
+
// log() throws FeatureContextRevokedError after feature shutdown, which
|
|
91
|
+
// would turn this last-resort guard into an unhandled rejection.
|
|
92
|
+
const sweepInBackground = () => {
|
|
93
|
+
if (sweepInFlight)
|
|
94
|
+
return;
|
|
95
|
+
sweepInFlight = true;
|
|
96
|
+
void sweep()
|
|
97
|
+
.catch((error) => {
|
|
98
|
+
console.warn('Connector lifecycle sweep failed', {
|
|
99
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
100
|
+
});
|
|
101
|
+
})
|
|
102
|
+
.finally(() => {
|
|
103
|
+
sweepInFlight = false;
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const countWrite = () => {
|
|
107
|
+
writesSinceSweep += 1;
|
|
108
|
+
if (writesSinceSweep < SWEEP_EVERY_WRITES)
|
|
109
|
+
return;
|
|
110
|
+
writesSinceSweep = 0;
|
|
111
|
+
sweepInBackground();
|
|
112
|
+
};
|
|
113
|
+
// Platform effects must not fail the action; the write-ahead record already
|
|
114
|
+
// accepted the event, so a throwing callback is logged and skipped.
|
|
115
|
+
const runEffectSafely = async (executionId, state, label, effect) => {
|
|
116
|
+
try {
|
|
117
|
+
await effect();
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
context.log('warn', `Connector lifecycle ${label} failed`, {
|
|
122
|
+
executionId,
|
|
123
|
+
state,
|
|
124
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
125
|
+
});
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
// Binding listing feeds every side-effect path; a failure here must
|
|
130
|
+
// surface as a coded lifecycle error, not a bare storage/transport
|
|
131
|
+
// exception escaping the action boundary.
|
|
132
|
+
const listThreadBindings = async (threadId) => {
|
|
133
|
+
let listed;
|
|
134
|
+
try {
|
|
135
|
+
listed = await threadMappings(context).listBindings();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw lifecycleError('PLUGIN_INTERNAL', `lifecycle thread ${threadId} binding listing failed: ${error instanceof Error ? error.message : 'unknown'}`);
|
|
139
|
+
}
|
|
140
|
+
return listed.filter(candidate => candidate.threadId === threadId);
|
|
141
|
+
};
|
|
142
|
+
// A redelivery (or a later transition arriving first) can find a record
|
|
143
|
+
// whose accepted side effect never ran because the process died between the
|
|
144
|
+
// pre-write and the platform call. Re-run exactly that effect through the
|
|
145
|
+
// same callbacks and per-binding fallbacks as the original path.
|
|
146
|
+
//
|
|
147
|
+
// The marker is cleared after the ATTEMPT, not after confirmed success:
|
|
148
|
+
// runEffectSafely swallows per-binding failures (they are logged), and an
|
|
149
|
+
// unbounded "retry until applied" loop would duplicate-send on every
|
|
150
|
+
// redelivery. One bounded re-execution per redelivery beats both a lost
|
|
151
|
+
// recovery hint and an effect storm.
|
|
152
|
+
//
|
|
153
|
+
// Returns whether the effect was actually attempted on at least one
|
|
154
|
+
// binding. A thread with no matching binding is not an attempt: the caller
|
|
155
|
+
// must keep the marker so a redelivery after the binding returns can still
|
|
156
|
+
// recover the hint.
|
|
157
|
+
const reexecutePendingEffect = async (stored) => {
|
|
158
|
+
const pending = stored.pendingEffect;
|
|
159
|
+
if (pending === undefined)
|
|
160
|
+
return false;
|
|
161
|
+
const sourceEvent = [...stored.history].reverse().find((candidate) => (candidate.state === pending.state));
|
|
162
|
+
if (sourceEvent === undefined)
|
|
163
|
+
return false;
|
|
164
|
+
const bindings = await listThreadBindings(sourceEvent.threadId);
|
|
165
|
+
// Legacy records carry a single platformMessageId; with exactly one
|
|
166
|
+
// matching binding it can only have belonged to that binding.
|
|
167
|
+
let messageIds = { ...(stored.platformMessageByKey ?? {}) };
|
|
168
|
+
if (Object.keys(messageIds).length === 0 && bindings.length === 1 && stored.platformMessageId !== undefined) {
|
|
169
|
+
messageIds = { [bindings[0].key]: stored.platformMessageId };
|
|
170
|
+
}
|
|
171
|
+
for (const binding of bindings) {
|
|
172
|
+
const platformMessageId = messageIds[binding.key];
|
|
173
|
+
if (pending.state === 'blocked') {
|
|
174
|
+
const edited = platformMessageId !== undefined && await runEffectSafely(sourceEvent.executionId, 'blocked', 'blocked edit', async () => {
|
|
175
|
+
const applied = await callbacks.editPlaceholder(binding.key, platformMessageId, pending.recoveryText, 'blocked', sourceEvent.executionId);
|
|
176
|
+
if (!applied)
|
|
177
|
+
throw new Error('placeholder is no longer editable');
|
|
178
|
+
});
|
|
179
|
+
if (!edited) {
|
|
180
|
+
await runEffectSafely(sourceEvent.executionId, 'blocked', 'blocked recovery send', () => callbacks.sendRecovery(binding.key, pending.recoveryText));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
await runEffectSafely(sourceEvent.executionId, 'settled', 'settlement', () => callbacks.settle({
|
|
185
|
+
externalConversationId: binding.key,
|
|
186
|
+
...(platformMessageId === undefined ? {} : { platformMessageId }),
|
|
187
|
+
actorDisplayName: stored.actorDisplayName,
|
|
188
|
+
recoveryText: pending.recoveryText,
|
|
189
|
+
event: sourceEvent,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return bindings.length > 0;
|
|
194
|
+
};
|
|
195
|
+
const clearPendingEffect = async (stateKey, stored) => {
|
|
196
|
+
try {
|
|
197
|
+
await context.storage.set(stateKey, { ...stored, pendingEffect: undefined });
|
|
198
|
+
countWrite();
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
// The marker survives, so a further redelivery re-executes the effect
|
|
202
|
+
// once more; losing the clear is duplicate-tolerant by design.
|
|
203
|
+
context.log('warn', 'Connector lifecycle pending-effect clear failed', {
|
|
204
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const handle = async (event) => {
|
|
209
|
+
const stateKey = `lifecycle/${event.executionId}`;
|
|
210
|
+
const safely = (label, effect) => runEffectSafely(event.executionId, event.state, label, effect);
|
|
211
|
+
const entry = await context.storage.get(stateKey);
|
|
212
|
+
const stored = entry === undefined ? undefined : storedLifecycle(entry.value);
|
|
213
|
+
if (stored?.tombstone === true) {
|
|
214
|
+
// The tombstone keeps the full event history, so redelivery judgment is
|
|
215
|
+
// the same as the pre-settle path: an exact redelivery of anything the
|
|
216
|
+
// Host already acked answers replay, the same deliveryId with different
|
|
217
|
+
// content is a delivery conflict, and anything else is out of order.
|
|
218
|
+
const tombstoneDecision = decideLifecycleTransition(stored.history, event);
|
|
219
|
+
if (tombstoneDecision.kind === 'replay') {
|
|
220
|
+
// Crash residue: the accepted side effect may never have run. Re-run
|
|
221
|
+
// it once for this redelivery. The marker is cleared only when the
|
|
222
|
+
// effect was actually attempted: with no binding there is nothing to
|
|
223
|
+
// retry, and a later redelivery must still be able to recover the
|
|
224
|
+
// hint (see reexecutePendingEffect for why not after success).
|
|
225
|
+
if (stored.pendingEffect !== undefined && stored.pendingEffect.state === event.state) {
|
|
226
|
+
if (await reexecutePendingEffect(stored)) {
|
|
227
|
+
await clearPendingEffect(stateKey, stored);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { deliveryId: event.deliveryId };
|
|
231
|
+
}
|
|
232
|
+
if (tombstoneDecision.kind === 'reject' && tombstoneDecision.reason === 'DELIVERY_CONFLICT') {
|
|
233
|
+
throw lifecycleError(lifecycleRejectReason(tombstoneDecision), `lifecycle ${tombstoneDecision.reason.toLowerCase()}`);
|
|
234
|
+
}
|
|
235
|
+
// Tombstones written before the full history was retained only know the
|
|
236
|
+
// accepted deliveryId set; for them that set is the only replay identity.
|
|
237
|
+
const deliveryIds = stored.deliveryIds ?? [];
|
|
238
|
+
if (deliveryIds.includes(event.deliveryId)) {
|
|
239
|
+
return { deliveryId: event.deliveryId };
|
|
240
|
+
}
|
|
241
|
+
throw lifecycleError('EXECUTION_OUT_OF_ORDER', 'lifecycle is already settled');
|
|
242
|
+
}
|
|
243
|
+
const history = stored?.history ?? [];
|
|
244
|
+
const decision = decideLifecycleTransition(history, event);
|
|
245
|
+
if (decision.kind === 'replay') {
|
|
246
|
+
// Crash residue, pre-settle flavor: the blocked recovery effect was
|
|
247
|
+
// accepted but may never have run. Re-run it once. The marker is
|
|
248
|
+
// cleared only when the effect was actually attempted: with no binding
|
|
249
|
+
// a later redelivery must still be able to recover the hint.
|
|
250
|
+
if (stored?.pendingEffect !== undefined && stored.pendingEffect.state === event.state) {
|
|
251
|
+
if (await reexecutePendingEffect(stored)) {
|
|
252
|
+
await clearPendingEffect(stateKey, stored);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return { deliveryId: event.deliveryId };
|
|
256
|
+
}
|
|
257
|
+
if (decision.kind === 'reject') {
|
|
258
|
+
throw lifecycleError(lifecycleRejectReason(decision), `lifecycle ${decision.reason.toLowerCase()}`);
|
|
259
|
+
}
|
|
260
|
+
// A new transition arrives while an older one still has an unconfirmed
|
|
261
|
+
// side effect (the crash window, or a lost post-write): this write would
|
|
262
|
+
// overwrite the marker, so flush the pending effect first and clear the
|
|
263
|
+
// marker. The marker is cleared only when the flush was actually
|
|
264
|
+
// attempted on at least one binding: with no binding the flush is a
|
|
265
|
+
// no-op and clearing here would lose the recovery hint for good once
|
|
266
|
+
// the zero-binding throw below rejects this delivery. Without the clear,
|
|
267
|
+
// a failed new pre-write would leave the old marker in place and every
|
|
268
|
+
// Host retry of this delivery would re-run the already-flushed effect
|
|
269
|
+
// again. An occasional duplicate beats a lost recovery hint.
|
|
270
|
+
if (stored?.pendingEffect !== undefined) {
|
|
271
|
+
if (await reexecutePendingEffect(stored)) {
|
|
272
|
+
await clearPendingEffect(stateKey, stored);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// One thread can carry several provider bindings (the Host allows
|
|
276
|
+
// rebinding different external chats onto the same thread), so every
|
|
277
|
+
// matching binding receives the outbound effects.
|
|
278
|
+
const bindings = await listThreadBindings(event.threadId);
|
|
279
|
+
if (bindings.length === 0) {
|
|
280
|
+
throw lifecycleError('PLUGIN_INTERNAL', `lifecycle thread ${event.threadId} has no provider binding`);
|
|
281
|
+
}
|
|
282
|
+
const actorDisplayName = event.state === 'started'
|
|
283
|
+
? event.presentation.actor.displayName
|
|
284
|
+
: stored?.actorDisplayName ?? '';
|
|
285
|
+
// Per-binding placeholder message ids. Legacy records carry a single
|
|
286
|
+
// platformMessageId; with exactly one matching binding it can only have
|
|
287
|
+
// belonged to that binding.
|
|
288
|
+
let messageIds = { ...(stored?.platformMessageByKey ?? {}) };
|
|
289
|
+
if (Object.keys(messageIds).length === 0 && bindings.length === 1 && stored?.platformMessageId !== undefined) {
|
|
290
|
+
messageIds = { [bindings[0].key]: stored.platformMessageId };
|
|
291
|
+
}
|
|
292
|
+
// Recovery text is decided up front: the blocked transition's hint, or
|
|
293
|
+
// the retained blocked event's hint for a settled transition.
|
|
294
|
+
const blocked = history.slice(history.map(candidate => candidate.state).lastIndexOf('resumed') + 1).reverse().find((candidate) => (candidate.state === 'blocked'));
|
|
295
|
+
// Transitions carrying a user-facing recovery side effect are marked
|
|
296
|
+
// pending-effect in the pre-write; the post-write clears the marker once
|
|
297
|
+
// the effect completed. Started placeholders stay at-most-once on
|
|
298
|
+
// purpose — the final reply arrives separately — and catching_up edits
|
|
299
|
+
// self-heal through the later blocked/settle effects.
|
|
300
|
+
const pendingEffect = event.state === 'blocked'
|
|
301
|
+
? { v: 1, state: 'blocked', recoveryText: recoveryText(event) }
|
|
302
|
+
: event.state === 'settled' && blocked !== undefined
|
|
303
|
+
? { v: 1, state: 'settled', recoveryText: recoveryText(blocked) }
|
|
304
|
+
: undefined;
|
|
305
|
+
// Settled records compress into a tombstone: platform effects stop, but
|
|
306
|
+
// the full event history is retained so redelivery judgment can still
|
|
307
|
+
// distinguish an exact replay from a same-deliveryId content conflict.
|
|
308
|
+
const nextRecord = (ids, pending) => ({
|
|
309
|
+
version: 2,
|
|
310
|
+
history: [...history, event],
|
|
311
|
+
// Keep the legacy single-id field only when it is unambiguous.
|
|
312
|
+
...(Object.keys(ids).length === 1 ? { platformMessageId: Object.values(ids)[0] } : {}),
|
|
313
|
+
...(Object.keys(ids).length === 0 ? {} : { platformMessageByKey: ids }),
|
|
314
|
+
actorDisplayName,
|
|
315
|
+
updatedAt: Date.now(),
|
|
316
|
+
...(pending !== undefined ? { pendingEffect: pending } : {}),
|
|
317
|
+
...(event.state === 'settled'
|
|
318
|
+
? { tombstone: true, deliveryIds: [...history.map(candidate => candidate.deliveryId), event.deliveryId], settledAt: Date.now() }
|
|
319
|
+
: {}),
|
|
320
|
+
});
|
|
321
|
+
// Write-ahead: persist the accepted event before any platform side
|
|
322
|
+
// effect, so a crash between effect and store cannot leave later events
|
|
323
|
+
// orphaned as OUT_OF_ORDER. The pre-write carries the pending-effect
|
|
324
|
+
// marker, so a redelivery that finds it re-runs the unconfirmed effect.
|
|
325
|
+
// If even the identical retry fails we throw before any side effect: no
|
|
326
|
+
// half-persisted record, no duplicate sends.
|
|
327
|
+
const preWrite = nextRecord(messageIds, pendingEffect);
|
|
328
|
+
try {
|
|
329
|
+
await context.storage.set(stateKey, preWrite);
|
|
330
|
+
countWrite();
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
try {
|
|
334
|
+
await context.storage.set(stateKey, preWrite);
|
|
335
|
+
countWrite();
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
throw lifecycleError('PLUGIN_INTERNAL', `lifecycle ${event.executionId} state pre-write failed: ${error instanceof Error ? error.message : 'unknown'}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Resolve the sender name once, outside the per-binding loop: it only
|
|
342
|
+
// depends on event.replyTo, never on the binding, so N bindings must not
|
|
343
|
+
// trigger N lookups. Resolve before the safely() wrapper: a throwing
|
|
344
|
+
// callback must not take down the whole placeholder send. resolve() in
|
|
345
|
+
// reply-sender-map already swallows storage errors; this catch covers
|
|
346
|
+
// every other failure reason.
|
|
347
|
+
let senderName;
|
|
348
|
+
if (event.state === 'started' && event.replyTo !== undefined && callbacks.resolveReplySenderName !== undefined) {
|
|
349
|
+
try {
|
|
350
|
+
senderName = await callbacks.resolveReplySenderName(event.replyTo);
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
context.log('warn', 'Connector lifecycle sender name resolution failed', {
|
|
354
|
+
executionId: event.executionId,
|
|
355
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
356
|
+
});
|
|
357
|
+
senderName = undefined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const senderSuffix = senderName ? `→${senderName}` : '';
|
|
361
|
+
for (const binding of bindings) {
|
|
362
|
+
let platformMessageId = messageIds[binding.key];
|
|
363
|
+
switch (event.state) {
|
|
364
|
+
case 'started':
|
|
365
|
+
await safely('placeholder send', async () => {
|
|
366
|
+
const displayName = actorDisplayName || '猫猫';
|
|
367
|
+
const placeholderLine = event.placeholderLine ?? STARTED_TEXT;
|
|
368
|
+
const candidate = await callbacks.sendPlaceholder(binding.key, `【${displayName}🐱${senderSuffix}】${placeholderLine}`);
|
|
369
|
+
platformMessageId = candidate.length > 0 ? candidate : undefined;
|
|
370
|
+
if (platformMessageId !== undefined) {
|
|
371
|
+
await callbacks.onPlaceholder?.(binding.key, platformMessageId, event.executionId);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
break;
|
|
375
|
+
case 'resumed':
|
|
376
|
+
await safely('resumed feedback', async () => {
|
|
377
|
+
const text = `【${actorDisplayName || '猫猫'}🐱】${STARTED_TEXT}`;
|
|
378
|
+
if (platformMessageId !== undefined && await callbacks.editPlaceholder(binding.key, platformMessageId, text, 'catching_up', event.executionId))
|
|
379
|
+
return;
|
|
380
|
+
const candidate = await callbacks.sendPlaceholder(binding.key, text);
|
|
381
|
+
platformMessageId = candidate || undefined;
|
|
382
|
+
if (platformMessageId)
|
|
383
|
+
await callbacks.onPlaceholder?.(binding.key, platformMessageId, event.executionId);
|
|
384
|
+
});
|
|
385
|
+
break;
|
|
386
|
+
case 'catching_up':
|
|
387
|
+
if (platformMessageId !== undefined) {
|
|
388
|
+
await safely('catching-up edit', () => callbacks.editPlaceholder(binding.key, platformMessageId, CATCHING_UP_TEXT, 'catching_up', event.executionId));
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
case 'blocked':
|
|
392
|
+
{
|
|
393
|
+
const text = recoveryText(event);
|
|
394
|
+
const edited = platformMessageId !== undefined && await safely('blocked edit', async () => {
|
|
395
|
+
const applied = await callbacks.editPlaceholder(binding.key, platformMessageId, text, 'blocked', event.executionId);
|
|
396
|
+
if (!applied)
|
|
397
|
+
throw new Error('placeholder is no longer editable');
|
|
398
|
+
});
|
|
399
|
+
if (!edited) {
|
|
400
|
+
await safely('blocked recovery send', () => callbacks.sendRecovery(binding.key, text));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
break;
|
|
404
|
+
case 'settled': {
|
|
405
|
+
await safely('settlement', () => callbacks.settle({
|
|
406
|
+
externalConversationId: binding.key,
|
|
407
|
+
...(platformMessageId === undefined ? {} : { platformMessageId }),
|
|
408
|
+
actorDisplayName,
|
|
409
|
+
...(blocked === undefined ? {} : { recoveryText: recoveryText(blocked) }),
|
|
410
|
+
event,
|
|
411
|
+
}));
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (platformMessageId === undefined)
|
|
416
|
+
delete messageIds[binding.key];
|
|
417
|
+
else
|
|
418
|
+
messageIds[binding.key] = platformMessageId;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
await context.storage.set(stateKey, nextRecord(messageIds));
|
|
422
|
+
countWrite();
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
// Post-write failure is warn-only: the event already sits in history
|
|
426
|
+
// via the write-ahead, so a Host replay of this delivery is answered
|
|
427
|
+
// as replay and never re-runs the platform side effects. The recovery
|
|
428
|
+
// effect is the exception: its marker survives until a redelivery
|
|
429
|
+
// re-executes it, so a lost post-write there is duplicate-tolerant
|
|
430
|
+
// rather than losing the hint. The only other loss is the fresh
|
|
431
|
+
// platformMessageId, covered by the existing
|
|
432
|
+
// undefined-platformMessageId fallback for later edits.
|
|
433
|
+
context.log('warn', 'Connector lifecycle state post-write failed', {
|
|
434
|
+
executionId: event.executionId,
|
|
435
|
+
state: event.state,
|
|
436
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
return { deliveryId: event.deliveryId };
|
|
440
|
+
};
|
|
441
|
+
const action = defineLifecycleAction(async (event) => {
|
|
442
|
+
const previous = tails.get(event.executionId) ?? Promise.resolve();
|
|
443
|
+
const current = previous.catch(() => undefined).then(() => handle(event));
|
|
444
|
+
const tail = current.then(() => undefined, () => undefined);
|
|
445
|
+
tails.set(event.executionId, tail);
|
|
446
|
+
try {
|
|
447
|
+
return await current;
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
if (tails.get(event.executionId) === tail)
|
|
451
|
+
tails.delete(event.executionId);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
// Test/debug hook: force the background sweep synchronously.
|
|
455
|
+
return Object.assign(action, { sweepNow: sweep });
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=lifecycle-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-action.js","sourceRoot":"","sources":["../src/lifecycle-action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface MaterializedMedia {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
cleanup(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
/** Materialize a Host-authorized byte stream inside a package-owned private directory. */
|
|
6
|
+
export declare function materializeMedia(content: AsyncIterable<Uint8Array>, fileName: string | undefined, maxBytes: number): Promise<MaterializedMedia>;
|
|
7
|
+
//# sourceMappingURL=materialize-media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize-media.d.ts","sourceRoot":"","sources":["../src/materialize-media.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,0FAA0F;AAC1F,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAClC,QAAQ,oBAAc,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAwB5B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createWriteStream } from 'node:fs';
|
|
2
|
+
import { chmod, mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { basename, join } from 'node:path';
|
|
5
|
+
import { Readable } from 'node:stream';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
/** Materialize a Host-authorized byte stream inside a package-owned private directory. */
|
|
8
|
+
export async function materializeMedia(content, fileName = 'media.bin', maxBytes) {
|
|
9
|
+
const directory = await mkdtemp(join(tmpdir(), 'clowder-dingtalk-outbound-'));
|
|
10
|
+
await chmod(directory, 0o700);
|
|
11
|
+
const candidate = basename(fileName).trim();
|
|
12
|
+
const safeName = candidate.length === 0 || candidate === '.' || candidate === '..' ? 'media.bin' : candidate;
|
|
13
|
+
const path = join(directory, safeName);
|
|
14
|
+
try {
|
|
15
|
+
let totalBytes = 0;
|
|
16
|
+
async function* boundedContent() {
|
|
17
|
+
for await (const chunk of content) {
|
|
18
|
+
totalBytes += chunk.byteLength;
|
|
19
|
+
if (totalBytes > maxBytes)
|
|
20
|
+
throw new RangeError(`media exceeds ${maxBytes} byte platform limit`);
|
|
21
|
+
yield chunk;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
await pipeline(Readable.from(boundedContent()), createWriteStream(path, { flags: 'wx', mode: 0o600 }));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
await rm(directory, { recursive: true, force: true }).catch(() => undefined);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
path,
|
|
32
|
+
cleanup: () => rm(directory, { recursive: true, force: true }),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=materialize-media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize-media.js","sourceRoot":"","sources":["../src/materialize-media.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAOhD,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAkC,EAClC,QAAQ,GAAG,WAAW,EACtB,QAAgB;IAEhB,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC,CAAC;IAC9E,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9B,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,KAAK,GAAG,IAAI,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7G,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,SAAS,CAAC,CAAC,cAAc;YAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAClC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC;gBAC/B,IAAI,UAAU,GAAG,QAAQ;oBAAE,MAAM,IAAI,UAAU,CAAC,iBAAiB,QAAQ,sBAAsB,CAAC,CAAC;gBACjG,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,EAAE,iBAAiB,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACzG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC7E,MAAM,KAAK,CAAC;IACd,CAAC;IACD,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KAC/D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface TypedMediaNoticeOptions {
|
|
2
|
+
readonly elements?: readonly unknown[];
|
|
3
|
+
readonly warnInvalid?: (elementId: string) => void;
|
|
4
|
+
}
|
|
5
|
+
export declare function renderTypedMediaNotice(element: unknown, options?: TypedMediaNoticeOptions): string | undefined;
|
|
6
|
+
//# sourceMappingURL=media-notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-notice.d.ts","sourceRoot":"","sources":["../src/media-notice.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACvC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAoBD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,uBAA4B,GACpC,MAAM,GAAG,SAAS,CAapB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isMediaUnavailableMessageElement, isMediaWarningMessageElement, } from '@clowder-ai/plugin-sdk';
|
|
2
|
+
const MEDIA_TYPE_LABEL = {
|
|
3
|
+
image: '图片',
|
|
4
|
+
file: '文件',
|
|
5
|
+
audio: '音频',
|
|
6
|
+
video: '视频',
|
|
7
|
+
};
|
|
8
|
+
const UNAVAILABLE_REASON_LABEL = {
|
|
9
|
+
source_expired: '来源已过期',
|
|
10
|
+
timeout: '处理超时',
|
|
11
|
+
unavailable: '无法获取',
|
|
12
|
+
};
|
|
13
|
+
const WARNING_STAGE_LABEL = {
|
|
14
|
+
transcription: '转写',
|
|
15
|
+
preview: '预览',
|
|
16
|
+
};
|
|
17
|
+
const WARNING_REASON_LABEL = {
|
|
18
|
+
timeout: '超时',
|
|
19
|
+
processing_failed: '处理失败',
|
|
20
|
+
};
|
|
21
|
+
function object(value) {
|
|
22
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function referencedMediaLabel(elements, mediaElementId) {
|
|
25
|
+
const referenced = elements.find((candidate) => (object(candidate) && candidate.elementId === mediaElementId && candidate.kind === 'media_ref'));
|
|
26
|
+
if (!object(referenced) || !object(referenced.payload))
|
|
27
|
+
return undefined;
|
|
28
|
+
if (typeof referenced.payload.fileName === 'string' && referenced.payload.fileName.trim() !== '') {
|
|
29
|
+
return referenced.payload.fileName;
|
|
30
|
+
}
|
|
31
|
+
const type = referenced.payload.type;
|
|
32
|
+
return typeof type === 'string' && type in MEDIA_TYPE_LABEL
|
|
33
|
+
? MEDIA_TYPE_LABEL[type]
|
|
34
|
+
: undefined;
|
|
35
|
+
}
|
|
36
|
+
export function renderTypedMediaNotice(element, options = {}) {
|
|
37
|
+
if (isMediaUnavailableMessageElement(element)) {
|
|
38
|
+
const label = element.payload.fileName ?? MEDIA_TYPE_LABEL[element.payload.type];
|
|
39
|
+
return `⚠️ 媒体不可用:${label}(${UNAVAILABLE_REASON_LABEL[element.payload.reason]})`;
|
|
40
|
+
}
|
|
41
|
+
if (isMediaWarningMessageElement(element)) {
|
|
42
|
+
const label = referencedMediaLabel(options.elements ?? [], element.payload.mediaElementId) ?? '媒体';
|
|
43
|
+
return `⚠️ 媒体处理警告:${label}(${WARNING_STAGE_LABEL[element.payload.stage]}${WARNING_REASON_LABEL[element.payload.reason]})`;
|
|
44
|
+
}
|
|
45
|
+
if (object(element) && (element.kind === 'media_unavailable' || element.kind === 'media_warning')) {
|
|
46
|
+
options.warnInvalid?.(typeof element.elementId === 'string' ? element.elementId : 'unknown');
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=media-notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-notice.js","sourceRoot":"","sources":["../src/media-notice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DingTalkConnectorRuntime, type DingTalkConnectorRuntimeOptions } from './runtime.js';
|
|
2
|
+
import { DingTalkAdapter } from './DingTalkAdapter.js';
|
|
3
|
+
type DingTalkRuntimeFactory = (options: DingTalkConnectorRuntimeOptions<DingTalkAdapter>) => DingTalkConnectorRuntime<DingTalkAdapter>;
|
|
4
|
+
export declare function createDingTalkPluginModule(createRuntime?: DingTalkRuntimeFactory): import("@clowder-ai/plugin-sdk").PluginModuleEntrypoint;
|
|
5
|
+
declare const _default: import("@clowder-ai/plugin-sdk").PluginModuleEntrypoint;
|
|
6
|
+
export default _default;
|
|
7
|
+
//# sourceMappingURL=plugin-entrypoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-entrypoint.d.ts","sourceRoot":"","sources":["../src/plugin-entrypoint.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,+BAA+B,EAErC,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOvD,KAAK,sBAAsB,GAAG,CAC5B,OAAO,EAAE,+BAA+B,CAAC,eAAe,CAAC,KACtD,wBAAwB,CAAC,eAAe,CAAC,CAAC;AAkN/C,wBAAgB,0BAA0B,CACxC,aAAa,GAAE,sBAAuD,2DAkFvE;;AAED,wBAA4C"}
|