@krischoichoi/channel-weixin 0.6.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 (138) hide show
  1. package/README.md +101 -0
  2. package/lib/adapter.d.ts +90 -0
  3. package/lib/adapter.d.ts.map +1 -0
  4. package/lib/adapter.js +247 -0
  5. package/lib/adapter.js.map +1 -0
  6. package/lib/auth/account-store.d.ts +60 -0
  7. package/lib/auth/account-store.d.ts.map +1 -0
  8. package/lib/auth/account-store.js +84 -0
  9. package/lib/auth/account-store.js.map +1 -0
  10. package/lib/auth/login.d.ts +83 -0
  11. package/lib/auth/login.d.ts.map +1 -0
  12. package/lib/auth/login.js +202 -0
  13. package/lib/auth/login.js.map +1 -0
  14. package/lib/config.d.ts +52 -0
  15. package/lib/config.d.ts.map +1 -0
  16. package/lib/config.js +36 -0
  17. package/lib/config.js.map +1 -0
  18. package/lib/definition.d.ts +59 -0
  19. package/lib/definition.d.ts.map +1 -0
  20. package/lib/definition.js +146 -0
  21. package/lib/definition.js.map +1 -0
  22. package/lib/ilink/base-info.d.ts +22 -0
  23. package/lib/ilink/base-info.d.ts.map +1 -0
  24. package/lib/ilink/base-info.js +11 -0
  25. package/lib/ilink/base-info.js.map +1 -0
  26. package/lib/ilink/client.d.ts +110 -0
  27. package/lib/ilink/client.d.ts.map +1 -0
  28. package/lib/ilink/client.js +308 -0
  29. package/lib/ilink/client.js.map +1 -0
  30. package/lib/ilink/constants.d.ts +59 -0
  31. package/lib/ilink/constants.d.ts.map +1 -0
  32. package/lib/ilink/constants.js +59 -0
  33. package/lib/ilink/constants.js.map +1 -0
  34. package/lib/ilink/errors.d.ts +79 -0
  35. package/lib/ilink/errors.d.ts.map +1 -0
  36. package/lib/ilink/errors.js +121 -0
  37. package/lib/ilink/errors.js.map +1 -0
  38. package/lib/ilink/headers.d.ts +40 -0
  39. package/lib/ilink/headers.d.ts.map +1 -0
  40. package/lib/ilink/headers.js +70 -0
  41. package/lib/ilink/headers.js.map +1 -0
  42. package/lib/ilink/schema.d.ts +154 -0
  43. package/lib/ilink/schema.d.ts.map +1 -0
  44. package/lib/ilink/schema.js +140 -0
  45. package/lib/ilink/schema.js.map +1 -0
  46. package/lib/ilink/types.d.ts +235 -0
  47. package/lib/ilink/types.d.ts.map +1 -0
  48. package/lib/ilink/types.js +21 -0
  49. package/lib/ilink/types.js.map +1 -0
  50. package/lib/index.d.ts +50 -0
  51. package/lib/index.d.ts.map +1 -0
  52. package/lib/index.js +91 -0
  53. package/lib/index.js.map +1 -0
  54. package/lib/manifest.d.ts +54 -0
  55. package/lib/manifest.d.ts.map +1 -0
  56. package/lib/manifest.js +32 -0
  57. package/lib/manifest.js.map +1 -0
  58. package/lib/media/decrypt.d.ts +3 -0
  59. package/lib/media/decrypt.d.ts.map +1 -0
  60. package/lib/media/decrypt.js +16 -0
  61. package/lib/media/decrypt.js.map +1 -0
  62. package/lib/media/download.d.ts +22 -0
  63. package/lib/media/download.d.ts.map +1 -0
  64. package/lib/media/download.js +62 -0
  65. package/lib/media/download.js.map +1 -0
  66. package/lib/media/encrypt.d.ts +3 -0
  67. package/lib/media/encrypt.d.ts.map +1 -0
  68. package/lib/media/encrypt.js +16 -0
  69. package/lib/media/encrypt.js.map +1 -0
  70. package/lib/media/send-media.d.ts +41 -0
  71. package/lib/media/send-media.d.ts.map +1 -0
  72. package/lib/media/send-media.js +52 -0
  73. package/lib/media/send-media.js.map +1 -0
  74. package/lib/media/silk-transcode.d.ts +38 -0
  75. package/lib/media/silk-transcode.d.ts.map +1 -0
  76. package/lib/media/silk-transcode.js +88 -0
  77. package/lib/media/silk-transcode.js.map +1 -0
  78. package/lib/media/upload.d.ts +38 -0
  79. package/lib/media/upload.d.ts.map +1 -0
  80. package/lib/media/upload.js +122 -0
  81. package/lib/media/upload.js.map +1 -0
  82. package/lib/messaging/dedup.d.ts +87 -0
  83. package/lib/messaging/dedup.d.ts.map +1 -0
  84. package/lib/messaging/dedup.js +127 -0
  85. package/lib/messaging/dedup.js.map +1 -0
  86. package/lib/messaging/mapper.d.ts +26 -0
  87. package/lib/messaging/mapper.d.ts.map +1 -0
  88. package/lib/messaging/mapper.js +107 -0
  89. package/lib/messaging/mapper.js.map +1 -0
  90. package/lib/messaging/monitor.d.ts +109 -0
  91. package/lib/messaging/monitor.d.ts.map +1 -0
  92. package/lib/messaging/monitor.js +234 -0
  93. package/lib/messaging/monitor.js.map +1 -0
  94. package/lib/messaging/quote.d.ts +9 -0
  95. package/lib/messaging/quote.d.ts.map +1 -0
  96. package/lib/messaging/quote.js +35 -0
  97. package/lib/messaging/quote.js.map +1 -0
  98. package/lib/messaging/send.d.ts +54 -0
  99. package/lib/messaging/send.d.ts.map +1 -0
  100. package/lib/messaging/send.js +173 -0
  101. package/lib/messaging/send.js.map +1 -0
  102. package/lib/messaging/typing.d.ts +34 -0
  103. package/lib/messaging/typing.d.ts.map +1 -0
  104. package/lib/messaging/typing.js +56 -0
  105. package/lib/messaging/typing.js.map +1 -0
  106. package/lib/storage/context-token.d.ts +26 -0
  107. package/lib/storage/context-token.d.ts.map +1 -0
  108. package/lib/storage/context-token.js +26 -0
  109. package/lib/storage/context-token.js.map +1 -0
  110. package/lib/storage/sync-cursor.d.ts +27 -0
  111. package/lib/storage/sync-cursor.d.ts.map +1 -0
  112. package/lib/storage/sync-cursor.js +25 -0
  113. package/lib/storage/sync-cursor.js.map +1 -0
  114. package/lib/transport.d.ts +38 -0
  115. package/lib/transport.d.ts.map +1 -0
  116. package/lib/transport.js +68 -0
  117. package/lib/transport.js.map +1 -0
  118. package/lib/upstream/compat.d.ts +54 -0
  119. package/lib/upstream/compat.d.ts.map +1 -0
  120. package/lib/upstream/compat.js +54 -0
  121. package/lib/upstream/compat.js.map +1 -0
  122. package/lib/upstream/config-manager.d.ts +35 -0
  123. package/lib/upstream/config-manager.d.ts.map +1 -0
  124. package/lib/upstream/config-manager.js +91 -0
  125. package/lib/upstream/config-manager.js.map +1 -0
  126. package/lib/upstream/index.d.ts +42 -0
  127. package/lib/upstream/index.d.ts.map +1 -0
  128. package/lib/upstream/index.js +13 -0
  129. package/lib/upstream/index.js.map +1 -0
  130. package/lib/upstream/port.d.ts +153 -0
  131. package/lib/upstream/port.d.ts.map +1 -0
  132. package/lib/upstream/port.js +15 -0
  133. package/lib/upstream/port.js.map +1 -0
  134. package/lib/upstream/tencent-upstream.d.ts +116 -0
  135. package/lib/upstream/tencent-upstream.d.ts.map +1 -0
  136. package/lib/upstream/tencent-upstream.js +518 -0
  137. package/lib/upstream/tencent-upstream.js.map +1 -0
  138. package/package.json +48 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Weixin QR login state machine (WX2).
