@tencent-weixin/openclaw-weixin 2.4.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.
Files changed (41) hide show
  1. package/dist/index.js +0 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/api/api.js +1 -2
  4. package/dist/src/api/api.js.map +1 -1
  5. package/dist/src/auth/accounts.js.map +1 -1
  6. package/dist/src/channel.js +11 -0
  7. package/dist/src/channel.js.map +1 -1
  8. package/dist/src/media/voice-outbound.js +177 -0
  9. package/dist/src/media/voice-outbound.js.map +1 -0
  10. package/dist/src/messaging/abort-fence.js +70 -0
  11. package/dist/src/messaging/abort-fence.js.map +1 -0
  12. package/dist/src/messaging/buttons.js +117 -0
  13. package/dist/src/messaging/buttons.js.map +1 -0
  14. package/dist/src/messaging/lane-key.js +66 -0
  15. package/dist/src/messaging/lane-key.js.map +1 -0
  16. package/dist/src/messaging/merged-record.js +149 -0
  17. package/dist/src/messaging/merged-record.js.map +1 -0
  18. package/dist/src/messaging/model-buttons.js +182 -0
  19. package/dist/src/messaging/model-buttons.js.map +1 -0
  20. package/dist/src/messaging/model-callback-handler.js +133 -0
  21. package/dist/src/messaging/model-callback-handler.js.map +1 -0
  22. package/dist/src/monitor/lane-scheduler.js +46 -0
  23. package/dist/src/monitor/lane-scheduler.js.map +1 -0
  24. package/dist/src/monitor/monitor.js +5 -12
  25. package/dist/src/monitor/monitor.js.map +1 -1
  26. package/dist/src/streaming/stream-pipeline.js +431 -0
  27. package/dist/src/streaming/stream-pipeline.js.map +1 -0
  28. package/dist/src/streaming/stream-session.js +260 -0
  29. package/dist/src/streaming/stream-session.js.map +1 -0
  30. package/dist/src/streaming/stream.js +239 -0
  31. package/dist/src/streaming/stream.js.map +1 -0
  32. package/dist/src/util/markdown-fences.js +54 -0
  33. package/dist/src/util/markdown-fences.js.map +1 -0
  34. package/index.ts +0 -5
  35. package/openclaw.plugin.json +1 -1
  36. package/package.json +1 -1
  37. package/src/api/api.ts +2 -3
  38. package/src/auth/accounts.ts +0 -1
  39. package/src/channel.ts +13 -1
  40. package/src/monitor/monitor.ts +11 -10
  41. 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,54 @@
