@zhin.js/adapter-weixin-ilink 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +96 -0
  2. package/client/LoginAssistPanel.tsx +200 -0
  3. package/client/WeixinIlinkManagement.tsx +139 -0
  4. package/client/index.tsx +11 -0
  5. package/client/tsconfig.json +13 -0
  6. package/client/utils/api.ts +30 -0
  7. package/dist/index.js +29 -0
  8. package/lib/adapter.d.ts +11 -0
  9. package/lib/adapter.d.ts.map +1 -0
  10. package/lib/adapter.js +14 -0
  11. package/lib/adapter.js.map +1 -0
  12. package/lib/aes-ecb.d.ts +7 -0
  13. package/lib/aes-ecb.d.ts.map +1 -0
  14. package/lib/aes-ecb.js +19 -0
  15. package/lib/aes-ecb.js.map +1 -0
  16. package/lib/bot.d.ts +37 -0
  17. package/lib/bot.d.ts.map +1 -0
  18. package/lib/bot.js +383 -0
  19. package/lib/bot.js.map +1 -0
  20. package/lib/cdn-upload.d.ts +18 -0
  21. package/lib/cdn-upload.d.ts.map +1 -0
  22. package/lib/cdn-upload.js +73 -0
  23. package/lib/cdn-upload.js.map +1 -0
  24. package/lib/cdn-url.d.ts +14 -0
  25. package/lib/cdn-url.d.ts.map +1 -0
  26. package/lib/cdn-url.js +14 -0
  27. package/lib/cdn-url.js.map +1 -0
  28. package/lib/context-store.d.ts +2 -0
  29. package/lib/context-store.d.ts.map +1 -0
  30. package/lib/context-store.js +2 -0
  31. package/lib/context-store.js.map +1 -0
  32. package/lib/credentials.d.ts +15 -0
  33. package/lib/credentials.d.ts.map +1 -0
  34. package/lib/credentials.js +49 -0
  35. package/lib/credentials.js.map +1 -0
  36. package/lib/ilink-api.d.ts +81 -0
  37. package/lib/ilink-api.d.ts.map +1 -0
  38. package/lib/ilink-api.js +273 -0
  39. package/lib/ilink-api.js.map +1 -0
  40. package/lib/ilink-config-cache.d.ts +19 -0
  41. package/lib/ilink-config-cache.d.ts.map +1 -0
  42. package/lib/ilink-config-cache.js +64 -0
  43. package/lib/ilink-config-cache.js.map +1 -0
  44. package/lib/ilink-logger.d.ts +10 -0
  45. package/lib/ilink-logger.d.ts.map +1 -0
  46. package/lib/ilink-logger.js +27 -0
  47. package/lib/ilink-logger.js.map +1 -0
  48. package/lib/ilink-meta.d.ts +13 -0
  49. package/lib/ilink-meta.d.ts.map +1 -0
  50. package/lib/ilink-meta.js +57 -0
  51. package/lib/ilink-meta.js.map +1 -0
  52. package/lib/ilink-random.d.ts +11 -0
  53. package/lib/ilink-random.d.ts.map +1 -0
  54. package/lib/ilink-random.js +16 -0
  55. package/lib/ilink-random.js.map +1 -0
  56. package/lib/ilink-redact.d.ts +21 -0
  57. package/lib/ilink-redact.d.ts.map +1 -0
  58. package/lib/ilink-redact.js +54 -0
  59. package/lib/ilink-redact.js.map +1 -0
  60. package/lib/ilink-session-guard.d.ts +16 -0
  61. package/lib/ilink-session-guard.d.ts.map +1 -0
  62. package/lib/ilink-session-guard.js +49 -0
  63. package/lib/ilink-session-guard.js.map +1 -0
  64. package/lib/ilink-types.d.ts +233 -0
  65. package/lib/ilink-types.d.ts.map +1 -0
  66. package/lib/ilink-types.js +35 -0
  67. package/lib/ilink-types.js.map +1 -0
  68. package/lib/index.d.ts +20 -0
  69. package/lib/index.d.ts.map +1 -0
  70. package/lib/index.js +38 -0
  71. package/lib/index.js.map +1 -0
  72. package/lib/login-assist-routes.d.ts +7 -0
  73. package/lib/login-assist-routes.d.ts.map +1 -0
  74. package/lib/login-assist-routes.js +29 -0
  75. package/lib/login-assist-routes.js.map +1 -0
  76. package/lib/login.d.ts +6 -0
  77. package/lib/login.d.ts.map +1 -0
  78. package/lib/login.js +121 -0
  79. package/lib/login.js.map +1 -0
  80. package/lib/media-download.d.ts +19 -0
  81. package/lib/media-download.d.ts.map +1 -0
  82. package/lib/media-download.js +95 -0
  83. package/lib/media-download.js.map +1 -0
  84. package/lib/mime.d.ts +9 -0
  85. package/lib/mime.d.ts.map +1 -0
  86. package/lib/mime.js +99 -0
  87. package/lib/mime.js.map +1 -0
  88. package/lib/outbound-media.d.ts +5 -0
  89. package/lib/outbound-media.d.ts.map +1 -0
  90. package/lib/outbound-media.js +122 -0
  91. package/lib/outbound-media.js.map +1 -0
  92. package/lib/pic-decrypt.d.ts +10 -0
  93. package/lib/pic-decrypt.d.ts.map +1 -0
  94. package/lib/pic-decrypt.js +89 -0
  95. package/lib/pic-decrypt.js.map +1 -0
  96. package/lib/routes.d.ts +8 -0
  97. package/lib/routes.d.ts.map +1 -0
  98. package/lib/routes.js +29 -0
  99. package/lib/routes.js.map +1 -0
  100. package/lib/silk-transcode.d.ts +9 -0
  101. package/lib/silk-transcode.d.ts.map +1 -0
  102. package/lib/silk-transcode.js +64 -0
  103. package/lib/silk-transcode.js.map +1 -0
  104. package/lib/types.d.ts +26 -0
  105. package/lib/types.d.ts.map +1 -0
  106. package/lib/types.js +2 -0
  107. package/lib/types.js.map +1 -0
  108. package/lib/typing-indicator.d.ts +32 -0
  109. package/lib/typing-indicator.d.ts.map +1 -0
  110. package/lib/typing-indicator.js +95 -0
  111. package/lib/typing-indicator.js.map +1 -0
  112. package/lib/upload.d.ts +43 -0
  113. package/lib/upload.d.ts.map +1 -0
  114. package/lib/upload.js +106 -0
  115. package/lib/upload.js.map +1 -0
  116. package/lib/weixin-inbound.d.ts +71 -0
  117. package/lib/weixin-inbound.d.ts.map +1 -0
  118. package/lib/weixin-inbound.js +201 -0
  119. package/lib/weixin-inbound.js.map +1 -0
  120. package/lib/weixin-send-media.d.ts +22 -0
  121. package/lib/weixin-send-media.d.ts.map +1 -0
  122. package/lib/weixin-send-media.js +54 -0
  123. package/lib/weixin-send-media.js.map +1 -0
  124. package/lib/weixin-send.d.ts +65 -0
  125. package/lib/weixin-send.d.ts.map +1 -0
  126. package/lib/weixin-send.js +181 -0
  127. package/lib/weixin-send.js.map +1 -0
  128. package/package.json +91 -0
  129. package/src/adapter.ts +16 -0
  130. package/src/aes-ecb.ts +21 -0
  131. package/src/bot.ts +483 -0
  132. package/src/cdn-upload.test.ts +177 -0
  133. package/src/cdn-upload.ts +87 -0
  134. package/src/cdn-url.ts +20 -0
  135. package/src/context-store.ts +8 -0
  136. package/src/credentials.ts +64 -0
  137. package/src/ilink-api.ts +349 -0
  138. package/src/ilink-config-cache.ts +79 -0
  139. package/src/ilink-logger.ts +36 -0
  140. package/src/ilink-meta.ts +60 -0
  141. package/src/ilink-random.ts +17 -0
  142. package/src/ilink-redact.ts +54 -0
  143. package/src/ilink-session-guard.ts +58 -0
  144. package/src/ilink-types.ts +261 -0
  145. package/src/index.ts +60 -0
  146. package/src/login-assist-routes.ts +36 -0
  147. package/src/login.ts +166 -0
  148. package/src/media-download.ts +149 -0
  149. package/src/mime.test.ts +74 -0
  150. package/src/mime.ts +105 -0
  151. package/src/outbound-media.ts +141 -0
  152. package/src/pic-decrypt.ts +101 -0
  153. package/src/routes.ts +39 -0
  154. package/src/silk-transcode.test.ts +58 -0
  155. package/src/silk-transcode.ts +74 -0
  156. package/src/silk-wasm.d.ts +6 -0
  157. package/src/types.ts +26 -0
  158. package/src/typing-indicator.ts +139 -0
  159. package/src/upload.test.ts +305 -0
  160. package/src/upload.ts +157 -0
  161. package/src/weixin-inbound.ts +257 -0
  162. package/src/weixin-send-media.ts +72 -0
  163. package/src/weixin-send.ts +237 -0