3
+ *
4
+ * Wraps the iLink `get_bot_qrcode` / `get_qrcode_status` calls with the full
5
+ * upstream state machine (wait/scaned/confirmed/expired/need_verifycode/
6
+ * verify_code_blocked/scaned_but_redirect/binded_redirect). The Channel
7
+ * Contract states (pending/authenticated/expired/failed) may normalize the
8
+ * upstream states, but the internal machine keeps full fidelity so none of the
9
+ * upstream information is lost.
10
+ *
11
+ * Redirect (`scaned_but_redirect`) updates the {@link ILinkClient} baseUrl for
12
+ * all later calls. Verify codes are submitted through {@link WeixinQrAuth.submitVerifyCode}
13
+ * — never through stdin — so the CLI/ChannelService owns that UX.
14
+ */
15
+ import type { AuthChallenge, AuthStatePoll } from '@krischoichoi/channel-core';
16
+ import type { ILinkClient } from '../ilink/client.js';
17
+ import type { ILinkQrStatus } from '../ilink/types.js';
18
+ export interface WeixinQrAuthOptions {
19
+ client: ILinkClient;
20
+ /** Recent locally-held bot tokens, supplied by the credential owner. */
21
+ localTokens?: readonly string[];
22
+ /** Injectable clock (tests). */
23
+ now?: () => number;
24
+ /** Optional expiry budget in ms (default 5 min). */
25
+ expiresInMs?: number;
26
+ }
27
+ /** Full fidelity upstream state + redirect/baseUrl tracking. */
28
+ export interface WeixinQrAuthInternalState {
29
+ status: ILinkQrStatus | 'unknown' | 'failed';
30
+ /** Effective base URL after IDC redirect. */
31
+ baseUrl?: string;
32
+ botToken?: string;
33
+ ilinkBotId?: string;
34
+ userId?: string;
35
+ pendingVerifyCode?: boolean;
36
+ /** True when `binded_redirect` reports the bot is already bound. */
37
+ alreadyBound?: boolean;
38
+ }
39
+ /** Map a raw upstream status (already validated) to a Channel AuthState. */
40
+ export declare function toChannelState(status: ILinkQrStatus | 'unknown'): AuthStatePoll['state'];
41
+ export declare class WeixinQrAuth {
42
+ private readonly client;
43
+ private readonly now;
44
+ private readonly expiresInMs;
45
+ private readonly localTokens?;
46
+ /** The last known full-fidelity upstream state. */
47
+ private state;
48
+ /** Active challenge state. */
49
+ private activeId?;
50
+ private qrcode?;
51
+ private startedAt;
52
+ private pendingVerifyCode?;
53
+ constructor(options: WeixinQrAuthOptions);
54
+ /** Current full-fidelity internal state. */
55
+ getState(): WeixinQrAuthInternalState;
56
+ /** Whether the last known state is authenticated. */
57
+ get isAuthenticated(): boolean;
58
+ /**
59
+ * Begin QR auth: fetch a fresh QR challenge from the iLink client.
60
+ * Returns the Channel Contract {@link AuthChallenge}.
61
+ */
62
+ beginAuth(): Promise<AuthChallenge>;
63
+ /**
64
+ * Poll the QR status. On `scaned_but_redirect`, updates the client baseUrl
65
+ * from `redirect_host`. On `confirmed`, records token/botId/baseUrl/userId
66
+ * and returns the applicable Channel AuthStatePoll.
67
+ */
68
+ pollAuth(challenge: AuthChallenge): Promise<AuthStatePoll>;
69
+ /**
70
+ * Submit a phone-verify code (from the ChannelService/CLI — never stdin
71
+ * inside the adapter). Clears any previously pending code.
72
+ */
73
+ submitVerifyCode(code: string): void;
74
+ /** The credential-ready result produced by a `confirmed` poll. */
75
+ get confirmedCredential(): {
76
+ token: string;
77
+ ilinkBotId: string;
78
+ userId?: string;
79
+ baseUrl: string;
80
+ } | undefined;
81
+ }
82
+ export type { AuthChallenge, AuthStatePoll };
83
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAyB,MAAM,mBAAmB,CAAC;AAE9E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,wEAAwE;IACxE,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,gEAAgE;AAChE,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC7C,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAYD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CAgBxF;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAoB;IAEjD,mDAAmD;IACnD,OAAO,CAAC,KAAK,CAA2C;IACxD,8BAA8B;IAC9B,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,iBAAiB,CAAC,CAAS;gBAEvB,OAAO,EAAE,mBAAmB;IAOxC,4CAA4C;IAC5C,QAAQ,IAAI,yBAAyB;IAIrC,qDAAqD;IACrD,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,aAAa,CAAC;IAsBzC;;;;OAIG;IACG,QAAQ,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAiGhE;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKpC,kEAAkE;IAClE,IAAI,mBAAmB,IACnB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GACvE,SAAS,CAQZ;CACF;AAED,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,202 @@
1
+ import { ChannelError } from '@krischoichoi/channel-core';
2
+ const EMPTY = {
3
+ status: 'unknown',
4
+ baseUrl: undefined,
5
+ botToken: undefined,
6
+ ilinkBotId: undefined,
7
+ userId: undefined,
8
+ pendingVerifyCode: false,
9
+ alreadyBound: false,
10
+ };
11
+ /** Map a raw upstream status (already validated) to a Channel AuthState. */
12
+ export function toChannelState(status) {
13
+ switch (status) {
14
+ case 'confirmed':
15
+ case 'binded_redirect':
16
+ return 'authenticated';
17
+ case 'expired':
18
+ case 'verify_code_blocked':
19
+ return 'failed';
20
+ case 'need_verifycode':
21
+ case 'scaned_but_redirect':
22
+ case 'scaned':
23
+ case 'wait':
24
+ return 'pending';
25
+ default:
26
+ return 'pending';
27
+ }
28
+ }
29
+ export class WeixinQrAuth {
30
+ client;
31
+ now;
32
+ expiresInMs;
33
+ localTokens;
34
+ /** The last known full-fidelity upstream state. */
35
+ state = { ...EMPTY };
36
+ /** Active challenge state. */
37
+ activeId;
38
+ qrcode;
39
+ startedAt = 0;
40
+ pendingVerifyCode;
41
+ constructor(options) {
42
+ this.client = options.client;
43
+ this.now = options.now ?? Date.now;
44
+ this.expiresInMs = options.expiresInMs ?? 3 * 60_000;
45
+ this.localTokens = options.localTokens;
46
+ }
47
+ /** Current full-fidelity internal state. */
48
+ getState() {
49
+ return { ...this.state };
50
+ }
51
+ /** Whether the last known state is authenticated. */
52
+ get isAuthenticated() {
53
+ return this.state.status === 'confirmed' || this.state.status === 'binded_redirect';
54
+ }
55
+ /**
56
+ * Begin QR auth: fetch a fresh QR challenge from the iLink client.
57
+ * Returns the Channel Contract {@link AuthChallenge}.
58
+ */
59
+ async beginAuth() {
60
+ const started = this.now();
61
+ const qr = await this.client.getBotQrcode({ localTokens: this.localTokens });
62
+ // Reset internal machine for a fresh challenge.
63
+ this.state = { ...EMPTY };
64
+ this.activeId = `weixin-qr-${started}`;
65
+ this.qrcode = qr.qrcode;
66
+ this.startedAt = started;
67
+ this.pendingVerifyCode = undefined;
68
+ if (!qr.qrcode && !qr.qrcode_img_content) {
69
+ throw new ChannelError('CHANNEL_AUTH_FAILED', 'weixin QR login: server returned no qrcode');
70
+ }
71
+ return {
72
+ id: this.activeId,
73
+ instruction: '请使用微信扫描二维码',
74
+ qrUrl: qr.qrcode_img_content,
75
+ expiresAt: started + this.expiresInMs,
76
+ };
77
+ }
78
+ /**
79
+ * Poll the QR status. On `scaned_but_redirect`, updates the client baseUrl
80
+ * from `redirect_host`. On `confirmed`, records token/botId/baseUrl/userId
81
+ * and returns the applicable Channel AuthStatePoll.
82
+ */
83
+ async pollAuth(challenge) {
84
+ if (this.activeId !== challenge.id || !this.qrcode) {
85
+ throw new ChannelError('CHANNEL_AUTH_FAILED', 'weixin QR auth: poll used a stale/invalid challenge');
86
+ }
87
+ if (challenge.expiresAt !== undefined && this.now() > challenge.expiresAt) {
88
+ this.state.status = 'expired';
89
+ return { state: 'expired', detail: 'weixin QR code expired' };
90
+ }
91
+ let response;
92
+ try {
93
+ response = await this.client.getQrcodeStatus(this.qrcode, {
94
+ verifyCode: this.pendingVerifyCode,
95
+ });
96
+ }
97
+ catch (error) {
98
+ // getQrcodeStatus already maps long-poll aborts/timeouts to `wait`, so
99
+ // a throw here is a genuine protocol/network error — surface as failed.
100
+ this.state.status = 'unknown';
101
+ return {
102
+ state: 'failed',
103
+ detail: error instanceof Error ? error.message : String(error),
104
+ };
105
+ }
106
+ const status = (response.status ?? 'wait');
107
+ this.state.status = status;
108
+ switch (status) {
109
+ case 'scaned_but_redirect': {
110
+ if (response.redirect_host) {
111
+ const newBase = `https://${response.redirect_host}`;
112
+ try {
113
+ this.client.setBaseUrl(newBase);
114
+ this.state.baseUrl = this.client.baseUrl;
115
+ }
116
+ catch {
117
+ this.state.status = 'failed';
118
+ return { state: 'failed', detail: 'weixin QR redirect returned an untrusted iLink host' };
119
+ }
120
+ }
121
+ return { state: 'pending', detail: 'weixin QR scanned; redirecting to IDC host' };
122
+ }
123
+ case 'need_verifycode': {
124
+ this.state.pendingVerifyCode = this.state.pendingVerifyCode === undefined ? true : undefined;
125
+ return { state: 'pending', detail: 'weixin QR requires a phone-verified code' };
126
+ }
127
+ case 'scaned': {
128
+ // After a verify code is accepted, the status may return to scaned.
129
+ if (this.pendingVerifyCode)
130
+ this.pendingVerifyCode = undefined;
131
+ return { state: 'pending', detail: 'weixin QR scanned; waiting for confirmation' };
132
+ }
133
+ case 'confirmed': {
134
+ if (!response.bot_token || !response.ilink_bot_id) {
135
+ this.state.status = 'failed';
136
+ return { state: 'failed', detail: 'weixin QR confirmed but server omitted bot identity' };
137
+ }
138
+ this.state.botToken = response.bot_token;
139
+ this.state.ilinkBotId = response.ilink_bot_id;
140
+ this.state.userId = response.ilink_user_id;
141
+ this.state.alreadyBound = false;
142
+ if (response.baseurl) {
143
+ try {
144
+ this.client.setBaseUrl(response.baseurl);
145
+ this.state.baseUrl = this.client.baseUrl;
146
+ }
147
+ catch {
148
+ this.state.status = 'failed';
149
+ return { state: 'failed', detail: 'weixin QR confirmation returned an untrusted iLink host' };
150
+ }
151
+ }
152
+ else if (!this.client.baseUrl) {
153
+ this.state.baseUrl = this.client.baseUrl;
154
+ }
155
+ return { state: 'authenticated', detail: 'weixin QR confirmed and authenticated' };
156
+ }
157
+ case 'binded_redirect': {
158
+ this.state.alreadyBound = true;
159
+ if (response.redirect_host) {
160
+ const newBase = `https://${response.redirect_host}`;
161
+ try {
162
+ this.client.setBaseUrl(newBase);
163
+ this.state.baseUrl = this.client.baseUrl;
164
+ }
165
+ catch {
166
+ this.state.status = 'failed';
167
+ return { state: 'failed', detail: 'weixin QR binding returned an untrusted iLink host' };
168
+ }
169
+ }
170
+ return { state: 'authenticated', detail: 'weixin bot already bound; existing credentials remain valid' };
171
+ }
172
+ case 'expired':
173
+ return { state: 'expired', detail: 'weixin QR code expired' };
174
+ case 'verify_code_blocked':
175
+ this.state.pendingVerifyCode = undefined;
176
+ return { state: 'failed', detail: 'weixin QR verify code blocked after repeated failures' };
177
+ case 'wait':
178
+ default:
179
+ return { state: 'pending', detail: 'awaiting WeChat scan' };
180
+ }
181
+ }
182
+ /**
183
+ * Submit a phone-verify code (from the ChannelService/CLI — never stdin
184
+ * inside the adapter). Clears any previously pending code.
185
+ */
186
+ submitVerifyCode(code) {
187
+ this.pendingVerifyCode = code;
188
+ this.state.pendingVerifyCode = true;
189
+ }
190
+ /** The credential-ready result produced by a `confirmed` poll. */
191
+ get confirmedCredential() {
192
+ if (this.state.status !== 'confirmed' || !this.state.botToken || !this.state.ilinkBotId)
193
+ return undefined;
194
+ return {
195
+ token: this.state.botToken,
196
+ ilinkBotId: this.state.ilinkBotId,
197
+ userId: this.state.userId,
198
+ baseUrl: this.state.baseUrl ?? this.client.baseUrl,
199
+ };
200
+ }
201
+ }
202
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Weixin adapter configuration (Schemastery).
3
+ *
4
+ * Per doc section 29 — the channels-weixin config exposes iLink base URLs,
5
+ * network timeouts and reconnect policy. Credentials are NEVER configured in
6
+ * YAML; they live in the read/write credential store resolved at start.
7
+ */
8
+ import Schema from '@deepseek-ai/schemastery';
9
+ import type { Volatile } from '@deepseek-ai/cordis';
10
+ export interface WeixinIlinkConfig {
11
+ /** API base URL; may be redirected per-account after QR login. */
12
+ baseUrl: string;
13
+ /** CDN base URL for media. */
14
+ cdnBaseUrl: string;
15
+ /** bot_agent value sent in base_info. */
16
+ botAgent?: string;
17
+ }
18
+ export interface WeixinNetworkConfig {
19
+ /** Per-request timeout in ms. */
20
+ timeoutMs: number;
21
+ /** Initial getUpdates long-poll timeout in ms. */
22
+ longPollTimeoutMs: number;
23
+ }
24
+ export interface WeixinReconnectConfig {
25
+ enabled: boolean;
26
+ baseDelayMs: number;
27
+ maxDelayMs: number;
28
+ }
29
+ export interface WeixinConfig {
30
+ enabled: boolean;
31
+ /** Local DSH account alias; default 'main'. */
32
+ accountId: string;
33
+ ilink: WeixinIlinkConfig;
34
+ network: WeixinNetworkConfig;
35
+ reconnect: WeixinReconnectConfig;
36
+ }
37
+ /**
38
+ * Activation-time config shape (Harness 0.2.0): volatile fields resolve to a
39
+ * `Volatile<T>` live handle instead of a plain value. `apply()` unwraps them
40
+ * once via `resolveVolatileConfig` into a plain `WeixinConfig`. Weixin has no
41
+ * runtime-writable config beyond the enabled intent, so `enabled` is the only
42
+ * volatile field.
43
+ */
44
+ export interface WeixinConfigLive {
45
+ enabled: Volatile<boolean>;
46
+ accountId: string;
47
+ ilink: WeixinIlinkConfig;
48
+ network: WeixinNetworkConfig;
49
+ reconnect: WeixinReconnectConfig;
50
+ }
51
+ export declare const Config: Schema<WeixinConfigLive>;
52
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;CAClC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,iBAAiB,CAAC;IACzB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,qBAAqB,CAAC;CAClC;AAMD,eAAO,MAAM,MAAM,EAqBF,MAAM,CAAC,gBAAgB,CAAC,CAAC"}
package/lib/config.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Weixin adapter configuration (Schemastery).
3
+ *
4
+ * Per doc section 29 — the channels-weixin config exposes iLink base URLs,
5
+ * network timeouts and reconnect policy. Credentials are NEVER configured in
6
+ * YAML; they live in the read/write credential store resolved at start.
7
+ */
8
+ import Schema from '@deepseek-ai/schemastery';
9
+ import { DEFAULT_BASE_URL, DEFAULT_CDN_BASE_URL } from './ilink/constants.js';
10
+ // The runtime schema is genuine Schemastery (validation happens at the Loader
11
+ // boundary); the annotation records the activation-time output shape. The cast
12
+ // is required because Schemastery's inferred ObjectT models per-field optionality
13
+ // more precisely than the hand-written `WeixinConfigLive` alias.
14
+ export const Config = Schema.object({
15
+ // Harness 0.2.0 settings model: fields the control plane / settings page may
16
+ // write at runtime are declared `.volatile()` — `ctx.settings.update(
17
+ // entryId, patch)` accepts exactly these paths, and at activation they
18
+ // resolve to `Volatile<T>` handles (unwrapped by `resolveVolatileConfig`).
19
+ enabled: Schema.boolean().default(true).volatile(),
20
+ accountId: Schema.string().default('main'),
21
+ ilink: Schema.object({
22
+ baseUrl: Schema.string().default(DEFAULT_BASE_URL).description('Weixin iLink API base URL'),
23
+ cdnBaseUrl: Schema.string().default(DEFAULT_CDN_BASE_URL).description('Weixin CDN base URL'),
24
+ botAgent: Schema.string().description('bot_agent value for base_info'),
25
+ }),
26
+ network: Schema.object({
27
+ timeoutMs: Schema.natural().default(15000),
28
+ longPollTimeoutMs: Schema.natural().default(35000),
29
+ }),
30
+ reconnect: Schema.object({
31
+ enabled: Schema.boolean().default(true),
32
+ baseDelayMs: Schema.natural().default(2000),
33
+ maxDelayMs: Schema.natural().default(30000),
34
+ }),
35
+ });
36
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAgD9E,8EAA8E;AAC9E,+EAA+E;AAC/E,kFAAkF;AAClF,iEAAiE;AACjE,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,6EAA6E;IAC7E,sEAAsE;IACtE,uEAAuE;IACvE,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IAClD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC;QAC3F,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,WAAW,CAAC,qBAAqB,CAAC;QAC5F,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,+BAA+B,CAAC;KACvE,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;QAC1C,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;KACnD,CAAC;IACF,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACvC,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QAC3C,UAAU,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;KAC5C,CAAC;CACH,CAAwC,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Weixin ChannelDefinition — control-plane entry.
3
+ *
4
+ * Weixin has NO credentials/config fields: it mounts at startup (autoStart)
5
+ * and the QR login happens on the MOUNTED adapter. The definition therefore
6
+ * delegates beginAuth/pollAuth/submitAuthInput to the live adapter so the M1
7
+ * QR flow itself is byte-for-byte unchanged (same WeixinAdapter methods, same
8
+ * challenge/state shapes). Only the ENTRY moves to the control plane.
9
+ *
10
+ * getConfiguredState() always reports configured=true because there is nothing
11
+ * to configure — the auth session decides authenticity. The UI never parses
12
+ * free-form detail text: [toPublicStatus] derives a structured AuthPhase and
13
+ * prompt entirely inside this host definition (doc §16).
14
+ */
15
+ import { type ChannelAdapter, type AuthStatePoll } from '@krischoichoi/channel-core';
16
+ import type { AuthProviderSession, ChannelDefinition, PublicAuthStatus, PublicQrPayload } from '@krischoichoi/channel-control';
17
+ import type { WeixinConfig } from './config.js';
18
+ import { type WeixinAdapterDeps } from './adapter.js';
19
+ /** Options for [createWeixinDefinition]. */
20
+ export interface WeixinDefinitionOptions {
21
+ /** Resolved Weixin config (already validated by Schemastery). */
22
+ config: WeixinConfig;
23
+ /** Injectable adapter deps (tests). */
24
+ deps: WeixinAdapterDeps;
25
+ /**
26
+ * Resolve the currently mounted adapter (doc §43). The definition drives the
27
+ * M1 auth flow through this seam so behavior stays identical to headless.
28
+ */
29
+ getAdapter: () => ChannelAdapter | undefined;
30
+ /** Durable store for the enabled intent (doc §21) when the host provides one. */
31
+ persistEnabled?: (enabled: boolean) => Promise<void>;
32
+ /**
33
+ * Optional owner-identity resolver. Returns the canonical
34
+ * sender.id of the Weixin account owner (the scanning QR userId). When
35
+ * provided, the returned definition exposes `resolveOwnerIdentity` so the
36
+ * control plane can safely bootstrap an owner-only policy. The caller is
37
+ * responsible for mapping storage — never exposes platform credential keys.
38
+ */
39
+ resolveOwnerIdentity?: (accountId: string) => Promise<string | undefined>;
40
+ }
41
+ /**
42
+ * Map a raw adapter AuthStatePoll into a structured control-plane
43
+ * PublicAuthStatus (doc §16/§17). This is the ONLY place detail text is
44
+ * interpreted — the Web plane switches on .phase, never on .detail.
45
+ */
46
+ export declare function toPublicStatus(poll: AuthStatePoll, session: AuthProviderSession): PublicAuthStatus;
47
+ /**
48
+ * Wrap a QR login URL into a structured PublicQrPayload (doc §17). A ready-made
49
+ * data:image is passed through as 'data-url'; every other value is 'content'
50
+ * that the browser renders into a QR image (via QRCode.toDataURL).
51
+ */
52
+ export declare function toQrPayload(value: string, expiresAt?: number): PublicQrPayload;
53
+ /**
54
+ * Build the Weixin ChannelDefinition. Weixin has no config fields (setup
55
+ * fields = []) and only the QR auth method. All auth methods delegate to the
56
+ * mounted adapter via [options.getAdapter].
57
+ */
58
+ export declare function createWeixinDefinition(options: WeixinDefinitionOptions): ChannelDefinition;
59
+ //# sourceMappingURL=definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,cAAc,EAAsB,KAAK,aAAa,EAAyB,MAAM,4BAA4B,CAAC;AAChI,OAAO,KAAK,EAEV,mBAAmB,EACnB,iBAAiB,EAEjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAErE,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC,iEAAiE;IACjE,MAAM,EAAE,YAAY,CAAC;IACrB,uCAAuC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;OAGG;IACH,UAAU,EAAE,MAAM,cAAc,GAAG,SAAS,CAAC;IAC7C,iFAAiF;IACjF,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC3E;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,mBAAmB,GAC3B,gBAAgB,CAuBlB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,CAM9E;AAUD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,iBAAiB,CAiF1F"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Weixin ChannelDefinition — control-plane entry.
3
+ *
4
+ * Weixin has NO credentials/config fields: it mounts at startup (autoStart)
5
+ * and the QR login happens on the MOUNTED adapter. The definition therefore
6
+ * delegates beginAuth/pollAuth/submitAuthInput to the live adapter so the M1
7
+ * QR flow itself is byte-for-byte unchanged (same WeixinAdapter methods, same
8
+ * challenge/state shapes). Only the ENTRY moves to the control plane.
9
+ *
10
+ * getConfiguredState() always reports configured=true because there is nothing
11
+ * to configure — the auth session decides authenticity. The UI never parses
12
+ * free-form detail text: [toPublicStatus] derives a structured AuthPhase and
13
+ * prompt entirely inside this host definition (doc §16).
14
+ */
15
+ import { resolveVolatileConfig } from '@krischoichoi/channel-core';
16
+ import { ControlError } from '@krischoichoi/channel-control';
17
+ import { WeixinAdapter } from './adapter.js';
18
+ const AUTH_NOT_READY_MSG = 'weixin adapter is not mounted; start the channel first';
19
+ /**
20
+ * Map a raw adapter AuthStatePoll into a structured control-plane
21
+ * PublicAuthStatus (doc §16/§17). This is the ONLY place detail text is
22
+ * interpreted — the Web plane switches on .phase, never on .detail.
23
+ */
24
+ export function toPublicStatus(poll, session) {
25
+ if (poll.state === 'authenticated') {
26
+ return { state: 'authenticated', phase: 'authorized', expiresAt: session.expiresAt };
27
+ }
28
+ if (poll.state === 'expired') {
29
+ return { state: 'expired', phase: 'expired', expiresAt: session.expiresAt };
30
+ }
31
+ if (poll.state === 'failed') {
32
+ return { state: 'failed', phase: 'failed', expiresAt: session.expiresAt };
33
+ }
34
+ // pending — derive a structured phase + prompt from the adapter detail.
35
+ const detail = poll.detail ?? '';
36
+ let phase = 'waiting-scan';
37
+ let prompt;
38
+ if (/verify|code/i.test(detail)) {
39
+ phase = 'verification-required';
40
+ prompt = { kind: 'verification-code', message: detail };
41
+ }
42
+ else if (/confirm|redirect/i.test(detail)) {
43
+ phase = 'waiting-confirm';
44
+ prompt = { kind: 'confirm-on-phone', message: detail };
45
+ }
46
+ return { state: 'pending', phase, prompt, expiresAt: session.expiresAt, detail };
47
+ }
48
+ /**
49
+ * Wrap a QR login URL into a structured PublicQrPayload (doc §17). A ready-made
50
+ * data:image is passed through as 'data-url'; every other value is 'content'
51
+ * that the browser renders into a QR image (via QRCode.toDataURL).
52
+ */
53
+ export function toQrPayload(value, expiresAt) {
54
+ return {
55
+ kind: /^data:image\//i.test(value) ? 'data-url' : 'content',
56
+ value,
57
+ expiresAt,
58
+ };
59
+ }
60
+ /** Extract the stored AuthChallenge from a provider session's opaque state. */
61
+ function challengeOf(session) {
62
+ return session.providerState.challenge;
63
+ }
64
+ const POLL_INTERVAL_MS = 3000;
65
+ const DEFAULT_EXPIRES_MS = 3 * 60_000;
66
+ /**
67
+ * Build the Weixin ChannelDefinition. Weixin has no config fields (setup
68
+ * fields = []) and only the QR auth method. All auth methods delegate to the
69
+ * mounted adapter via [options.getAdapter].
70
+ */
71
+ export function createWeixinDefinition(options) {
72
+ const { config, deps, getAdapter } = options;
73
+ // Under Harness 0.2.0 volatile fields (enabled) arrive as `Volatile<T>`
74
+ // handles; unwrap them once so the definition sees plain values. Writes must
75
+ // NOT go through the config object — keep the live flag in a local mutable
76
+ // snapshot (mirrors qq/telegram's `snapshot.enabled`) and let
77
+ // persistEnabled() persist the intent through the settings forms.
78
+ const state = { enabled: resolveVolatileConfig(config).enabled };
79
+ const requireAdapter = () => getAdapter();
80
+ const pollCurrent = async (session) => {
81
+ const adapter = requireAdapter();
82
+ if (!adapter?.pollAuth) {
83
+ throw new ControlError('AUTH_NOT_READY', AUTH_NOT_READY_MSG);
84
+ }
85
+ const poll = await adapter.pollAuth(challengeOf(session));
86
+ return toPublicStatus(poll, session);
87
+ };
88
+ return {
89
+ id: 'weixin',
90
+ get enabled() {
91
+ return state.enabled;
92
+ },
93
+ async setEnabled(enabled) {
94
+ state.enabled = enabled;
95
+ await options.persistEnabled?.(enabled);
96
+ },
97
+ setup: {
98
+ fields: [],
99
+ authMethods: ['qr'],
100
+ },
101
+ getConfiguredState: async () => ({ configured: true, fields: {} }),
102
+ saveConfig: async () => {
103
+ // Nothing configurable — accept and ignore unknown keys (no-op).
104
+ },
105
+ beginAuth: async () => {
106
+ const adapter = requireAdapter();
107
+ if (!adapter?.beginAuth) {
108
+ throw new ControlError('AUTH_NOT_READY', AUTH_NOT_READY_MSG);
109
+ }
110
+ const challenge = await adapter.beginAuth();
111
+ return {
112
+ provider: 'weixin',
113
+ expiresAt: challenge.expiresAt ?? Date.now() + DEFAULT_EXPIRES_MS,
114
+ pollingIntervalMs: POLL_INTERVAL_MS,
115
+ qr: challenge.qrUrl ? toQrPayload(challenge.qrUrl, challenge.expiresAt) : undefined,
116
+ prompt: { kind: 'confirm-on-phone', message: challenge.instruction },
117
+ providerState: { challenge },
118
+ };
119
+ },
120
+ pollAuth: pollCurrent,
121
+ submitAuthInput: async (session, input) => {
122
+ const adapter = requireAdapter();
123
+ if (!adapter?.submitAuthInput) {
124
+ throw new ControlError('AUTH_NOT_READY', AUTH_NOT_READY_MSG);
125
+ }
126
+ await adapter.submitAuthInput(challengeOf(session), input);
127
+ return pollCurrent(session);
128
+ },
129
+ createAdapter: async () => new WeixinAdapter(config, deps),
130
+ autoStart: true,
131
+ // Declared access capability. Weixin is DM-only, no group
132
+ // mention activation in V1; owner is discovered from the account.
133
+ access: {
134
+ directMessages: true,
135
+ groups: false,
136
+ mentions: false,
137
+ ownerDiscovery: 'account',
138
+ identityLabels: { user: 'Weixin User ID' },
139
+ },
140
+ // Expose the optional owner-identity resolver wired by the plugin.
141
+ ...(options.resolveOwnerIdentity
142
+ ? { resolveOwnerIdentity: options.resolveOwnerIdentity }
143
+ : {}),
144
+ };
145
+ }
146
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Classification: A — DSH glue [keep].
3
+ *
4
+ * `base_info` payload builder. The official equivalent is api/api.js
5
+ * buildBaseInfo() but is OpenClaw coupled (reads bot_agent from OpenClaw config).
6
+ * DSH keeps its own neutral builder (DSH-specific bot_agent), which is pure
7
+ * glue, not protocol duplication.
8
+ */
9
+ /**
10
+ * `base_info` payload attached to every iLink request.
11
+ */
12
+ import type { ILinkBaseInfo } from './types.js';
13
+ export declare const DEFAULT_BOT_AGENT = "DeepSeekHarness";
14
+ export interface BuildBaseInfoOptions {
15
+ /** Optional channel/app version string. */
16
+ channelVersion?: string;
17
+ /** Optional bot agent override; defaults to `DeepSeekHarness`. */
18
+ botAgent?: string;
19
+ }
20
+ /** Build the `base_info` object sent with every iLink CGI request. */
21
+ export declare function buildBaseInfo(opts?: BuildBaseInfoOptions): ILinkBaseInfo;
22
+ //# sourceMappingURL=base-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-info.d.ts","sourceRoot":"","sources":["../../src/ilink/base-info.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAM5E"}