@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,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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Generate a prefixed unique ID using timestamp + crypto random bytes.
|
|
4
|
+
* Format: `{prefix}:{timestamp}-{8-char hex}`
|
|
5
|
+
*/
|
|
6
|
+
export function generateId(prefix) {
|
|
7
|
+
return `${prefix}:${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Generate a temporary file name with random suffix.
|
|
11
|
+
* Format: `{prefix}-{timestamp}-{8-char hex}{ext}`
|
|
12
|
+
*/
|
|
13
|
+
export function tempFileName(prefix, ext) {
|
|
14
|
+
return `${prefix}-${Date.now()}-${crypto.randomBytes(4).toString("hex")}${ext}`;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=random.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"random.js","sourceRoot":"","sources":["../../../src/util/random.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,GAAW;IACtD,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const DEFAULT_BODY_MAX_LEN = 200;
|
|
2
|
+
const DEFAULT_TOKEN_PREFIX_LEN = 6;
|
|
3
|
+
/**
|
|
4
|
+
* Truncate a string, appending a length indicator when trimmed.
|
|
5
|
+
* Returns `""` for empty/undefined input.
|
|
6
|
+
*/
|
|
7
|
+
export function truncate(s, max) {
|
|
8
|
+
if (!s)
|
|
9
|
+
return "";
|
|
10
|
+
if (s.length <= max)
|
|
11
|
+
return s;
|
|
12
|
+
return `${s.slice(0, max)}…(len=${s.length})`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Redact a token/secret: show only the first few chars + total length.
|
|
16
|
+
* Returns `"(none)"` when absent.
|
|
17
|
+
*/
|
|
18
|
+
export function redactToken(token, prefixLen = DEFAULT_TOKEN_PREFIX_LEN) {
|
|
19
|
+
if (!token)
|
|
20
|
+
return "(none)";
|
|
21
|
+
if (token.length <= prefixLen)
|
|
22
|
+
return `****(len=${token.length})`;
|
|
23
|
+
return `${token.slice(0, prefixLen)}…(len=${token.length})`;
|
|
24
|
+
}
|
|
25
|
+
/** Field names whose values should be masked in logged JSON bodies. */
|
|
26
|
+
const SENSITIVE_FIELDS = /\b(context_token|bot_token|token|authorization|Authorization)\b/;
|
|
27
|
+
/**
|
|
28
|
+
* Truncate a JSON body string to `maxLen` chars for safe logging.
|
|
29
|
+
* Redacts known sensitive fields before truncating.
|
|
30
|
+
*/
|
|
31
|
+
export function redactBody(body, maxLen = DEFAULT_BODY_MAX_LEN) {
|
|
32
|
+
if (!body)
|
|
33
|
+
return "(empty)";
|
|
34
|
+
// Mask values of known sensitive JSON keys: "key":"value" → "key":"<redacted>"
|
|
35
|
+
const redacted = body.replace(/"(context_token|bot_token|token|authorization|Authorization)"\s*:\s*"[^"]*"/g, '"$1":"<redacted>"');
|
|
36
|
+
if (redacted.length <= maxLen)
|
|
37
|
+
return redacted;
|
|
38
|
+
return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Strip query string (which often contains signatures/tokens) from a URL,
|
|
42
|
+
* keeping only origin + pathname.
|
|
43
|
+
*/
|
|
44
|
+
export function redactUrl(rawUrl) {
|
|
45
|
+
try {
|
|
46
|
+
const u = new URL(rawUrl);
|
|
47
|
+
const base = `${u.origin}${u.pathname}`;
|
|
48
|
+
return u.search ? `${base}?<redacted>` : base;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return truncate(rawUrl, 80);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/util/redact.ts"],"names":[],"mappings":"AAAA,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAEnC;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,CAAqB,EAAE,GAAW;IACzD,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAyB,EAAE,SAAS,GAAG,wBAAwB;IACzF,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,YAAY,KAAK,CAAC,MAAM,GAAG,CAAC;IAClE,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,SAAS,KAAK,CAAC,MAAM,GAAG,CAAC;AAC9D,CAAC;AAED,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,iEAAiE,CAAC;AAE3F;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAwB,EAAE,MAAM,GAAG,oBAAoB;IAChF,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,+EAA+E;IAC/E,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAC3B,8EAA8E,EAC9E,mBAAmB,CACpB,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC/C,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,yBAAyB,QAAQ,CAAC,MAAM,GAAG,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;QACxC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9B,CAAC;AACH,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
|
};
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-weixin",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"channels": [
|
|
5
5
|
"openclaw-weixin"
|
|
6
6
|
],
|
|
7
|
+
"channelConfigs": {
|
|
8
|
+
"openclaw-weixin": {
|
|
9
|
+
"schema": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"additionalProperties": true
|
|
12
|
+
},
|
|
13
|
+
"label": "openclaw-weixin",
|
|
14
|
+
"description": "Weixin channel configuration."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
7
17
|
"configSchema": {
|
|
8
18
|
"type": "object",
|
|
9
19
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tencent-weixin/openclaw-weixin",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "OpenClaw Weixin channel",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tencent",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"!src/**/node_modules/",
|
|
12
12
|
"index.ts",
|
|
13
13
|
"openclaw.plugin.json",
|
|
14
|
+
"dist/",
|
|
14
15
|
"README.md",
|
|
15
16
|
"README.zh_CN.md",
|
|
16
17
|
"CHANGELOG.md",
|
|
@@ -27,7 +28,10 @@
|
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"qrcode-terminal": "0.12.0",
|
|
30
|
-
"zod": "4.3.6"
|
|
31
|
+
"zod": "^4.3.6"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"openclaw": ">=2026.3.22"
|
|
31
35
|
},
|
|
32
36
|
"devDependencies": {
|
|
33
37
|
"@vitest/coverage-v8": "^3.1.0",
|
|
@@ -40,6 +44,9 @@
|
|
|
40
44
|
"extensions": [
|
|
41
45
|
"./index.ts"
|
|
42
46
|
],
|
|
47
|
+
"runtimeExtensions": [
|
|
48
|
+
"./dist/index.js"
|
|
49
|
+
],
|
|
43
50
|
"channel": {
|
|
44
51
|
"id": "openclaw-weixin",
|
|
45
52
|
"label": "openclaw-weixin",
|
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
|
|
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
|
|
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 =
|
package/src/auth/accounts.ts
CHANGED
|
@@ -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";
|
package/src/channel.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
3
|
+
import type { ChannelPlugin, OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk/core";
|
|
4
4
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
5
5
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/infra-runtime";
|
|
6
6
|
|
|
@@ -428,6 +428,17 @@ export const weixinPlugin: ChannelPlugin<ResolvedWeixinAccount> = {
|
|
|
428
428
|
const logPath = aLog.getLogFilePath();
|
|
429
429
|
ctx.log?.info?.(`[${account.accountId}] weixin logs: ${logPath}`);
|
|
430
430
|
|
|
431
|
+
// The gateway injects the channel runtime surface per-call (task-scoped). We require it:
|
|
432
|
+
// it carries reply/routing/session/media/commands helpers used by processOneMessage.
|
|
433
|
+
// Available on hosts >= 2026.2.19 (our peerDependency is >= 2026.3.22).
|
|
434
|
+
if (!ctx.channelRuntime) {
|
|
435
|
+
const msg = `ctx.channelRuntime missing — host too old or plugin SDK contract violated`;
|
|
436
|
+
aLog.error(msg);
|
|
437
|
+
ctx.log?.error?.(`[${account.accountId}] ${msg}`);
|
|
438
|
+
ctx.setStatus?.({ accountId: account.accountId, running: false });
|
|
439
|
+
throw new Error(msg);
|
|
440
|
+
}
|
|
441
|
+
|
|
431
442
|
const { monitorWeixinProvider } = await import("./monitor/monitor.js");
|
|
432
443
|
return monitorWeixinProvider({
|
|
433
444
|
baseUrl: account.baseUrl,
|
|
@@ -436,6 +447,7 @@ export const weixinPlugin: ChannelPlugin<ResolvedWeixinAccount> = {
|
|
|
436
447
|
accountId: account.accountId,
|
|
437
448
|
config: ctx.cfg,
|
|
438
449
|
runtime: ctx.runtime,
|
|
450
|
+
channelRuntime: ctx.channelRuntime as unknown as PluginRuntime["channel"],
|
|
439
451
|
abortSignal: ctx.abortSignal,
|
|
440
452
|
setStatus: ctx.setStatus,
|
|
441
453
|
});
|
package/src/monitor/monitor.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { getUpdates } from "../api/api.js";
|
|
|
5
5
|
import { WeixinConfigManager } from "../api/config-cache.js";
|
|
6
6
|
import { SESSION_EXPIRED_ERRCODE, pauseSession, getRemainingPauseMs } from "../api/session-guard.js";
|
|
7
7
|
import { processOneMessage } from "../messaging/process-message.js";
|
|
8
|
-
import { getWeixinRuntime, waitForWeixinRuntime } from "../runtime.js";
|
|
9
8
|
import { getSyncBufFilePath, loadGetUpdatesBuf, saveGetUpdatesBuf } from "../storage/sync-buf.js";
|
|
10
9
|
import { logger } from "../util/logger.js";
|
|
11
10
|
import type { Logger } from "../util/logger.js";
|
|
@@ -25,6 +24,11 @@ export type MonitorWeixinOpts = {
|
|
|
25
24
|
allowFrom?: string[];
|
|
26
25
|
config: import("openclaw/plugin-sdk/core").OpenClawConfig;
|
|
27
26
|
runtime?: { log?: (msg: string) => void; error?: (msg: string) => void };
|
|
27
|
+
/**
|
|
28
|
+
* Gateway-injected channel runtime surface (reply/routing/session/media/commands/...).
|
|
29
|
+
* Required for inbound message processing; provided by `ChannelGatewayContext.channelRuntime`.
|
|
30
|
+
*/
|
|
31
|
+
channelRuntime: PluginRuntime["channel"];
|
|
28
32
|
abortSignal?: AbortSignal;
|
|
29
33
|
longPollTimeoutMs?: number;
|
|
30
34
|
/** Gateway status callback — called on each successful poll and inbound message. */
|
|
@@ -42,6 +46,7 @@ export async function monitorWeixinProvider(opts: MonitorWeixinOpts): Promise<vo
|
|
|
42
46
|
token,
|
|
43
47
|
accountId,
|
|
44
48
|
config,
|
|
49
|
+
channelRuntime,
|
|
45
50
|
abortSignal,
|
|
46
51
|
longPollTimeoutMs,
|
|
47
52
|
setStatus,
|
|
@@ -50,15 +55,11 @@ export async function monitorWeixinProvider(opts: MonitorWeixinOpts): Promise<vo
|
|
|
50
55
|
const errLog = opts.runtime?.error ?? ((m: string) => log(m));
|
|
51
56
|
const aLog: Logger = logger.withAccount(accountId);
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
aLog.info(`Weixin runtime acquired, channelRuntime type: ${typeof channelRuntime}`);
|
|
59
|
-
} catch (err) {
|
|
60
|
-
aLog.error(`waitForWeixinRuntime() failed: ${String(err)}`);
|
|
61
|
-
throw err;
|
|
58
|
+
if (!channelRuntime) {
|
|
59
|
+
const msg =
|
|
60
|
+
"channelRuntime missing on monitor opts; gateway must inject ChannelGatewayContext.channelRuntime";
|
|
61
|
+
aLog.error(msg);
|
|
62
|
+
throw new Error(msg);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
log(`weixin monitor started (${baseUrl}, account=${accountId})`);
|
package/src/runtime.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk/core";
|
|
2
|
-
|
|
3
|
-
import { logger } from "./util/logger.js";
|
|
4
|
-
|
|
5
|
-
let pluginRuntime: PluginRuntime | null = null;
|
|
6
|
-
|
|
7
|
-
export type PluginChannelRuntime = PluginRuntime["channel"];
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Sets the global Weixin runtime (called from plugin register).
|
|
11
|
-
*/
|
|
12
|
-
export function setWeixinRuntime(next: PluginRuntime): void {
|
|
13
|
-
pluginRuntime = next;
|
|
14
|
-
logger.info(`[runtime] setWeixinRuntime called, runtime set successfully`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Gets the global Weixin runtime (throws if not initialized).
|
|
19
|
-
*/
|
|
20
|
-
export function getWeixinRuntime(): PluginRuntime {
|
|
21
|
-
if (!pluginRuntime) {
|
|
22
|
-
throw new Error("Weixin runtime not initialized");
|
|
23
|
-
}
|
|
24
|
-
return pluginRuntime;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const WAIT_INTERVAL_MS = 100;
|
|
28
|
-
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Waits for the Weixin runtime to be initialized (async polling).
|
|
32
|
-
*/
|
|
33
|
-
export async function waitForWeixinRuntime(
|
|
34
|
-
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
35
|
-
): Promise<PluginRuntime> {
|
|
36
|
-
const start = Date.now();
|
|
37
|
-
while (!pluginRuntime) {
|
|
38
|
-
if (Date.now() - start > timeoutMs) {
|
|
39
|
-
throw new Error("Weixin runtime initialization timeout");
|
|
40
|
-
}
|
|
41
|
-
await new Promise((resolve) => setTimeout(resolve, WAIT_INTERVAL_MS));
|
|
42
|
-
}
|
|
43
|
-
return pluginRuntime;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Resolves `PluginRuntime["channel"]` for the long-poll monitor.
|
|
48
|
-
*
|
|
49
|
-
* Prefer the gateway-injected `channelRuntime` on `ChannelGatewayContext` when present (avoids
|
|
50
|
-
* races with the module-global from `register()`). Fall back to the global set by `setWeixinRuntime()`,
|
|
51
|
-
* then to a short wait for legacy hosts.
|
|
52
|
-
*/
|
|
53
|
-
export async function resolveWeixinChannelRuntime(params: {
|
|
54
|
-
channelRuntime?: PluginChannelRuntime;
|
|
55
|
-
waitTimeoutMs?: number;
|
|
56
|
-
}): Promise<PluginChannelRuntime> {
|
|
57
|
-
if (params.channelRuntime) {
|
|
58
|
-
logger.debug("[runtime] channelRuntime from gateway context");
|
|
59
|
-
return params.channelRuntime;
|
|
60
|
-
}
|
|
61
|
-
if (pluginRuntime) {
|
|
62
|
-
logger.debug("[runtime] channelRuntime from register() global");
|
|
63
|
-
return pluginRuntime.channel;
|
|
64
|
-
}
|
|
65
|
-
logger.warn(
|
|
66
|
-
"[runtime] no channelRuntime on ctx and no global runtime yet; waiting for register()",
|
|
67
|
-
);
|
|
68
|
-
const pr = await waitForWeixinRuntime(params.waitTimeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
69
|
-
return pr.channel;
|
|
70
|
-
}
|