@huaweidevtools/aishell-dsh-credentials-store 0.0.1-beta.2 → 0.0.1-beta.4

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 (74) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +62 -14
  3. package/lib/accounts.d.ts +187 -0
  4. package/lib/accounts.d.ts.map +1 -0
  5. package/lib/accounts.js +298 -0
  6. package/lib/accounts.js.map +1 -0
  7. package/lib/index-CsXaUaRH.d.ts +211 -0
  8. package/lib/index-CsXaUaRH.d.ts.map +1 -0
  9. package/lib/index.d.ts +3 -208
  10. package/lib/index.js +26 -28
  11. package/lib/index.js.map +1 -1
  12. package/lib/paths-DtFRzop6.js +46 -0
  13. package/lib/paths-DtFRzop6.js.map +1 -0
  14. package/lib/paths.d.ts +56 -0
  15. package/lib/paths.d.ts.map +1 -0
  16. package/lib/paths.js +3 -0
  17. package/lib/scope-CFetnbG3.d.ts +71 -0
  18. package/lib/scope-CFetnbG3.d.ts.map +1 -0
  19. package/lib/scope-QmrHZaZt.js +96 -0
  20. package/lib/scope-QmrHZaZt.js.map +1 -0
  21. package/lib/scope.d.ts +2 -0
  22. package/lib/scope.js +3 -0
  23. package/lib/signer-BPR0UAF0.js +66 -0
  24. package/lib/signer-BPR0UAF0.js.map +1 -0
  25. package/lib/signer-Et0DIH6o.d.ts +40 -0
  26. package/lib/signer-Et0DIH6o.d.ts.map +1 -0
  27. package/lib/signer.d.ts +2 -40
  28. package/lib/signer.js +2 -65
  29. package/lib/types/accounts.d.ts +231 -0
  30. package/lib/types/accounts.d.ts.map +1 -0
  31. package/lib/types/accounts.js +384 -0
  32. package/lib/types/accounts.js.map +1 -0
  33. package/lib/types/backends/fingerprint.d.ts +41 -0
  34. package/lib/types/backends/fingerprint.d.ts.map +1 -0
  35. package/lib/types/backends/fingerprint.js +135 -0
  36. package/lib/types/backends/fingerprint.js.map +1 -0
  37. package/lib/types/backends/keyring.d.ts +69 -0
  38. package/lib/types/backends/keyring.d.ts.map +1 -0
  39. package/lib/types/backends/keyring.js +107 -0
  40. package/lib/types/backends/keyring.js.map +1 -0
  41. package/lib/types/backends/memory.d.ts +22 -0
  42. package/lib/types/backends/memory.d.ts.map +1 -0
  43. package/lib/types/backends/memory.js +35 -0
  44. package/lib/types/backends/memory.js.map +1 -0
  45. package/lib/types/failover.d.ts +136 -0
  46. package/lib/types/failover.d.ts.map +1 -0
  47. package/lib/types/failover.js +242 -0
  48. package/lib/types/failover.js.map +1 -0
  49. package/lib/types/index.d.ts +70 -0
  50. package/lib/types/index.d.ts.map +1 -0
  51. package/lib/types/index.js +73 -0
  52. package/lib/types/index.js.map +1 -0
  53. package/lib/types/paths.d.ts +50 -0
  54. package/lib/types/paths.d.ts.map +1 -0
  55. package/lib/types/paths.js +59 -0
  56. package/lib/types/paths.js.map +1 -0
  57. package/lib/types/scope.d.ts +65 -0
  58. package/lib/types/scope.d.ts.map +1 -0
  59. package/lib/types/scope.js +102 -0
  60. package/lib/types/scope.js.map +1 -0
  61. package/lib/types/signer.d.ts +36 -0
  62. package/lib/types/signer.d.ts.map +1 -0
  63. package/lib/types/signer.js +81 -0
  64. package/lib/types/signer.js.map +1 -0
  65. package/lib/types/verify.d.ts +114 -0
  66. package/lib/types/verify.d.ts.map +1 -0
  67. package/lib/types/verify.js +240 -0
  68. package/lib/types/verify.js.map +1 -0
  69. package/lib/verify.d.ts +72 -0
  70. package/lib/verify.d.ts.map +1 -0
  71. package/lib/verify.js +172 -0
  72. package/lib/verify.js.map +1 -0
  73. package/package.json +21 -8
  74. package/lib/index.d.ts.map +0 -1
