@tencent-weixin/openclaw-weixin 2.3.1 → 2.4.2
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/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/api.js +374 -0
- package/dist/src/api/api.js.map +1 -0
- package/dist/src/api/config-cache.js +64 -0
- package/dist/src/api/config-cache.js.map +1 -0
- package/dist/src/api/session-guard.js +49 -0
- package/dist/src/api/session-guard.js.map +1 -0
- package/dist/src/api/types.js +35 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/auth/accounts.js +326 -0
- package/dist/src/auth/accounts.js.map +1 -0
- package/dist/src/auth/login-qr.js +332 -0
- package/dist/src/auth/login-qr.js.map +1 -0
- package/dist/src/auth/pairing.js +104 -0
- package/dist/src/auth/pairing.js.map +1 -0
- package/dist/src/cdn/aes-ecb.js +19 -0
- package/dist/src/cdn/aes-ecb.js.map +1 -0
- package/dist/src/cdn/cdn-upload.js +73 -0
- package/dist/src/cdn/cdn-upload.js.map +1 -0
- package/dist/src/cdn/cdn-url.js +14 -0
- package/dist/src/cdn/cdn-url.js.map +1 -0
- package/dist/src/cdn/pic-decrypt.js +89 -0
- package/dist/src/cdn/pic-decrypt.js.map +1 -0
- package/dist/src/cdn/upload.js +106 -0
- package/dist/src/cdn/upload.js.map +1 -0
- package/dist/src/channel.js +460 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/compat.js +67 -0
- package/dist/src/compat.js.map +1 -0
- package/dist/src/config/config-schema.js +19 -0
- package/dist/src/config/config-schema.js.map +1 -0
- package/dist/src/media/media-download.js +95 -0
- package/dist/src/media/media-download.js.map +1 -0
- package/dist/src/media/mime.js +73 -0
- package/dist/src/media/mime.js.map +1 -0
- package/dist/src/media/silk-transcode.js +64 -0
- package/dist/src/media/silk-transcode.js.map +1 -0
- package/dist/src/media/voice-outbound.js +177 -0
- package/dist/src/media/voice-outbound.js.map +1 -0
- package/dist/src/messaging/abort-fence.js +70 -0
- package/dist/src/messaging/abort-fence.js.map +1 -0
- package/dist/src/messaging/buttons.js +117 -0
- package/dist/src/messaging/buttons.js.map +1 -0
- package/dist/src/messaging/debug-mode.js +63 -0
- package/dist/src/messaging/debug-mode.js.map +1 -0
- package/dist/src/messaging/error-notice.js +24 -0
- package/dist/src/messaging/error-notice.js.map +1 -0
- package/dist/src/messaging/inbound.js +201 -0
- package/dist/src/messaging/inbound.js.map +1 -0
- package/dist/src/messaging/lane-key.js +66 -0
- package/dist/src/messaging/lane-key.js.map +1 -0
- package/dist/src/messaging/markdown-filter.js +368 -0
- package/dist/src/messaging/markdown-filter.js.map +1 -0
- package/dist/src/messaging/merged-record.js +149 -0
- package/dist/src/messaging/merged-record.js.map +1 -0
- package/dist/src/messaging/model-buttons.js +182 -0
- package/dist/src/messaging/model-buttons.js.map +1 -0
- package/dist/src/messaging/model-callback-handler.js +133 -0
- package/dist/src/messaging/model-callback-handler.js.map +1 -0
- package/dist/src/messaging/outbound-hooks.js +56 -0
- package/dist/src/messaging/outbound-hooks.js.map +1 -0
- package/dist/src/messaging/process-message.js +381 -0
- package/dist/src/messaging/process-message.js.map +1 -0
- package/dist/src/messaging/send-media.js +54 -0
- package/dist/src/messaging/send-media.js.map +1 -0
- package/dist/src/messaging/send.js +182 -0
- package/dist/src/messaging/send.js.map +1 -0
- package/dist/src/messaging/slash-commands.js +70 -0
- package/dist/src/messaging/slash-commands.js.map +1 -0
- package/dist/src/monitor/lane-scheduler.js +46 -0
- package/dist/src/monitor/lane-scheduler.js.map +1 -0
- package/dist/src/monitor/monitor.js +143 -0
- package/dist/src/monitor/monitor.js.map +1 -0
- package/dist/src/runtime.js +54 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/storage/state-dir.js +9 -0
- package/dist/src/storage/state-dir.js.map +1 -0
- package/dist/src/storage/sync-buf.js +64 -0
- package/dist/src/storage/sync-buf.js.map +1 -0
- package/dist/src/streaming/stream-pipeline.js +431 -0
- package/dist/src/streaming/stream-pipeline.js.map +1 -0
- package/dist/src/streaming/stream-session.js +260 -0
- package/dist/src/streaming/stream-session.js.map +1 -0
- package/dist/src/streaming/stream.js +239 -0
- package/dist/src/streaming/stream.js.map +1 -0
- package/dist/src/util/logger.js +120 -0
- package/dist/src/util/logger.js.map +1 -0
- package/dist/src/util/markdown-fences.js +54 -0
- package/dist/src/util/markdown-fences.js.map +1 -0
- package/dist/src/util/random.js +16 -0
- package/dist/src/util/random.js.map +1 -0
- package/dist/src/util/redact.js +54 -0
- package/dist/src/util/redact.js.map +1 -0
- package/index.ts +0 -5
- package/openclaw.plugin.json +11 -1
- package/package.json +9 -2
- package/src/api/api.ts +2 -3
- package/src/auth/accounts.ts +0 -1
- package/src/channel.ts +13 -1
- package/src/monitor/monitor.ts +11 -10
- package/src/runtime.ts +0 -70
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { StreamingMarkdownFilter } from "../messaging/markdown-filter.js";
|
|
2
|
+
import { findFenceSpanAt, parseFenceSpans } from "../util/markdown-fences.js";
|
|
3
|
+
/**
|
|
4
|
+
* Matches every line starting with `WEIXIN_BUTTONS:` in the text. Kept in
|
|
5
|
+
* sync with INLINE_BUTTONS_RE in messaging/buttons.ts so the streaming
|
|
6
|
+
* suppressor drops exactly what parseInlineButtons later strips at deliver
|
|
7
|
+
* — including the fence-aware skip (a marker inside a code block is treated
|
|
8
|
+
* as literal documentation, never suppressed and never stripped).
|
|
9
|
+
*/
|
|
10
|
+
const MARKER_LINE_START_RE = /^WEIXIN_BUTTONS:/gm;
|
|
11
|
+
function stripReasoningFormat(text) {
|
|
12
|
+
let s = text;
|
|
13
|
+
if (s.startsWith("Reasoning:\n"))
|
|
14
|
+
s = s.slice("Reasoning:\n".length);
|
|
15
|
+
else if (s.startsWith("Reasoning:"))
|
|
16
|
+
s = s.slice("Reasoning:".length);
|
|
17
|
+
else
|
|
18
|
+
return text;
|
|
19
|
+
return s.split("\n").map((line) => {
|
|
20
|
+
if (line.startsWith("_") && line.endsWith("_") && line.length >= 2) {
|
|
21
|
+
return line.slice(1, -1);
|
|
22
|
+
}
|
|
23
|
+
if (line.startsWith("_"))
|
|
24
|
+
return line.slice(1);
|
|
25
|
+
if (line.endsWith("_"))
|
|
26
|
+
return line.slice(0, -1);
|
|
27
|
+
return line;
|
|
28
|
+
}).join("\n");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Thin synchronous adapter that converts SDK callbacks into pipeline commands.
|
|
32
|
+
*
|
|
33
|
+
* Handles only text-processing concerns (delta extraction, markdown filtering,
|
|
34
|
+
* reasoning format stripping). All async protocol work is delegated to the
|
|
35
|
+
* StreamPipeline via `push()`.
|
|
36
|
+
*/
|
|
37
|
+
export class StreamSession {
|
|
38
|
+
pipeline;
|
|
39
|
+
log;
|
|
40
|
+
// -- text processing (synchronous) --
|
|
41
|
+
prevReasoningText = "";
|
|
42
|
+
prevPartialText = "";
|
|
43
|
+
thinkingMdFilter = new StreamingMarkdownFilter();
|
|
44
|
+
resultMdFilter = new StreamingMarkdownFilter();
|
|
45
|
+
resultFilteredAccum = "";
|
|
46
|
+
// -- WEIXIN_BUTTONS suppression for streaming --
|
|
47
|
+
_buttonsLineSuppressed = false;
|
|
48
|
+
// -- debug/logging --
|
|
49
|
+
thinkingSegments = [];
|
|
50
|
+
// -- deliver payload (managed locally, not a pipeline command) --
|
|
51
|
+
_lastDeliverPayload;
|
|
52
|
+
_finalTextCache;
|
|
53
|
+
// -- TEMP DEBUG: track MEDIA: directive presence in raw stream --
|
|
54
|
+
_mediaDebugSeenAt;
|
|
55
|
+
_mediaDebugFirstSnippet;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
this.pipeline = opts.pipeline;
|
|
58
|
+
this.log = opts.log;
|
|
59
|
+
}
|
|
60
|
+
/** Reply callbacks to spread into replyOptions. */
|
|
61
|
+
get replyCallbacks() {
|
|
62
|
+
return {
|
|
63
|
+
onReasoningStream: (p) => this.onReasoningStream(p),
|
|
64
|
+
onReasoningEnd: () => this.onReasoningEnd(),
|
|
65
|
+
onToolStart: (p) => this.onToolStart(p),
|
|
66
|
+
onAssistantMessageStart: () => this.onAssistantMessageStart(),
|
|
67
|
+
onPartialReply: (p) => this.onPartialReply(p),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
bufferDeliverPayload(payload) {
|
|
71
|
+
this._lastDeliverPayload = payload;
|
|
72
|
+
this._finalTextCache = undefined;
|
|
73
|
+
}
|
|
74
|
+
get lastDeliverPayload() { return this._lastDeliverPayload; }
|
|
75
|
+
get streamId() { return this.pipeline.resultStreamId; }
|
|
76
|
+
get hadStreaming() { return this.pipeline.hadStreaming; }
|
|
77
|
+
/**
|
|
78
|
+
* One-shot markdown-filtered version of the deliver text.
|
|
79
|
+
* Result is memoized; invalidated when bufferDeliverPayload is called.
|
|
80
|
+
*/
|
|
81
|
+
getFinalText() {
|
|
82
|
+
if (!this._lastDeliverPayload)
|
|
83
|
+
return "";
|
|
84
|
+
if (this._finalTextCache !== undefined)
|
|
85
|
+
return this._finalTextCache;
|
|
86
|
+
const f = new StreamingMarkdownFilter();
|
|
87
|
+
this._finalTextCache = f.feed(this._lastDeliverPayload.text) + f.flush();
|
|
88
|
+
return this._finalTextCache;
|
|
89
|
+
}
|
|
90
|
+
/** Flush filters, push finalize command, and wait for the pipeline to drain. */
|
|
91
|
+
async finalize() {
|
|
92
|
+
this.log.debug(`stream-cb: finalize hadDeliver=${this._lastDeliverPayload != null} resultAccumLen=${this.resultFilteredAccum.length} prevPartialLen=${this.prevPartialText.length}`);
|
|
93
|
+
// [MEDIA-DEBUG] Dump the tail of the raw accumulated text so we can compare
|
|
94
|
+
// against the deliver payload (which has MEDIA: stripped by the host).
|
|
95
|
+
{
|
|
96
|
+
const raw = this.prevPartialText;
|
|
97
|
+
const tail = raw.slice(-300).replaceAll("\n", "\\n");
|
|
98
|
+
const hasMedia = /\bmedia:/i.test(raw);
|
|
99
|
+
const allMediaMatches = hasMedia
|
|
100
|
+
? Array.from(raw.matchAll(/\bMEDIA:[^\n]*/gi)).map((m) => m[0])
|
|
101
|
+
: [];
|
|
102
|
+
this.log.info(`[media-debug] finalize rawLen=${raw.length} hasMediaInRaw=${hasMedia} mediaLines=${JSON.stringify(allMediaMatches)} tail="${tail}"`);
|
|
103
|
+
if (this._lastDeliverPayload) {
|
|
104
|
+
const dt = this._lastDeliverPayload.text ?? "";
|
|
105
|
+
this.log.info(`[media-debug] finalize deliver payload textLen=${dt.length} mediaUrl=${this._lastDeliverPayload.mediaUrl ?? "none"} deliverHasMedia=${/\bmedia:/i.test(dt)} deliverTail="${dt.slice(-300).replaceAll("\n", "\\n")}"`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.log.info(`[media-debug] finalize no deliver payload buffered`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Thinking filter is flushed by onReasoningEnd(); we don't flush it again
|
|
112
|
+
// here to avoid pushing stale thinking text into a result-phase pipeline.
|
|
113
|
+
if (this.prevReasoningText) {
|
|
114
|
+
this.thinkingSegments.push({ type: "text", text: this.prevReasoningText });
|
|
115
|
+
this.prevReasoningText = "";
|
|
116
|
+
}
|
|
117
|
+
this.log.debug(`stream: thinking segments=${JSON.stringify(this.thinkingSegments)}`);
|
|
118
|
+
// Flush result markdown filter → push remaining as result text
|
|
119
|
+
const resultRemaining = this.resultMdFilter.flush();
|
|
120
|
+
if (resultRemaining) {
|
|
121
|
+
this.resultFilteredAccum += resultRemaining;
|
|
122
|
+
this.pipeline.push({ kind: "result", text: resultRemaining });
|
|
123
|
+
}
|
|
124
|
+
// Check for gap between streamed accum and one-shot filtered deliver text.
|
|
125
|
+
// Only fill the gap when some result text was already streamed — otherwise
|
|
126
|
+
// (e.g. synchronous command replies like /models) pushing a fresh "result"
|
|
127
|
+
// would force the pipeline to start a brand-new streaming phase for a
|
|
128
|
+
// single buffered payload, costing ~2s of extra HTTP round-trips. In the
|
|
129
|
+
// thinking-only → deliver case the pipeline's doFinalize handles the
|
|
130
|
+
// late thinking→result transition via its own _hadStreaming branch.
|
|
131
|
+
if (this._lastDeliverPayload && this.resultFilteredAccum.length > 0) {
|
|
132
|
+
const fullFiltered = this.getFinalText();
|
|
133
|
+
if (fullFiltered.length > this.resultFilteredAccum.length &&
|
|
134
|
+
fullFiltered.startsWith(this.resultFilteredAccum)) {
|
|
135
|
+
const gap = fullFiltered.slice(this.resultFilteredAccum.length);
|
|
136
|
+
this.pipeline.push({ kind: "result", text: gap });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.pipeline.push({
|
|
140
|
+
kind: "finalize",
|
|
141
|
+
deliverText: this.getFinalText() || undefined,
|
|
142
|
+
});
|
|
143
|
+
await this.pipeline.drain();
|
|
144
|
+
}
|
|
145
|
+
async abort(errorMsg) {
|
|
146
|
+
await this.pipeline.abort(errorMsg);
|
|
147
|
+
}
|
|
148
|
+
// ---- SDK callbacks (all synchronous, just push commands) ----
|
|
149
|
+
onReasoningStream(payload) {
|
|
150
|
+
const rawText = payload.text ?? "";
|
|
151
|
+
const stripped = stripReasoningFormat(rawText);
|
|
152
|
+
if (stripped === this.prevReasoningText)
|
|
153
|
+
return;
|
|
154
|
+
const isRefresh = !stripped.startsWith(this.prevReasoningText);
|
|
155
|
+
const delta = isRefresh ? stripped : stripped.slice(this.prevReasoningText.length);
|
|
156
|
+
this.prevReasoningText = stripped;
|
|
157
|
+
if (!delta)
|
|
158
|
+
return;
|
|
159
|
+
if (isRefresh) {
|
|
160
|
+
this.thinkingMdFilter = new StreamingMarkdownFilter();
|
|
161
|
+
}
|
|
162
|
+
const filtered = this.thinkingMdFilter.feed(delta);
|
|
163
|
+
if (!filtered)
|
|
164
|
+
return;
|
|
165
|
+
if (isRefresh) {
|
|
166
|
+
this.pipeline.push({ kind: "thinking_refresh", text: filtered });
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.pipeline.push({ kind: "thinking", text: filtered });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
onReasoningEnd() {
|
|
173
|
+
this.log.debug(`stream-cb: onReasoningEnd`);
|
|
174
|
+
const remaining = this.thinkingMdFilter.flush();
|
|
175
|
+
if (remaining) {
|
|
176
|
+
this.pipeline.push({ kind: "thinking", text: remaining });
|
|
177
|
+
}
|
|
178
|
+
if (this.prevReasoningText) {
|
|
179
|
+
this.thinkingSegments.push({ type: "text", text: this.prevReasoningText });
|
|
180
|
+
this.prevReasoningText = "";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
onToolStart(payload) {
|
|
184
|
+
this.log.debug(`stream-cb: onToolStart name=${payload.name ?? "?"} phase=${payload.phase ?? "?"}`);
|
|
185
|
+
const phase = payload.phase === "complete" ? "end"
|
|
186
|
+
: payload.phase === "running" ? "continue"
|
|
187
|
+
: "start";
|
|
188
|
+
if (phase === "start") {
|
|
189
|
+
this.thinkingSegments.push({ type: "tool_calling", name: payload.name });
|
|
190
|
+
}
|
|
191
|
+
this.pipeline.push({ kind: "tool_call", name: payload.name, phase: phase });
|
|
192
|
+
}
|
|
193
|
+
onAssistantMessageStart() {
|
|
194
|
+
this.log.debug(`stream-cb: onAssistantMessageStart`);
|
|
195
|
+
this.prevPartialText = "";
|
|
196
|
+
this.resultFilteredAccum = "";
|
|
197
|
+
this.resultMdFilter = new StreamingMarkdownFilter();
|
|
198
|
+
this._buttonsLineSuppressed = false;
|
|
199
|
+
}
|
|
200
|
+
onPartialReply(payload) {
|
|
201
|
+
const fullText = payload.text ?? "";
|
|
202
|
+
if (this._buttonsLineSuppressed) {
|
|
203
|
+
this.prevPartialText = fullText;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const prevLen = this.prevPartialText.length;
|
|
207
|
+
const rawDelta = fullText.slice(prevLen);
|
|
208
|
+
this.prevPartialText = fullText;
|
|
209
|
+
if (!rawDelta)
|
|
210
|
+
return;
|
|
211
|
+
// [MEDIA-DEBUG] Detect MEDIA: directive appearance in raw stream so we can
|
|
212
|
+
// verify whether the host strips it before deliver. Logs once per stream.
|
|
213
|
+
if (this._mediaDebugSeenAt === undefined && /\bmedia:/i.test(fullText)) {
|
|
214
|
+
this._mediaDebugSeenAt = fullText.search(/\bmedia:/i);
|
|
215
|
+
const start = Math.max(0, this._mediaDebugSeenAt - 40);
|
|
216
|
+
const end = Math.min(fullText.length, this._mediaDebugSeenAt + 200);
|
|
217
|
+
this._mediaDebugFirstSnippet = fullText.slice(start, end).replaceAll("\n", "\\n");
|
|
218
|
+
this.log.info(`[media-debug] MEDIA: seen in raw stream at offset=${this._mediaDebugSeenAt} fullLen=${fullText.length} snippet="${this._mediaDebugFirstSnippet}"`);
|
|
219
|
+
}
|
|
220
|
+
// Detect WEIXIN_BUTTONS: marker anywhere in the accumulated text. We mirror
|
|
221
|
+
// the recognition rule used by parseInlineButtons (/^WEIXIN_BUTTONS:/m —
|
|
222
|
+
// start of any line, fence-aware), so whatever gets stripped at deliver-time
|
|
223
|
+
// also gets suppressed in the live stream, regardless of whether the marker
|
|
224
|
+
// is at the tail, followed by trailing newline, or followed by more content.
|
|
225
|
+
// Markers inside an (open or closed) markdown code fence are treated as
|
|
226
|
+
// literal documentation and never suppressed.
|
|
227
|
+
const fenceSpans = parseFenceSpans(fullText);
|
|
228
|
+
const re = new RegExp(MARKER_LINE_START_RE.source, MARKER_LINE_START_RE.flags);
|
|
229
|
+
let markerStart = -1;
|
|
230
|
+
let m;
|
|
231
|
+
while ((m = re.exec(fullText)) !== null) {
|
|
232
|
+
if (!findFenceSpanAt(fenceSpans, m.index)) {
|
|
233
|
+
markerStart = m.index;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (markerStart >= 0) {
|
|
238
|
+
this._buttonsLineSuppressed = true;
|
|
239
|
+
// Push the portion of the delta that lies BEFORE the marker start.
|
|
240
|
+
// Anything from markerStart onwards (marker itself + any future tokens)
|
|
241
|
+
// is dropped from the stream.
|
|
242
|
+
const deltaCutoff = Math.max(0, markerStart - prevLen);
|
|
243
|
+
const deltaBeforeMarker = rawDelta.slice(0, deltaCutoff);
|
|
244
|
+
if (deltaBeforeMarker) {
|
|
245
|
+
const filteredDelta = this.resultMdFilter.feed(deltaBeforeMarker);
|
|
246
|
+
if (filteredDelta) {
|
|
247
|
+
this.resultFilteredAccum += filteredDelta;
|
|
248
|
+
this.pipeline.push({ kind: "result", text: filteredDelta });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const filteredDelta = this.resultMdFilter.feed(rawDelta);
|
|
254
|
+
if (filteredDelta) {
|
|
255
|
+
this.resultFilteredAccum += filteredDelta;
|
|
256
|
+
this.pipeline.push({ kind: "result", text: filteredDelta });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=stream-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-session.js","sourceRoot":"","sources":["../../../src/streaming/stream-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { initStream, syncStream } from "../api/api.js";
|
|
3
|
+
import { AbortType } from "../api/types.js";
|
|
4
|
+
import { getOrCreateDeviceId } from "../auth/accounts.js";
|
|
5
|
+
import { logger } from "../util/logger.js";
|
|
6
|
+
const STREAM_BUSINESS_TYPE = 10;
|
|
7
|
+
/**
|
|
8
|
+
* Manages a single uplink stream session.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const sender = new WeixinStreamSender(opts);
|
|
12
|
+
* await sender.init();
|
|
13
|
+
* await sender.sendPiece({ type: "text", text: "Hello " });
|
|
14
|
+
* await sender.sendPiece({ type: "text", text: "world!" });
|
|
15
|
+
* await sender.end();
|
|
16
|
+
*/
|
|
17
|
+
export class WeixinStreamSender {
|
|
18
|
+
opts;
|
|
19
|
+
deviceId;
|
|
20
|
+
clientStreamId;
|
|
21
|
+
streamTicket;
|
|
22
|
+
pieceSeq = 0;
|
|
23
|
+
ended = false;
|
|
24
|
+
/**
|
|
25
|
+
* Pieces from a previous sendPiece/end call that failed to reach the server.
|
|
26
|
+
* They are prepended to the next syncStream request so the server receives
|
|
27
|
+
* them with the original piece_seq values (true retry, not accumulation).
|
|
28
|
+
*/
|
|
29
|
+
pendingPieces = [];
|
|
30
|
+
/** pieceSeq value before the pending pieces were assigned; used for rollback. */
|
|
31
|
+
seqBeforePending = 0;
|
|
32
|
+
constructor(opts) {
|
|
33
|
+
this.opts = opts;
|
|
34
|
+
this.deviceId = getOrCreateDeviceId(opts.accountId);
|
|
35
|
+
this.clientStreamId = `${opts.accountId}:${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
|
|
36
|
+
}
|
|
37
|
+
/** Call native_init_stream to obtain a stream_ticket. Must be called before sendPiece. */
|
|
38
|
+
async init() {
|
|
39
|
+
let resp;
|
|
40
|
+
try {
|
|
41
|
+
resp = await initStream({
|
|
42
|
+
baseUrl: this.opts.baseUrl,
|
|
43
|
+
token: this.opts.token,
|
|
44
|
+
timeoutMs: this.opts.timeoutMs,
|
|
45
|
+
accountId: this.opts.accountId,
|
|
46
|
+
body: {
|
|
47
|
+
device_id: this.deviceId,
|
|
48
|
+
client_stream_id: this.clientStreamId,
|
|
49
|
+
business_type: STREAM_BUSINESS_TYPE,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
logger.warn(`WeixinStreamSender.init: initStream threw streamId=${this.clientStreamId} err=${String(err)}`);
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
if (resp.base_response?.ret && resp.base_response.ret !== 0) {
|
|
58
|
+
logger.warn(`WeixinStreamSender.init: ret=${resp.base_response.ret} errmsg=${resp.base_response.errmsg ?? ""} streamId=${this.clientStreamId}`);
|
|
59
|
+
throw new Error(`initStream failed: ret=${resp.base_response.ret} errmsg=${resp.base_response.errmsg ?? ""}`);
|
|
60
|
+
}
|
|
61
|
+
if (!resp.stream_ticket) {
|
|
62
|
+
logger.warn(`WeixinStreamSender.init: no stream_ticket in response streamId=${this.clientStreamId}`);
|
|
63
|
+
throw new Error("initStream: no stream_ticket in response");
|
|
64
|
+
}
|
|
65
|
+
this.streamTicket = resp.stream_ticket;
|
|
66
|
+
logger.debug(`WeixinStreamSender.init: streamId=${this.clientStreamId}`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Send a single piece of data on the stream.
|
|
70
|
+
* piece_seq auto-increments starting from 1.
|
|
71
|
+
*
|
|
72
|
+
* If a previous call failed, the unsent pieces are automatically prepended
|
|
73
|
+
* to this request so they are retried with their original piece_seq values.
|
|
74
|
+
*/
|
|
75
|
+
async sendPiece(data) {
|
|
76
|
+
this.assertReady();
|
|
77
|
+
// Remember the seq checkpoint *before* we assign a new seq, so we can
|
|
78
|
+
// roll back on failure.
|
|
79
|
+
const seqBefore = this.pendingPieces.length > 0
|
|
80
|
+
? this.seqBeforePending
|
|
81
|
+
: this.pieceSeq;
|
|
82
|
+
this.pieceSeq += 1;
|
|
83
|
+
const newPiece = {
|
|
84
|
+
piece_seq: this.pieceSeq,
|
|
85
|
+
piece_data: Buffer.from(JSON.stringify(data), "utf-8").toString("base64"),
|
|
86
|
+
};
|
|
87
|
+
// Merge any previously-failed pieces with the new one.
|
|
88
|
+
const pieces = [...this.pendingPieces, newPiece];
|
|
89
|
+
try {
|
|
90
|
+
const resp = await syncStream({
|
|
91
|
+
baseUrl: this.opts.baseUrl,
|
|
92
|
+
token: this.opts.token,
|
|
93
|
+
timeoutMs: this.opts.timeoutMs,
|
|
94
|
+
accountId: this.opts.accountId,
|
|
95
|
+
body: {
|
|
96
|
+
device_id: this.deviceId,
|
|
97
|
+
client_stream_id: this.clientStreamId,
|
|
98
|
+
business_type: STREAM_BUSINESS_TYPE,
|
|
99
|
+
up_piece_list: pieces,
|
|
100
|
+
end_up_piece_seq: 0,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
this.checkAbort(resp);
|
|
104
|
+
// Success — clear pending state.
|
|
105
|
+
const retried = pieces.length - 1;
|
|
106
|
+
this.pendingPieces = [];
|
|
107
|
+
this.seqBeforePending = 0;
|
|
108
|
+
if (retried > 0) {
|
|
109
|
+
logger.info(`WeixinStreamSender.sendPiece: seq=${this.pieceSeq} type=${data.type} batch=${pieces.length} retried=${retried} streamId=${this.clientStreamId}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
logger.debug(`WeixinStreamSender.sendPiece: seq=${this.pieceSeq} type=${data.type} batch=${pieces.length}`);
|
|
113
|
+
}
|
|
114
|
+
return resp;
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
// Roll back: save all pieces for retry and restore pieceSeq.
|
|
118
|
+
this.pendingPieces = pieces;
|
|
119
|
+
this.seqBeforePending = seqBefore;
|
|
120
|
+
this.pieceSeq = seqBefore;
|
|
121
|
+
const seqs = pieces.map((p) => p.piece_seq).join(",");
|
|
122
|
+
logger.warn(`WeixinStreamSender.sendPiece: failed — kept pendingSeqs=[${seqs}] rolledBackTo=${seqBefore} type=${data.type} streamId=${this.clientStreamId} err=${String(err)}`);
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Signal that the uplink stream has ended.
|
|
128
|
+
* Optionally sends a final piece in the same request as the end marker.
|
|
129
|
+
* Any pending (previously-failed) pieces are included in the same request.
|
|
130
|
+
*/
|
|
131
|
+
async end(finalData) {
|
|
132
|
+
this.assertReady();
|
|
133
|
+
this.pieceSeq += 1;
|
|
134
|
+
const finalPiece = {
|
|
135
|
+
piece_seq: this.pieceSeq,
|
|
136
|
+
piece_data: Buffer.from(JSON.stringify(finalData ?? { type: "text", text: "" }), "utf-8").toString("base64"),
|
|
137
|
+
};
|
|
138
|
+
const pieces = [...this.pendingPieces, finalPiece];
|
|
139
|
+
const pendingCarried = this.pendingPieces.length;
|
|
140
|
+
this.ended = true;
|
|
141
|
+
let resp;
|
|
142
|
+
try {
|
|
143
|
+
resp = await syncStream({
|
|
144
|
+
baseUrl: this.opts.baseUrl,
|
|
145
|
+
token: this.opts.token,
|
|
146
|
+
timeoutMs: this.opts.timeoutMs,
|
|
147
|
+
accountId: this.opts.accountId,
|
|
148
|
+
body: {
|
|
149
|
+
device_id: this.deviceId,
|
|
150
|
+
client_stream_id: this.clientStreamId,
|
|
151
|
+
business_type: STREAM_BUSINESS_TYPE,
|
|
152
|
+
up_piece_list: pieces,
|
|
153
|
+
end_up_piece_seq: this.pieceSeq,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const seqs = pieces.map((p) => p.piece_seq).join(",");
|
|
159
|
+
logger.warn(`WeixinStreamSender.end: failed — endSeq=${this.pieceSeq} batch=${pieces.length} carriedPending=${pendingCarried} seqs=[${seqs}] streamId=${this.clientStreamId} err=${String(err)}`);
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
this.pendingPieces = [];
|
|
163
|
+
this.seqBeforePending = 0;
|
|
164
|
+
if (pendingCarried > 0) {
|
|
165
|
+
logger.info(`WeixinStreamSender.end: endSeq=${this.pieceSeq} batch=${pieces.length} retried=${pendingCarried} streamId=${this.clientStreamId}`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
logger.debug(`WeixinStreamSender.end: endSeq=${this.pieceSeq} batch=${pieces.length}`);
|
|
169
|
+
}
|
|
170
|
+
return resp;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Send a client-side abort signal.
|
|
174
|
+
*/
|
|
175
|
+
async abort(errorMsg) {
|
|
176
|
+
this.assertReady();
|
|
177
|
+
this.ended = true;
|
|
178
|
+
let resp;
|
|
179
|
+
try {
|
|
180
|
+
resp = await syncStream({
|
|
181
|
+
baseUrl: this.opts.baseUrl,
|
|
182
|
+
token: this.opts.token,
|
|
183
|
+
timeoutMs: this.opts.timeoutMs,
|
|
184
|
+
accountId: this.opts.accountId,
|
|
185
|
+
body: {
|
|
186
|
+
device_id: this.deviceId,
|
|
187
|
+
client_stream_id: this.clientStreamId,
|
|
188
|
+
business_type: STREAM_BUSINESS_TYPE,
|
|
189
|
+
up_piece_list: [],
|
|
190
|
+
end_up_piece_seq: this.pieceSeq || 1,
|
|
191
|
+
abort_info: {
|
|
192
|
+
abort_type: AbortType.CLIENT_ABORT,
|
|
193
|
+
abort_detail_error_code: 0,
|
|
194
|
+
abort_detail_error_msg: errorMsg ?? "client abort",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
logger.warn(`WeixinStreamSender.abort: failed streamId=${this.clientStreamId} reason=${errorMsg ?? "client abort"} err=${String(err)}`);
|
|
201
|
+
throw err;
|
|
202
|
+
}
|
|
203
|
+
logger.debug(`WeixinStreamSender.abort: streamId=${this.clientStreamId} reason=${errorMsg ?? "client abort"}`);
|
|
204
|
+
return resp;
|
|
205
|
+
}
|
|
206
|
+
get currentPieceSeq() {
|
|
207
|
+
return this.pieceSeq;
|
|
208
|
+
}
|
|
209
|
+
get streamId() {
|
|
210
|
+
return this.clientStreamId;
|
|
211
|
+
}
|
|
212
|
+
get ticket() {
|
|
213
|
+
return this.streamTicket;
|
|
214
|
+
}
|
|
215
|
+
get isEnded() {
|
|
216
|
+
return this.ended;
|
|
217
|
+
}
|
|
218
|
+
assertReady() {
|
|
219
|
+
if (!this.streamTicket) {
|
|
220
|
+
throw new Error("WeixinStreamSender: not initialized — call init() first");
|
|
221
|
+
}
|
|
222
|
+
if (this.ended) {
|
|
223
|
+
throw new Error("WeixinStreamSender: stream already ended");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
checkAbort(resp) {
|
|
227
|
+
if (resp.abort_info?.abort_type) {
|
|
228
|
+
const info = resp.abort_info;
|
|
229
|
+
logger.warn(`WeixinStreamSender: server abort type=${info.abort_type} code=${info.abort_detail_error_code} msg=${info.abort_detail_error_msg}`);
|
|
230
|
+
this.ended = true;
|
|
231
|
+
throw new Error(`Stream aborted: type=${info.abort_type} code=${info.abort_detail_error_code} msg=${info.abort_detail_error_msg ?? ""}`);
|
|
232
|
+
}
|
|
233
|
+
if (resp.base_response?.ret && resp.base_response.ret !== 0) {
|
|
234
|
+
logger.warn(`WeixinStreamSender: syncStream error ret=${resp.base_response.ret} errmsg=${resp.base_response.errmsg}`);
|
|
235
|
+
throw new Error(`syncStream failed: ret=${resp.base_response.ret} errmsg=${resp.base_response.errmsg ?? ""}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../src/streaming/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/infra-runtime";
|
|
5
|
+
/**
|
|
6
|
+
* Plugin logger — writes JSON lines to the main openclaw log file:
|
|
7
|
+
* <tmpDir>/openclaw-YYYY-MM-DD.log
|
|
8
|
+
* Same file and format used by all other channels.
|
|
9
|
+
*/
|
|
10
|
+
const MAIN_LOG_DIR = resolvePreferredOpenClawTmpDir();
|
|
11
|
+
const SUBSYSTEM = "gateway/channels/openclaw-weixin";
|
|
12
|
+
const RUNTIME = "node";
|
|
13
|
+
const RUNTIME_VERSION = process.versions.node;
|
|
14
|
+
const HOSTNAME = os.hostname() || "unknown";
|
|
15
|
+
const PARENT_NAMES = ["openclaw"];
|
|
16
|
+
/** tslog-compatible level IDs (higher = more severe). */
|
|
17
|
+
const LEVEL_IDS = {
|
|
18
|
+
TRACE: 1,
|
|
19
|
+
DEBUG: 2,
|
|
20
|
+
INFO: 3,
|
|
21
|
+
WARN: 4,
|
|
22
|
+
ERROR: 5,
|
|
23
|
+
FATAL: 6,
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_LOG_LEVEL = "INFO";
|
|
26
|
+
function resolveMinLevel() {
|
|
27
|
+
const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase();
|
|
28
|
+
if (env && env in LEVEL_IDS)
|
|
29
|
+
return LEVEL_IDS[env];
|
|
30
|
+
return LEVEL_IDS[DEFAULT_LOG_LEVEL];
|
|
31
|
+
}
|
|
32
|
+
let minLevelId = resolveMinLevel();
|
|
33
|
+
/** Dynamically change the minimum log level at runtime. */
|
|
34
|
+
export function setLogLevel(level) {
|
|
35
|
+
const upper = level.toUpperCase();
|
|
36
|
+
if (!(upper in LEVEL_IDS)) {
|
|
37
|
+
throw new Error(`Invalid log level: ${level}. Valid levels: ${Object.keys(LEVEL_IDS).join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
minLevelId = LEVEL_IDS[upper];
|
|
40
|
+
}
|
|
41
|
+
/** Shift a Date into local time so toISOString() renders local clock digits. */
|
|
42
|
+
function toLocalISO(now) {
|
|
43
|
+
const offsetMs = -now.getTimezoneOffset() * 60_000;
|
|
44
|
+
const sign = offsetMs >= 0 ? "+" : "-";
|
|
45
|
+
const abs = Math.abs(now.getTimezoneOffset());
|
|
46
|
+
const offStr = `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
|
|
47
|
+
return new Date(now.getTime() + offsetMs).toISOString().replace("Z", offStr);
|
|
48
|
+
}
|
|
49
|
+
function localDateKey(now) {
|
|
50
|
+
return toLocalISO(now).slice(0, 10);
|
|
51
|
+
}
|
|
52
|
+
function resolveMainLogPath() {
|
|
53
|
+
const dateKey = localDateKey(new Date());
|
|
54
|
+
return path.join(MAIN_LOG_DIR, `openclaw-${dateKey}.log`);
|
|
55
|
+
}
|
|
56
|
+
let logDirEnsured = false;
|
|
57
|
+
function buildLoggerName(accountId) {
|
|
58
|
+
return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
|
|
59
|
+
}
|
|
60
|
+
function writeLog(level, message, accountId) {
|
|
61
|
+
const levelId = LEVEL_IDS[level] ?? LEVEL_IDS.INFO;
|
|
62
|
+
if (levelId < minLevelId)
|
|
63
|
+
return;
|
|
64
|
+
const now = new Date();
|
|
65
|
+
const loggerName = buildLoggerName(accountId);
|
|
66
|
+
const prefixedMessage = accountId ? `[${accountId}] ${message}` : message;
|
|
67
|
+
const entry = JSON.stringify({
|
|
68
|
+
"0": loggerName,
|
|
69
|
+
"1": prefixedMessage,
|
|
70
|
+
_meta: {
|
|
71
|
+
runtime: RUNTIME,
|
|
72
|
+
runtimeVersion: RUNTIME_VERSION,
|
|
73
|
+
hostname: HOSTNAME,
|
|
74
|
+
name: loggerName,
|
|
75
|
+
parentNames: PARENT_NAMES,
|
|
76
|
+
date: now.toISOString(),
|
|
77
|
+
logLevelId: LEVEL_IDS[level] ?? LEVEL_IDS.INFO,
|
|
78
|
+
logLevelName: level,
|
|
79
|
+
},
|
|
80
|
+
time: toLocalISO(now),
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
if (!logDirEnsured) {
|
|
84
|
+
fs.mkdirSync(MAIN_LOG_DIR, { recursive: true });
|
|
85
|
+
logDirEnsured = true;
|
|
86
|
+
}
|
|
87
|
+
fs.appendFileSync(resolveMainLogPath(), `${entry}\n`, "utf-8");
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Best-effort; never block on logging failures.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Creates a logger instance, optionally bound to a specific account. */
|
|
94
|
+
function createLogger(accountId) {
|
|
95
|
+
return {
|
|
96
|
+
info(message) {
|
|
97
|
+
writeLog("INFO", message, accountId);
|
|
98
|
+
},
|
|
99
|
+
debug(message) {
|
|
100
|
+
writeLog("DEBUG", message, accountId);
|
|
101
|
+
},
|
|
102
|
+
warn(message) {
|
|
103
|
+
writeLog("WARN", message, accountId);
|
|
104
|
+
},
|
|
105
|
+
error(message) {
|
|
106
|
+
writeLog("ERROR", message, accountId);
|
|
107
|
+
},
|
|
108
|
+
withAccount(id) {
|
|
109
|
+
return createLogger(id);
|
|
110
|
+
},
|
|
111
|
+
getLogFilePath() {
|
|
112
|
+
return resolveMainLogPath();
|
|
113
|
+
},
|
|
114
|
+
close() {
|
|
115
|
+
// No-op: appendFileSync has no persistent handle to close.
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export const logger = createLogger();
|
|
120
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/util/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAC;AAEnF;;;;GAIG;AAEH,MAAM,YAAY,GAAG,8BAA8B,EAAE,CAAC;AACtD,MAAM,SAAS,GAAG,kCAAkC,CAAC;AACrD,MAAM,OAAO,GAAG,MAAM,CAAC;AACvB,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC9C,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,IAAI,SAAS,CAAC;AAC5C,MAAM,YAAY,GAAG,CAAC,UAAU,CAAC,CAAC;AAElC,yDAAyD;AACzD,MAAM,SAAS,GAA2B;IACxC,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC,SAAS,eAAe;IACtB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAC1D,IAAI,GAAG,IAAI,GAAG,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;IACnD,OAAO,SAAS,CAAC,iBAAiB,CAAC,CAAC;AACtC,CAAC;AAED,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;AAEnC,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAClC,IAAI,CAAC,CAAC,KAAK,IAAI,SAAS,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,mBAAmB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,gFAAgF;AAChF,SAAS,UAAU,CAAC,GAAS;IAC3B,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,GAAG,MAAM,CAAC;IACnD,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC9G,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,YAAY,CAAC,GAAS;IAC7B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,kBAAkB;IACzB,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,YAAY,OAAO,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED,IAAI,aAAa,GAAG,KAAK,CAAC;AAc1B,SAAS,eAAe,CAAC,SAAkB;IACzC,OAAO,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,OAAe,EAAE,SAAkB;IAClE,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC;IACnD,IAAI,OAAO,GAAG,UAAU;QAAE,OAAO;IAEjC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,GAAG,EAAE,UAAU;QACf,GAAG,EAAE,eAAe;QACpB,KAAK,EAAE;YACL,OAAO,EAAE,OAAO;YAChB,cAAc,EAAE,eAAe;YAC/B,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,YAAY;YACzB,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE;YACvB,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI;YAC9C,YAAY,EAAE,KAAK;SACpB;QACD,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC;KACtB,CAAC,CAAC;IACH,IAAI,CAAC;QACH,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,EAAE,CAAC,cAAc,CAAC,kBAAkB,EAAE,EAAE,GAAG,KAAK,IAAI,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,SAAS,YAAY,CAAC,SAAkB;IACtC,OAAO;QACL,IAAI,CAAC,OAAe;YAClB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QACvC,CAAC;QACD,KAAK,CAAC,OAAe;YACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,OAAe;YAClB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QACvC,CAAC;QACD,KAAK,CAAC,OAAe;YACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QACD,WAAW,CAAC,EAAU;YACpB,OAAO,YAAY,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;QACD,cAAc;YACZ,OAAO,kBAAkB,EAAE,CAAC;QAC9B,CAAC;QACD,KAAK;YACH,2DAA2D;QAC7D,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAW,YAAY,EAAE,CAAC"}
|