1
+ export function parseFenceSpans(buffer) {
2
+ const spans = [];
3
+ let open;
4
+ let offset = 0;
5
+ while (offset <= buffer.length) {
6
+ const nextNewline = buffer.indexOf("\n", offset);
7
+ const lineEnd = nextNewline === -1 ? buffer.length : nextNewline;
8
+ const line = buffer.slice(offset, lineEnd);
9
+ const match = /^( {0,3})(`{3,}|~{3,})(.*)$/.exec(line);
10
+ if (match) {
11
+ const marker = match[2];
12
+ const markerChar = marker[0];
13
+ const markerLen = marker.length;
14
+ if (!open) {
15
+ open = { start: offset, markerChar, markerLen };
16
+ }
17
+ else if (open.markerChar === markerChar && markerLen >= open.markerLen) {
18
+ spans.push({ start: open.start, end: lineEnd });
19
+ open = undefined;
20
+ }
21
+ }
22
+ if (nextNewline === -1)
23
+ break;
24
+ offset = nextNewline + 1;
25
+ }
26
+ if (open) {
27
+ spans.push({ start: open.start, end: buffer.length });
28
+ }
29
+ return spans;
30
+ }
31
+ /**
32
+ * Return the fence span containing `index`, or undefined if `index` is not
33
+ * inside any fence. Spans are assumed to be sorted by `start` (which is the
34
+ * natural output of parseFenceSpans).
35
+ */
36
+ export function findFenceSpanAt(spans, index) {
37
+ let low = 0;
38
+ let high = spans.length - 1;
39
+ while (low <= high) {
40
+ const mid = (low + high) >>> 1;
41
+ const span = spans[mid];
42
+ if (index < span.start) {
43
+ high = mid - 1;
44
+ continue;
45
+ }
46
+ if (index >= span.end) {
47
+ low = mid + 1;
48
+ continue;
49
+ }
50
+ return span;
51
+ }
52
+ return undefined;
53
+ }
54
+ //# sourceMappingURL=markdown-fences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-fences.js","sourceRoot":"","sources":["../../../src/util/markdown-fences.ts"],"names":[],"mappings":"AAoBA,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,IAAI,IAA0E,CAAC;IAC/E,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;YAChC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;YAClD,CAAC;iBAAM,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,IAAI,SAAS,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACzE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;gBAChD,IAAI,GAAG,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,WAAW,KAAK,CAAC,CAAC;YAAE,MAAM;QAC9B,MAAM,GAAG,WAAW,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,IAAI,EAAE,CAAC;QACT,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAA2B,EAAE,KAAa;IACxE,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5B,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAE,CAAC;QACzB,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACvB,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACtB,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;YACd,SAAS;QACX,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/index.ts CHANGED
@@ -4,7 +4,6 @@ import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-sch
4
4
  import { weixinPlugin } from "./src/channel.js";
5
5
  import { assertHostCompatibility } from "./src/compat.js";
6
6
  import { WeixinConfigSchema } from "./src/config/config-schema.js";
7
- import { setWeixinRuntime } from "./src/runtime.js";
8
7
 
9
8
  export default {
10
9
  id: "openclaw-weixin",
@@ -15,10 +14,6 @@ export default {
15
14
  // Fail-fast: reject incompatible host versions before any side-effects.
16
15
  assertHostCompatibility(api.runtime?.version);
17
16
 
18
- if (api.runtime) {
19
- setWeixinRuntime(api.runtime);
20
- }
21
-
22
17
  api.registerChannel({ plugin: weixinPlugin });
23
18
  },
24
19
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "openclaw-weixin",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "channels": [
5
5
  "openclaw-weixin"
6
6
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-weixin/openclaw-weixin",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "description": "OpenClaw Weixin channel",
5
5
  "license": "MIT",
6
6
  "author": "Tencent",
package/src/api/api.ts CHANGED
@@ -201,11 +201,10 @@ function buildCommonHeaders(): Record<string, string> {
201
201
  return headers;
202
202
  }
203
203
 
204
- function buildHeaders(opts: { token?: string; body: string }): Record<string, string> {
204
+ function buildHeaders(opts: { token?: string }): Record<string, string> {
205
205
  const headers: Record<string, string> = {
206
206
  "Content-Type": "application/json",
207
207
  AuthorizationType: "ilink_bot_token",
208
- "Content-Length": String(Buffer.byteLength(opts.body, "utf-8")),
209
208
  "X-WECHAT-UIN": randomWechatUin(),
210
209
  ...buildCommonHeaders(),
211
210
  };
@@ -277,7 +276,7 @@ export async function apiPostFetch(params: {
277
276
  }): Promise<string> {
278
277
  const base = ensureTrailingSlash(params.baseUrl);
279
278
  const url = new URL(params.endpoint, base);
280
- const hdrs = buildHeaders({ token: params.token, body: params.body });
279
+ const hdrs = buildHeaders({ token: params.token });
281
280
  logger.debug(`POST ${redactUrl(url.toString())} body=${redactBody(params.body)}`);
282
281
 
283
282
  const controller =
@@ -4,7 +4,6 @@ import path from "node:path";
4
4
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
5
5
  import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
6
6
 
7
- import { getWeixinRuntime } from "../runtime.js";
8
7
  import { resolveStateDir } from "../storage/state-dir.js";
9
8
  import { resolveFrameworkAllowFromPath } from "./pairing.js";
10
9
  import { logger } from "../util/logger.js";