package/lib/signer.d.ts CHANGED
@@ -1,40 +1,2 @@
1
- //#region src/signer.d.ts
2
- /**
3
- * Huawei Cloud SDK-HMAC-SHA256 request signing.
4
- *
5
- * Signs an HTTP request with AK/SK (and an optional temporary SecurityToken)
6
- * using the same algorithm as the official Huawei Cloud SDK. Produces the
7
- * `Authorization`, `x-sdk-date`, and `x-security-token` headers to attach
8
- * to a `fetch` call.
9
- *
10
- * @module @huaweidevtools/aishell-dsh-credentials-store/signer
11
- */
12
- /** Credentials for signing. */
13
- interface SignCredentials {
14
- readonly ak: string;
15
- readonly sk: string;
16
- readonly securityToken?: string;
17
- }
18
- /** The headers produced by {@link sign}. */
19
- interface SignedHeaders {
20
- readonly host: string;
21
- readonly 'x-sdk-date': string;
22
- readonly Authorization: string;
23
- readonly 'x-security-token'?: string;
24
- }
25
- /**
26
- * Sign an HTTP request with SDK-HMAC-SHA256.
27
- *
28
- * @param method - HTTP method (GET, POST, ...).
29
- * @param host - Host component of the request URL (e.g. `api.aishell.openbuildlab.com`).
30
- * @param path - Path component of the request URL (e.g. `/chat/completions`).
31
- * @param query - Query parameters as a key-value map.
32
- * @param body - Request body (stringified for hashing; empty string for GET).
33
- * @param credentials - AK/SK and optional SecurityToken.
34
- * @returns The headers to attach to the fetch call.
35
- */
36
- declare function sign(method: string, host: string, path: string, query: Record<string, string>, body: string, credentials: SignCredentials): SignedHeaders; //#endregion
37
-
38
- //# sourceMappingURL=signer.d.ts.map
39
- export { SignCredentials, SignedHeaders, sign };
40
- //# sourceMappingURL=signer.d.ts.map
1
+ import { SignCredentials, SignedHeaders, sign$1 as sign } from "./signer-Et0DIH6o.js";
2
+ export { SignCredentials, SignedHeaders, sign };
package/lib/signer.js CHANGED
@@ -1,66 +1,3 @@
1
- import crypto from "node:crypto";
1
+ import { sign } from "./signer-BPR0UAF0.js";
2
2
 