@@ -0,0 +1,81 @@
1
+ import type { GetUploadUrlReq, GetUploadUrlResp, GetUpdatesReq, GetUpdatesResp, NotifyStopResp, NotifyStartResp, SendMessageReq, SendTypingReq, GetConfigResp } from "./ilink-types.js";
2
+ export { buildBaseInfo, sanitizeBotAgent } from "./ilink-meta.js";
3
+ export type WeixinApiOptions = {
4
+ baseUrl: string;
5
+ token?: string;
6
+ timeoutMs?: number;
7
+ /** Long-poll timeout for getUpdates (server may hold the request up to this). */
8
+ longPollTimeoutMs?: number;
9
+ };
10
+ /**
11
+ * GET fetch wrapper: send a GET request to a Weixin API endpoint.
12
+ * When `timeoutMs` is set, the request is aborted after that many milliseconds.
13
+ * Query parameters should already be encoded in `endpoint`.
14
+ * Returns the raw response text on success; throws on HTTP error or (if used) timeout abort.
15
+ */
16
+ export declare function apiGetFetch(params: {
17
+ baseUrl: string;
18
+ endpoint: string;
19
+ timeoutMs?: number;
20
+ label: string;
21
+ }): Promise<string>;
22
+ /**
23
+ * Common fetch wrapper: POST JSON to a Weixin API endpoint.
24
+ * When `timeoutMs` is provided, the request is aborted after that many milliseconds.
25
+ * When omitted, no client-side timeout is applied (relies on OS/TCP stack).
26
+ * When `abortSignal` is provided, an external channel stop also aborts the request.
27
+ * Returns the raw response text on success; throws on HTTP error or timeout.
28
+ */
29
+ export declare function apiPostFetch(params: {
30
+ baseUrl: string;
31
+ endpoint: string;
32
+ body: string;
33
+ token?: string;
34
+ timeoutMs?: number;
35
+ label: string;
36
+ abortSignal?: AbortSignal;
37
+ }): Promise<string>;
38
+ /**
39
+ * Long-poll getUpdates. Server should hold the request until new messages or timeout.
40
+ *
41
+ * On client-side timeout (no server response within timeoutMs), returns an empty response
42
+ * with ret=0 so the caller can simply retry. This is normal for long-poll.
43
+ */
44
+ export declare function getUpdates(params: GetUpdatesReq & {
45
+ baseUrl: string;
46
+ token?: string;
47
+ timeoutMs?: number;
48
+ /**
49
+ * Optional external abort signal (e.g. from the gateway when stopping the
50
+ * channel). When this aborts, the in-flight long-poll is terminated
51
+ * immediately so the monitor loop can exit well within the gateway's
52
+ * channel-stop budget (#141).
53
+ */
54
+ abortSignal?: AbortSignal;
55
+ }): Promise<GetUpdatesResp>;
56
+ /** Get a pre-signed CDN upload URL for a file. */
57
+ export declare function getUploadUrl(params: GetUploadUrlReq & WeixinApiOptions): Promise<GetUploadUrlResp>;
58
+ /** Send a single message downstream. */
59
+ export declare function sendMessage(params: WeixinApiOptions & {
60
+ body: SendMessageReq;
61
+ }): Promise<void>;
62
+ /** Fetch bot config (includes typing_ticket) for a given user. */
63
+ export declare function getConfig(params: WeixinApiOptions & {
64
+ ilinkUserId: string;
65
+ contextToken?: string;
66
+ }): Promise<GetConfigResp>;
67
+ /** Send a typing indicator to a user. */
68
+ export declare function sendTyping(params: WeixinApiOptions & {
69
+ body: SendTypingReq;
70
+ }): Promise<void>;
71
+ /**
72
+ * Notify Weixin that this channel client is stopping (gateway shutdown / channel stop).
73
+ * Uses a standalone timeout (not the gateway abort signal) so the request can finish
74
+ * after OpenClaw has already aborted the long-poll.
75
+ */
76
+ export declare function notifyStop(params: WeixinApiOptions): Promise<NotifyStopResp>;
77
+ /**
78
+ * Notify Weixin that this channel client is starting (gateway startup / channel start).
79
+ */
80
+ export declare function notifyStart(params: WeixinApiOptions): Promise<NotifyStartResp>;
81
+ //# sourceMappingURL=ilink-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-api.d.ts","sourceRoot":"","sources":["../src/ilink-api.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AA4CF;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BlB;AAuBD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GAAG,OAAO,CAAC,MAAM,CAAC,CAgClB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,aAAa,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GACA,OAAO,CAAC,cAAc,CAAC,CAiCzB;AAED,kDAAkD;AAClD,wBAAsB,YAAY,CAChC,MAAM,EAAE,eAAe,GAAG,gBAAgB,GACzC,OAAO,CAAC,gBAAgB,CAAC,CAwB3B;AAED,wCAAwC;AACxC,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CASf;AAED,kEAAkE;AAClE,wBAAsB,SAAS,CAC7B,MAAM,EAAE,gBAAgB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACxE,OAAO,CAAC,aAAa,CAAC,CAexB;AAED,yCAAyC;AACzC,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACjD,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CAUlF;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAUpF"}
@@ -0,0 +1,273 @@
1
+ import crypto from "node:crypto";
2
+ import { logger } from "./ilink-logger.js";
3
+ import { buildBaseInfo, ILINK_APP_CLIENT_VERSION, ILINK_APP_ID, } from "./ilink-meta.js";
4
+ import { redactBody, redactUrl } from "./ilink-redact.js";
5
+ export { buildBaseInfo, sanitizeBotAgent } from "./ilink-meta.js";
6
+ /** Default timeout for long-poll getUpdates requests. */
7
+ const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
8
+ /** Default timeout for regular API requests (sendMessage, getUploadUrl). */
9
+ const DEFAULT_API_TIMEOUT_MS = 15_000;
10
+ /** Default timeout for lightweight API requests (getConfig, sendTyping). */
11
+ const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
12
+ function ensureTrailingSlash(url) {
13
+ return url.endsWith("/") ? url : `${url}/`;
14
+ }
15
+ /** X-WECHAT-UIN header: random uint32 -> decimal string -> base64. */
16
+ function randomWechatUin() {
17
+ const uint32 = crypto.randomBytes(4).readUInt32BE(0);
18
+ return Buffer.from(String(uint32), "utf-8").toString("base64");
19
+ }
20
+ /** Build headers shared by both GET and POST requests. */
21
+ function buildCommonHeaders() {
22
+ const headers = {
23
+ "iLink-App-Id": ILINK_APP_ID,
24
+ "iLink-App-ClientVersion": String(ILINK_APP_CLIENT_VERSION),
25
+ };
26
+ return headers;
27
+ }
28
+ function buildHeaders(opts) {
29
+ const headers = {
30
+ "Content-Type": "application/json",
31
+ AuthorizationType: "ilink_bot_token",
32
+ "X-WECHAT-UIN": randomWechatUin(),
33
+ ...buildCommonHeaders(),
34
+ };
35
+ if (opts.token?.trim()) {
36
+ headers.Authorization = `Bearer ${opts.token.trim()}`;
37
+ }
38
+ logger.debug(`requestHeaders: ${JSON.stringify({ ...headers, Authorization: headers.Authorization ? "Bearer ***" : undefined })}`);
39
+ return headers;
40
+ }
41
+ /**
42
+ * GET fetch wrapper: send a GET request to a Weixin API endpoint.
43
+ * When `timeoutMs` is set, the request is aborted after that many milliseconds.
44
+ * Query parameters should already be encoded in `endpoint`.
45
+ * Returns the raw response text on success; throws on HTTP error or (if used) timeout abort.
46
+ */
47
+ export async function apiGetFetch(params) {
48
+ const base = ensureTrailingSlash(params.baseUrl);
49
+ const url = new URL(params.endpoint, base);
50
+ const hdrs = buildCommonHeaders();
51
+ logger.debug(`GET ${redactUrl(url.toString())}`);
52
+ const timeoutMs = params.timeoutMs;
53
+ const controller = timeoutMs != null && timeoutMs > 0 ? new AbortController() : undefined;
54
+ const t = controller != null && timeoutMs != null
55
+ ? setTimeout(() => controller.abort(), timeoutMs)
56
+ : undefined;
57
+ try {
58
+ const res = await fetch(url.toString(), {
59
+ method: "GET",
60
+ headers: hdrs,
61
+ ...(controller ? { signal: controller.signal } : {}),
62
+ });
63
+ if (t !== undefined)
64
+ clearTimeout(t);
65
+ const rawText = await res.text();
66
+ logger.debug(`${params.label} status=${res.status} raw=${redactBody(rawText)}`);
67
+ if (!res.ok) {
68
+ throw new Error(`${params.label} ${res.status}: ${rawText}`);
69
+ }
70
+ return rawText;
71
+ }
72
+ catch (err) {
73
+ if (t !== undefined)
74
+ clearTimeout(t);
75
+ throw err;
76
+ }
77
+ }
78
+ function combineAbortSignals(internal, external) {
79
+ if (!internal && !external)
80
+ return { cleanup: () => { } };
81
+ if (!internal)
82
+ return { signal: external, cleanup: () => { } };
83
+ if (!external)
84
+ return { signal: internal.signal, cleanup: () => { } };
85
+ if (external.aborted) {
86
+ internal.abort();
87
+ return { signal: internal.signal, cleanup: () => { } };
88
+ }
89
+ const onExternalAbort = () => internal.abort();
90
+ external.addEventListener("abort", onExternalAbort, { once: true });
91
+ return {
92
+ signal: internal.signal,
93
+ cleanup: () => external.removeEventListener("abort", onExternalAbort),
94
+ };
95
+ }
96
+ /**
97
+ * Common fetch wrapper: POST JSON to a Weixin API endpoint.
98
+ * When `timeoutMs` is provided, the request is aborted after that many milliseconds.
99
+ * When omitted, no client-side timeout is applied (relies on OS/TCP stack).
100
+ * When `abortSignal` is provided, an external channel stop also aborts the request.
101
+ * Returns the raw response text on success; throws on HTTP error or timeout.
102
+ */
103
+ export async function apiPostFetch(params) {
104
+ const base = ensureTrailingSlash(params.baseUrl);
105
+ const url = new URL(params.endpoint, base);
106
+ const hdrs = buildHeaders({ token: params.token });
107
+ logger.debug(`POST ${redactUrl(url.toString())} body=${redactBody(params.body)}`);
108
+ const controller = params.timeoutMs !== undefined ? new AbortController() : undefined;
109
+ const t = controller != null && params.timeoutMs !== undefined
110
+ ? setTimeout(() => controller.abort(), params.timeoutMs)
111
+ : undefined;
112
+ const { signal, cleanup } = combineAbortSignals(controller, params.abortSignal);
113
+ try {
114
+ const res = await fetch(url.toString(), {
115
+ method: "POST",
116
+ headers: hdrs,
117
+ body: params.body,
118
+ ...(signal ? { signal } : {}),
119
+ });
120
+ const rawText = await res.text();
121
+ logger.debug(`${params.label} status=${res.status} raw=${redactBody(rawText)}`);
122
+ if (!res.ok) {
123
+ throw new Error(`${params.label} ${res.status}: ${rawText}`);
124
+ }
125
+ return rawText;
126
+ }
127
+ catch (err) {
128
+ throw err;
129
+ }
130
+ finally {
131
+ if (t !== undefined)
132
+ clearTimeout(t);
133
+ cleanup();
134
+ }
135
+ }
136
+ /**
137
+ * Long-poll getUpdates. Server should hold the request until new messages or timeout.
138
+ *
139
+ * On client-side timeout (no server response within timeoutMs), returns an empty response
140
+ * with ret=0 so the caller can simply retry. This is normal for long-poll.
141
+ */
142
+ export async function getUpdates(params) {
143
+ const timeout = params.timeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;
144
+ try {
145
+ const rawText = await apiPostFetch({
146
+ baseUrl: params.baseUrl,
147
+ endpoint: "ilink/bot/getupdates",
148
+ body: JSON.stringify({
149
+ get_updates_buf: params.get_updates_buf ?? "",
150
+ base_info: buildBaseInfo(),
151
+ }),
152
+ token: params.token,
153
+ timeoutMs: timeout,
154
+ label: "getUpdates",
155
+ abortSignal: params.abortSignal,
156
+ });
157
+ const resp = JSON.parse(rawText);
158
+ return resp;
159
+ }
160
+ catch (err) {
161
+ // Long-poll timeout *or* external abort both surface as AbortError. The caller
162
+ // re-checks `abortSignal?.aborted` after we return; when aborted, it exits
163
+ // the loop. When not aborted (i.e. plain client-side long-poll timeout),
164
+ // returning the empty response lets the caller retry — preserving prior
165
+ // behavior for the normal long-poll case.
166
+ if (err instanceof Error && err.name === "AbortError") {
167
+ if (params.abortSignal?.aborted) {
168
+ logger.debug(`getUpdates: aborted by external signal`);
169
+ }
170
+ else {
171
+ logger.debug(`getUpdates: client-side timeout after ${timeout}ms, returning empty response`);
172
+ }
173
+ return { ret: 0, msgs: [], get_updates_buf: params.get_updates_buf };
174
+ }
175
+ throw err;
176
+ }
177
+ }
178
+ /** Get a pre-signed CDN upload URL for a file. */
179
+ export async function getUploadUrl(params) {
180
+ const rawText = await apiPostFetch({
181
+ baseUrl: params.baseUrl,
182
+ endpoint: "ilink/bot/getuploadurl",
183
+ body: JSON.stringify({
184
+ filekey: params.filekey,
185
+ media_type: params.media_type,
186
+ to_user_id: params.to_user_id,
187
+ rawsize: params.rawsize,
188
+ rawfilemd5: params.rawfilemd5,
189
+ filesize: params.filesize,
190
+ thumb_rawsize: params.thumb_rawsize,
191
+ thumb_rawfilemd5: params.thumb_rawfilemd5,
192
+ thumb_filesize: params.thumb_filesize,
193
+ no_need_thumb: params.no_need_thumb,
194
+ aeskey: params.aeskey,
195
+ base_info: buildBaseInfo(),
196
+ }),
197
+ token: params.token,
198
+ timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
199
+ label: "getUploadUrl",
200
+ });
201
+ const resp = JSON.parse(rawText);
202
+ return resp;
203
+ }
204
+ /** Send a single message downstream. */
205
+ export async function sendMessage(params) {
206
+ await apiPostFetch({
207
+ baseUrl: params.baseUrl,
208
+ endpoint: "ilink/bot/sendmessage",
209
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
210
+ token: params.token,
211
+ timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
212
+ label: "sendMessage",
213
+ });
214
+ }
215
+ /** Fetch bot config (includes typing_ticket) for a given user. */
216
+ export async function getConfig(params) {
217
+ const rawText = await apiPostFetch({
218
+ baseUrl: params.baseUrl,
219
+ endpoint: "ilink/bot/getconfig",
220
+ body: JSON.stringify({
221
+ ilink_user_id: params.ilinkUserId,
222
+ context_token: params.contextToken,
223
+ base_info: buildBaseInfo(),
224
+ }),
225
+ token: params.token,
226
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
227
+ label: "getConfig",
228
+ });
229
+ const resp = JSON.parse(rawText);
230
+ return resp;
231
+ }
232
+ /** Send a typing indicator to a user. */
233
+ export async function sendTyping(params) {
234
+ await apiPostFetch({
235
+ baseUrl: params.baseUrl,
236
+ endpoint: "ilink/bot/sendtyping",
237
+ body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
238
+ token: params.token,
239
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
240
+ label: "sendTyping",
241
+ });
242
+ }
243
+ /**
244
+ * Notify Weixin that this channel client is stopping (gateway shutdown / channel stop).
245
+ * Uses a standalone timeout (not the gateway abort signal) so the request can finish
246
+ * after OpenClaw has already aborted the long-poll.
247
+ */
248
+ export async function notifyStop(params) {
249
+ const rawText = await apiPostFetch({
250
+ baseUrl: params.baseUrl,
251
+ endpoint: "ilink/bot/msg/notifystop",
252
+ body: JSON.stringify({ base_info: buildBaseInfo() }),
253
+ token: params.token,
254
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
255
+ label: "notifyStop",
256
+ });
257
+ return JSON.parse(rawText);
258
+ }
259
+ /**
260
+ * Notify Weixin that this channel client is starting (gateway startup / channel start).
261
+ */
262
+ export async function notifyStart(params) {
263
+ const rawText = await apiPostFetch({
264
+ baseUrl: params.baseUrl,
265
+ endpoint: "ilink/bot/msg/notifystart",
266
+ body: JSON.stringify({ base_info: buildBaseInfo() }),
267
+ token: params.token,
268
+ timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
269
+ label: "notifyStart",
270
+ });
271
+ return JSON.parse(rawText);
272
+ }
273
+ //# sourceMappingURL=ilink-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-api.js","sourceRoot":"","sources":["../src/ilink-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /** Subset of getConfig fields that we actually need; add new fields here as needed. */
2
+ export interface CachedConfig {
3
+ typingTicket: string;
4
+ }
5
+ /**
6
+ * Per-user getConfig cache with periodic random refresh (within 24h) and
7
+ * exponential-backoff retry (up to 1h) on failure.
8
+ */
9
+ export declare class WeixinConfigManager {
10
+ private apiOpts;
11
+ private log;
12
+ private cache;
13
+ constructor(apiOpts: {
14
+ baseUrl: string;
15
+ token?: string;
16
+ }, log: (msg: string) => void);
17
+ getForUser(userId: string, contextToken?: string): Promise<CachedConfig>;
18
+ }
19
+ //# sourceMappingURL=ilink-config-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-config-cache.d.ts","sourceRoot":"","sources":["../src/ilink-config-cache.ts"],"names":[],"mappings":"AAEA,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB;AAaD;;;GAGG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,GAAG;IAJb,OAAO,CAAC,KAAK,CAAuC;gBAG1C,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAC5C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI;IAG9B,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAgD/E"}
@@ -0,0 +1,64 @@
1
+ import { getConfig } from "./ilink-api.js";
2
+ const CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
3
+ const CONFIG_CACHE_INITIAL_RETRY_MS = 2_000;
4
+ const CONFIG_CACHE_MAX_RETRY_MS = 60 * 60 * 1000;
5
+ /**
6
+ * Per-user getConfig cache with periodic random refresh (within 24h) and
7
+ * exponential-backoff retry (up to 1h) on failure.
8
+ */
9
+ export class WeixinConfigManager {
10
+ apiOpts;
11
+ log;
12
+ cache = new Map();
13
+ constructor(apiOpts, log) {
14
+ this.apiOpts = apiOpts;
15
+ this.log = log;
16
+ }
17
+ async getForUser(userId, contextToken) {
18
+ const now = Date.now();
19
+ const entry = this.cache.get(userId);
20
+ const shouldFetch = !entry || now >= entry.nextFetchAt;
21
+ if (shouldFetch) {
22
+ let fetchOk = false;
23
+ try {
24
+ const resp = await getConfig({
25
+ baseUrl: this.apiOpts.baseUrl,
26
+ token: this.apiOpts.token,
27
+ ilinkUserId: userId,
28
+ contextToken,
29
+ });
30
+ if (resp.ret === 0) {
31
+ this.cache.set(userId, {
32
+ config: { typingTicket: resp.typing_ticket ?? "" },
33
+ everSucceeded: true,
34
+ nextFetchAt: now + Math.random() * CONFIG_CACHE_TTL_MS,
35
+ retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
36
+ });
37
+ this.log(`[weixin] config ${entry?.everSucceeded ? "refreshed" : "cached"} for ${userId}`);
38
+ fetchOk = true;
39
+ }
40
+ }
41
+ catch (err) {
42
+ this.log(`[weixin] getConfig failed for ${userId} (ignored): ${String(err)}`);
43
+ }
44
+ if (!fetchOk) {
45
+ const prevDelay = entry?.retryDelayMs ?? CONFIG_CACHE_INITIAL_RETRY_MS;
46
+ const nextDelay = Math.min(prevDelay * 2, CONFIG_CACHE_MAX_RETRY_MS);
47
+ if (entry) {
48
+ entry.nextFetchAt = now + nextDelay;
49
+ entry.retryDelayMs = nextDelay;
50
+ }
51
+ else {
52
+ this.cache.set(userId, {
53
+ config: { typingTicket: "" },
54
+ everSucceeded: false,
55
+ nextFetchAt: now + CONFIG_CACHE_INITIAL_RETRY_MS,
56
+ retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
57
+ });
58
+ }
59
+ }
60
+ }
61
+ return this.cache.get(userId)?.config ?? { typingTicket: "" };
62
+ }
63
+ }
64
+ //# sourceMappingURL=ilink-config-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-config-cache.js","sourceRoot":"","sources":["../src/ilink-config-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type IlinkLogger = {
2
+ debug: (msg: string) => void;
3
+ info: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ withAccount: (accountId: string) => IlinkLogger;
7
+ };
8
+ export declare function setIlinkLogger(next: IlinkLogger): void;
9
+ export declare const logger: IlinkLogger;
10
+ //# sourceMappingURL=ilink-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-logger.d.ts","sourceRoot":"","sources":["../src/ilink-logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,WAAW,CAAC;CACjD,CAAC;AAmBF,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAEtD;AAED,eAAO,MAAM,MAAM,EAAE,WAMpB,CAAC"}
@@ -0,0 +1,27 @@
1
+ let sink = {
2
+ debug: () => { },
3
+ info: () => { },
4
+ warn: (msg) => console.warn(`[weixin-ilink] ${msg}`),
5
+ error: (msg) => console.error(`[weixin-ilink] ${msg}`),
6
+ withAccount(accountId) {
7
+ const prefix = `[weixin-ilink:${accountId}]`;
8
+ return {
9
+ debug: (msg) => sink.debug(`${prefix} ${msg}`),
10
+ info: (msg) => sink.info(`${prefix} ${msg}`),
11
+ warn: (msg) => sink.warn(`${prefix} ${msg}`),
12
+ error: (msg) => sink.error(`${prefix} ${msg}`),
13
+ withAccount: () => this,
14
+ };
15
+ },
16
+ };
17
+ export function setIlinkLogger(next) {
18
+ sink = next;
19
+ }
20
+ export const logger = {
21
+ debug: (msg) => sink.debug(msg),
22
+ info: (msg) => sink.info(msg),
23
+ warn: (msg) => sink.warn(msg),
24
+ error: (msg) => sink.error(msg),
25
+ withAccount: (accountId) => sink.withAccount(accountId),
26
+ };
27
+ //# sourceMappingURL=ilink-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-logger.js","sourceRoot":"","sources":["../src/ilink-logger.ts"],"names":[],"mappings":"AAQA,IAAI,IAAI,GAAgB;IACtB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;IACd,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IACpD,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,GAAG,EAAE,CAAC;IACtD,WAAW,CAAC,SAAS;QACnB,MAAM,MAAM,GAAG,iBAAiB,SAAS,GAAG,CAAC;QAC7C,OAAO;YACL,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;YAC9C,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;YAC5C,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;YAC5C,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC;YAC9C,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;SACxB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,IAAiB;IAC9C,IAAI,GAAG,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAgB;IACjC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC/B,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7B,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7B,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC/B,WAAW,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;CACxD,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { BaseInfo } from "./ilink-types.js";
2
+ export declare const ILINK_APP_ID = "bot";
3
+ export declare const DEFAULT_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
4
+ export declare const DEFAULT_API_BASE_URL = "https://ilinkai.weixin.qq.com";
5
+ export declare function configureIlinkMeta(opts: {
6
+ botAgent?: string;
7
+ version?: string;
8
+ }): void;
9
+ export declare function buildClientVersion(version: string): number;
10
+ export declare const ILINK_APP_CLIENT_VERSION: number;
11
+ export declare function sanitizeBotAgent(raw: string | undefined): string;
12
+ export declare function buildBaseInfo(): BaseInfo;
13
+ //# sourceMappingURL=ilink-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-meta.d.ts","sourceRoot":"","sources":["../src/ilink-meta.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,eAAO,MAAM,YAAY,QAAQ,CAAC;AAClC,eAAO,MAAM,oBAAoB,0CAA0C,CAAC;AAC5E,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAoBpE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGtF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,eAAO,MAAM,wBAAwB,QAAqC,CAAC;AAE3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAUhE;AAED,wBAAgB,aAAa,IAAI,QAAQ,CAKxC"}
@@ -0,0 +1,57 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export const ILINK_APP_ID = "bot";
5
+ export const DEFAULT_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
6
+ export const DEFAULT_API_BASE_URL = "https://ilinkai.weixin.qq.com";
7
+ const DEFAULT_BOT_AGENT = "Zhin.js";
8
+ const BOT_AGENT_MAX_LEN = 256;
9
+ let channelVersion = "0.0.0";
10
+ let configuredBotAgent;
11
+ function readPackageVersion() {
12
+ try {
13
+ const pkgPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "../package.json");
14
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
15
+ return pkg.version ?? "0.0.0";
16
+ }
17
+ catch {
18
+ return "0.0.0";
19
+ }
20
+ }
21
+ channelVersion = readPackageVersion();
22
+ export function configureIlinkMeta(opts) {
23
+ if (opts.botAgent)
24
+ configuredBotAgent = opts.botAgent;
25
+ if (opts.version)
26
+ channelVersion = opts.version;
27
+ }
28
+ export function buildClientVersion(version) {
29
+ const parts = version.split(".").map((p) => parseInt(p, 10));
30
+ const major = parts[0] ?? 0;
31
+ const minor = parts[1] ?? 0;
32
+ const patch = parts[2] ?? 0;
33
+ return ((major & 0xff) << 16) | ((minor & 0xff) << 8) | (patch & 0xff);
34
+ }
35
+ export const ILINK_APP_CLIENT_VERSION = buildClientVersion(channelVersion);
36
+ export function sanitizeBotAgent(raw) {
37
+ if (!raw || typeof raw !== "string")
38
+ return configuredBotAgent ?? DEFAULT_BOT_AGENT;
39
+ const trimmed = raw.trim();
40
+ if (!trimmed)
41
+ return configuredBotAgent ?? DEFAULT_BOT_AGENT;
42
+ const productRe = /^[A-Za-z0-9_.\-]{1,32}\/[A-Za-z0-9_.+\-]{1,32}$/;
43
+ const tokens = trimmed.split(/\s+/).filter((tok) => productRe.test(tok));
44
+ if (tokens.length === 0)
45
+ return configuredBotAgent ?? DEFAULT_BOT_AGENT;
46
+ const joined = tokens.join(" ");
47
+ if (Buffer.byteLength(joined, "utf-8") <= BOT_AGENT_MAX_LEN)
48
+ return joined;
49
+ return (configuredBotAgent ?? DEFAULT_BOT_AGENT);
50
+ }
51
+ export function buildBaseInfo() {
52
+ return {
53
+ channel_version: channelVersion,
54
+ bot_agent: sanitizeBotAgent(configuredBotAgent),
55
+ };
56
+ }
57
+ //# sourceMappingURL=ilink-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-meta.js","sourceRoot":"","sources":["../src/ilink-meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Generate a prefixed unique ID using timestamp + crypto random bytes.
3
+ * Format: `{prefix}:{timestamp}-{8-char hex}`
4
+ */
5
+ export declare function generateId(prefix: string): string;
6
+ /**
7
+ * Generate a temporary file name with random suffix.
8
+ * Format: `{prefix}-{timestamp}-{8-char hex}{ext}`
9
+ */
10
+ export declare function tempFileName(prefix: string, ext: string): string;
11
+ //# sourceMappingURL=ilink-random.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-random.d.ts","sourceRoot":"","sources":["../src/ilink-random.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhE"}
@@ -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=ilink-random.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ilink-random.js","sourceRoot":"","sources":["../src/ilink-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"}