3
- //#region src/signer.ts
4
- function sha256Hex(data) {
5
- return crypto.createHash("sha256").update(data).digest("hex");
6
- }
7
- function hmacSha256(key, data) {
8
- return crypto.createHmac("sha256", key).update(data).digest("hex");
9
- }
10
- /** RFC 3986 URL-encode a string (unreserved chars stay literal). */
11
- function urlEncode(str) {
12
- const hex = (c) => "%" + (c < 16 ? "0" : "") + c.toString(16).toUpperCase();
13
- const noEscape = new Set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~".split(""));
14
- let out = "";
15
- for (const ch of str) {
16
- const c = ch.codePointAt(0);
17
- if (c < 128) out += noEscape.has(ch) ? ch : hex(c);
18
- else out += encodeURIComponent(ch);
19
- }
20
- return out;
21
- }
22
- /** ISO 8601 timestamp in Huawei Cloud format: `20260912T064552Z`. */
23
- function sdkTimestamp() {
24
- return new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d+/, "");
25
- }
26
- /**
27
- * Sign an HTTP request with SDK-HMAC-SHA256.
28
- *
29
- * @param method - HTTP method (GET, POST, ...).
30
- * @param host - Host component of the request URL (e.g. `api.aishell.openbuildlab.com`).
31
- * @param path - Path component of the request URL (e.g. `/chat/completions`).
32
- * @param query - Query parameters as a key-value map.
33
- * @param body - Request body (stringified for hashing; empty string for GET).
34
- * @param credentials - AK/SK and optional SecurityToken.
35
- * @returns The headers to attach to the fetch call.
36
- */
37
- function sign(method, host, path, query, body, credentials) {
38
- const ts = sdkTimestamp();
39
- const cqs = Object.entries(query).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${urlEncode(k)}=${urlEncode(v)}`).join("&");
40
- const curi = "/" + path.split("/").filter(Boolean).map(urlEncode).join("/") + "/";
41
- const hasToken = credentials.securityToken !== void 0;
42
- const signedHeaders = hasToken ? "host;x-sdk-date;x-security-token" : "host;x-sdk-date";
43
- const canonicalHeaders = hasToken ? `host:${host}\nx-sdk-date:${ts}\nx-security-token:${credentials.securityToken}\n` : `host:${host}\nx-sdk-date:${ts}\n`;
44
- const payloadHash = sha256Hex(body);
45
- const canonicalRequest = [
46
- method,
47
- curi,
48
- cqs,
49
- canonicalHeaders,
50
- signedHeaders,
51
- payloadHash
52
- ].join("\n");
53
- const stringToSign = `SDK-HMAC-SHA256\n${ts}\n${sha256Hex(canonicalRequest)}`;
54
- const signature = hmacSha256(credentials.sk, stringToSign);
55
- const headers = {
56
- host,
57
- "x-sdk-date": ts,
58
- Authorization: `SDK-HMAC-SHA256 Access=${credentials.ak}, SignedHeaders=${signedHeaders}, Signature=${signature}`,
59
- ...hasToken ? { "x-security-token": credentials.securityToken } : {}
60
- };
61
- return headers;
62
- }
63
-
64
- //#endregion
65
- export { sign };
66
- //# sourceMappingURL=signer.js.map
3
+ export { sign };
@@ -0,0 +1,231 @@
1
+ /**
2
+ * The Huawei Cloud credential vocabulary every plugin in this set shares.
3
+ *
4
+ * ## Why this module exists
5
+ *
6
+ * This repo once read the same keyring entries from four places, each declaring
7
+ * the account names itself and re-implementing "read AK and SK as a pair". The
8
+ * account names are a *persistence* contract, so a divergence would not fail a
9
+ * build — it would surface as one plugin working and another reporting "not
10
+ * authenticated" on a machine where the person had stored exactly one pair, and
11
+ * the cause would be invisible in the diff that introduced it.
12
+ *
13
+ * Everything now goes through this file.
14
+ *
15
+ * ## One entry, not three
16
+ *
17
+ * The store holds **one** record, `aishell-credential`, whose value is a single
18
+ * JSON bundle encoded as base64:
19
+ *
20
+ * ```json
21
+ * {
22
+ * "huaweicloud": { "access_key": "…", "secret_key": "…", "security_token": "…" },
23
+ * "aishell_e2b": { "api_key": "…", "expired_at": "…", "ak_fingerprint": "…" }
24
+ * }
25
+ * ```
26
+ *
27
+ * The earlier layout was three separate entries (`HW_ACCESS_KEY`,
28
+ * `HW_SECRET_KEY`, `HW_SECURITY_TOKEN`). That had two problems this fixes:
29
+ *
30
+ * - **A save was four operations.** Clear, then write AK, then SK, then
31
+ * token. Every backend is per-entry, so a failure between them left a
32
+ * half-set behind, and nothing rolled it back.
33
+ * - **A read was three reads.** A concurrent write landing between them could
34
+ * hand back a new AK with an old SK — a pair that passes "both present" and
35
+ * then fails at the gateway with 401, pointing nowhere near the cause.
36
+ *
37
+ * One entry makes a write one atomic operation and a read one consistent
38
+ * snapshot.
39
+ *
40
+ * ## base64 is an encoding, not encryption
41
+ *
42
+ * It keeps the secret from sitting in a plaintext field a person or a log
43
+ * prints while poking at the store. It is **not** a security boundary: anyone
44
+ * who can read the entry can decode it. The actual protection is the backend —
45
+ * the OS keyring, or AES-256-GCM in the file backend.
46
+ *
47
+ * ## Read-modify-write is not atomic across processes
48
+ *
49
+ * {@link updateCredentials} reads the bundle, applies a change and writes it
50
+ * back. Two processes doing that concurrently can lose one of the changes. That
51
+ * is accepted rather than solved: the only writer is a person via the
52
+ * credential form, and both the e2b key and the AK/SK are refreshed by that
53
+ * same person. A lost update would be corrected by the next save. Solving it
54
+ * properly needs compare-and-swap, which the keyring API does not offer.
55
+ *
56
+ * @module @huaweidevtools/aishell-dsh-credentials-store/accounts
57
+ */
58
+ import type { CredentialsStore } from './index.ts';
59
+ import type { SignCredentials } from './signer.ts';
60
+ /**
61
+ * The one store entry holding every credential this repo manages.
62
+ *
63
+ * Part of the persistence contract: renaming it orphans every stored entry
64
+ * while looking like a harmless constant change.
65
+ */
66
+ export declare const CREDENTIAL_ACCOUNT = "aishell-credential";
67
+ /**
68
+ * Entries written by the earlier three-key layout, in the order they are read
69
+ * during migration.
70
+ *
71
+ * Kept only so an existing install keeps working: see {@link readCredentials}.
72
+ * New writes never touch these.
73
+ */
74
+ export declare const LEGACY_ACCOUNTS: readonly ["HW_ACCESS_KEY", "HW_SECRET_KEY", "HW_SECURITY_TOKEN"];
75
+ /**
76
+ * The legacy entry holding an e2b API key on its own.
77
+ *
78
+ * Folded into the bundle by {@link readCredentials}. Kept as a constant because
79
+ * the migration has to name it.
80
+ */
81
+ export declare const LEGACY_E2B_ACCOUNT = "AISHELL_E2B_API_KEY";
82
+ /** The Huawei Cloud pair, as the bundle stores it. Snake-case on disk. */
83
+ export interface HuaweiCloudSection {
84
+ readonly access_key: string;
85
+ readonly secret_key: string;
86
+ readonly security_token?: string;
87
+ }
88
+ /**
89
+ * An e2b API key, as the bundle stores it.
90
+ *
91
+ * `ak_fingerprint` is not part of the key the gateway issued — it is a digest
92
+ * of the AK that minted it, so a reader can tell whether the key still belongs
93
+ * to the credentials in hand. Without it, an AK rotation would leave the old
94
+ * key (and so the old tenant, with all its sandboxes) alive. It lives here
95
+ * rather than only in the reader's memory because it has to survive a restart.
96
+ */
97
+ export interface E2BSection {
98
+ readonly api_key: string;
99
+ /** ISO 8601; `9999-12-31T23:59:59.000Z` when the key does not expire. */
100
+ readonly expired_at: string;
101
+ /** {@link fingerprint} of the AK that minted this key, when known. */
102
+ readonly ak_fingerprint?: string;
103
+ }
104
+ /** The whole bundle. Either section may be absent. */
105
+ export interface CredentialBundle {
106
+ readonly huaweicloud?: HuaweiCloudSection;
107
+ readonly aishell_e2b?: E2BSection;
108
+ }
109
+ /**
110
+ * Encode a bundle for storage.
111
+ *
112
+ * @param bundle - the bundle to store.
113
+ * @returns base64 of its JSON form.
114
+ */
115
+ export declare function encodeBundle(bundle: CredentialBundle): string;
116
+ /**
117
+ * Decode a stored value.
118
+ *
119
+ * Anything unreadable reads as an empty bundle rather than throwing: a corrupt
120
+ * entry should behave like "nothing stored" so the person is asked to
121
+ * authenticate again, not so every tool call fails on a parse error.
122
+ *
123
+ * A value that is not base64 is tried as raw JSON first — that is what the
124
+ * legacy e2b entry held, and migration reads it through here.
125
+ *
126
+ * @param raw - the stored string, if any.
127
+ * @returns the decoded bundle; empty when nothing usable is there.
128
+ */
129
+ export declare function decodeBundle(raw: string | undefined): CredentialBundle;
130
+ /**
131
+ * Read the whole bundle, migrating a legacy layout on the way.
132
+ *
133
+ * Migration is lazy and idempotent: the first read that finds no bundle but
134
+ * finds legacy entries merges them into one record, writes it, and removes the
135
+ * old entries. A failed write leaves the legacy entries alone, so the next read
136
+ * tries again — the person is never left with neither layout.
137
+ *
138
+ * @param store - the caller's credential store.
139
+ * @returns the bundle; empty when nothing is stored.
140
+ */
141
+ export declare function readCredentials(store: CredentialsStore): CredentialBundle;
142
+ /** The expiry recorded for a key that does not expire. */
143
+ export declare const FOREVER_EXPIRY = "9999-12-31T23:59:59.000Z";
144
+ /**
145
+ * Read the Huawei Cloud pair.
146
+ *
147
+ * AK and SK are read together and treated as one unit: half a pair reads as no
148
+ * credentials rather than an error, so a partially-configured machine gets
149
+ * "authenticate first" instead of a signing failure with no remedy. The
150
+ * SecurityToken is optional and is omitted entirely when absent — an empty
151
+ * string would sign as a third header the gateway then rejects.
152
+ *
153
+ * @param store - the caller's credential store.
154
+ * @returns the pair, or `undefined` when the AK/SK pair is incomplete.
155
+ */
156
+ export declare function readStoredPair(store: CredentialsStore): SignCredentials | undefined;
157
+ /**
158
+ * Whether a complete Huawei Cloud pair is stored.
159
+ *
160
+ * @param store - the caller's credential store.
161
+ */
162
+ export declare function hasStoredPair(store: CredentialsStore): boolean;
163
+ /**
164
+ * Read the stored e2b key.
165
+ *
166
+ * @param store - the caller's credential store.
167
+ * @returns the section, or `undefined` when no key is stored.
168
+ */
169
+ export declare function readE2BKey(store: CredentialsStore): E2BSection | undefined;
170
+ /**
171
+ * Apply one change to the bundle and write it back.
172
+ *
173
+ * The updater receives the current bundle and returns the next one. Returning
174
+ * `undefined` aborts the write, which is how a caller says "nothing changed" —
175
+ * that matters because every write is a full re-encode, and needlessly
176
+ * rewriting a record while another process reads it is how a spurious
177
+ * "credentials changed" is produced.
178
+ *
179
+ * @param store - the caller's credential store.
180
+ * @param update - computes the next bundle.
181
+ * @returns the bundle now stored.
182
+ */
183
+ export declare function updateCredentials(store: CredentialsStore, update: (current: CredentialBundle) => CredentialBundle | undefined): CredentialBundle;
184
+ /**
185
+ * Store the Huawei Cloud pair, replacing any previous one.
186
+ *
187
+ * One write, so the pair can never be left half-replaced. A save without a
188
+ * token removes a previously stored one: a token belongs to the credential set
189
+ * that carried it, and inheriting a stale one would sign the next request with
190
+ * someone else's session.
191
+ *
192
+ * @param store - the caller's credential store.
193
+ * @param credentials - the verified pair.
194
+ */
195
+ export declare function writeHuaweiCloud(store: CredentialsStore, credentials: SignCredentials): void;
196
+ /**
197
+ * Store an e2b key.
198
+ *
199
+ * @param store - the caller's credential store.
200
+ * @param key - the section to store.
201
+ */
202
+ export declare function writeE2BKey(store: CredentialsStore, key: E2BSection): void;
203
+ /**
204
+ * Remove the stored e2b key, leaving the Huawei Cloud pair alone.
205
+ *
206
+ * @param store - the caller's credential store.
207
+ */
208
+ export declare function clearE2BKey(store: CredentialsStore): void;
209
+ /**
210
+ * Remove every credential this repo writes.
211
+ *
212
+ * The whole record goes, so a clear cannot leave a token or a key behind for
213
+ * the next pair to inherit. Deleting an absent entry is a no-op in every
214
+ * backend, and the legacy entries are removed too so a half-migrated store does
215
+ * not come back.
216
+ *
217
+ * @param store - the caller's credential store.
218
+ */
219
+ export declare function clearCredentials(store: CredentialsStore): void;
220
+ /**
221
+ * A short, stable digest of an AK, for recording which identity minted a key.
222
+ *
223
+ * The AK rather than the pair: the SK can be rotated independently while the
224
+ * account stays the same, and the tenant the gateway derives is the account. A
225
+ * digest rather than the AK itself keeps the bundle from carrying a second
226
+ * readable copy of a credential identifier.
227
+ *
228
+ * @param ak - the access key.
229
+ */
230
+ export declare function fingerprint(ak: string): string;
231
+ //# sourceMappingURL=accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,kEAAmE,CAAA;AAE/F;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,wBAAwB,CAAA;AAEvD,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,UAAU,CAAA;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAE7D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAoBtE;AAoCD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAOzE;AAsED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,6BAA6B,CAAA;AAExD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,eAAe,GAAG,SAAS,CAQnF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,UAAU,GAAG,SAAS,CAE1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,gBAAgB,GAAG,SAAS,GAClE,gBAAgB,CAMlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,GAAG,IAAI,CAS5F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,CAE1E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAMzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAI9D;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9C"}