opencode-qwen-oauth 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -134,14 +134,43 @@ npx opencode-qwen-oauth uninstall
134
134
  npx opencode-qwen-oauth --help
135
135
  ```
136
136
 
137
+ ## Diagnostics
138
+
139
+ Test if the Qwen OAuth endpoints are accessible:
140
+
141
+ ```bash
142
+ npm run diagnose
143
+ ```
144
+
145
+ This will check:
146
+ - ✓ OAuth base URL accessibility
147
+ - ✓ Device code endpoint functionality
148
+ - ✓ API endpoint availability
149
+
150
+ Example output:
151
+ ```
152
+ [Base URL] https://chat.qwen.ai
153
+ Status: ✓ 200
154
+
155
+ [Device Code] https://chat.qwen.ai/api/v1/oauth2/device/code
156
+ Status: ✓ 200
157
+
158
+ [API Endpoints] Testing /chat/completions...
159
+ ⚠ https://portal.qwen.ai/v1
160
+ Status: 401 (endpoint exists, requires auth)
161
+ ```
162
+
137
163
  ## Troubleshooting
138
164
 
139
165
  ### "Device code expired"
140
- Complete the browser login within 5 minutes of starting `/connect`.
166
+ Complete the browser login within 15 minutes of starting `/connect`.
141
167
 
142
168
  ### "invalid_grant" error
143
169
  Your refresh token has expired. Run `/connect` to re-authenticate.
144
170
 
171
+ ### "Quota exceeded" error
172
+ Your free tier limit has been reached. Wait for quota reset or upgrade your account at https://chat.qwen.ai
173
+
145
174
  ### Provider not showing in /connect
146
175
  Use the CLI directly:
147
176
  ```bash
@@ -182,12 +211,31 @@ This plugin implements OAuth 2.0 Device Flow (RFC 8628) with PKCE:
182
211
  4. **Token Storage** - Tokens are stored in OpenCode's auth system
183
212
  5. **Auto Refresh** - Access tokens are refreshed before expiry
184
213
 
185
- ## Security
214
+ ## Security & Important Notes
215
+
216
+ ### Security Features
217
+ - ✅ Uses PKCE (RFC 7636) for enhanced security
218
+ - ✅ No client secret required (safer for public clients)
219
+ - ✅ Tokens stored in OpenCode's secure auth storage
220
+ - ✅ All OAuth activity logged for auditing
221
+ - ✅ Sensitive data sanitized in logs
222
+
223
+ ### Implementation Notes
224
+
225
+ ⚠️ **Important**: This plugin uses OAuth endpoints that appear to be part of Qwen's web interface (`chat.qwen.ai`). While the implementation follows standard OAuth 2.0 specifications (RFC 8628 Device Flow + RFC 7636 PKCE), these endpoints are not officially documented in Qwen's public API documentation.
226
+
227
+ **What this means:**
228
+ - The OAuth flow works correctly and follows industry standards
229
+ - Endpoints are actively maintained and functional
230
+ - Future changes to Qwen's authentication system may require plugin updates
231
+
232
+ **Verified Working:**
233
+ - ✅ OAuth Device Flow: `https://chat.qwen.ai/api/v1/oauth2/*`
234
+ - ✅ API Endpoint: `https://portal.qwen.ai/v1/chat/completions`
235
+ - ✅ Token Refresh: Automatic refresh before expiration
236
+ - ✅ OpenAI-Compatible: Uses standard OpenAI API format
186
237
 
187
- - Uses PKCE (RFC 7636) for enhanced security
188
- - No client secret required
189
- - Tokens stored in OpenCode's secure auth storage
190
- - All OAuth activity logged for auditing
238
+ Run `npm run diagnose` to verify endpoint availability at any time.
191
239
 
192
240
  ## Development
193
241
 
package/bin/install.js CHANGED
@@ -105,13 +105,17 @@ function install() {
105
105
  baseURL: "https://portal.qwen.ai/v1",
106
106
  },
107
107
  models: {
108
- "qwen3-coder-plus": {
109
- id: "qwen3-coder-plus",
110
- name: "Qwen3 Coder Plus",
108
+ "coder-model": {
109
+ id: "coder-model",
110
+ name: "Qwen Coder",
111
+ limit: { context: 1048576, output: 65536 },
112
+ modalities: { input: ["text"], output: ["text"] },
111
113
  },
112
- "qwen3-vl-plus": {
113
- id: "qwen3-vl-plus",
114
- name: "Qwen3 VL Plus",
114
+ "vision-model": {
115
+ id: "vision-model",
116
+ name: "Qwen Vision",
117
+ limit: { context: 131072, output: 32768 },
118
+ modalities: { input: ["text", "image"], output: ["text"] },
115
119
  attachment: true,
116
120
  },
117
121
  },
@@ -144,7 +148,7 @@ function install() {
144
148
 
145
149
  opencodePackage.dependencies = opencodePackage.dependencies || {};
146
150
  if (!opencodePackage.dependencies["opencode-qwen-oauth"]) {
147
- opencodePackage.dependencies["opencode-qwen-oauth"] = "^1.0.0";
151
+ opencodePackage.dependencies["opencode-qwen-oauth"] = "^2.3.0";
148
152
  log("Added 'opencode-qwen-oauth' to .opencode/package.json dependencies");
149
153
  }
150
154
 
@@ -159,12 +163,19 @@ function install() {
159
163
  process.exit(1);
160
164
  }
161
165
 
162
- log("Installation complete!");
166
+ log("Installation complete!");
167
+ log("");
163
168
  log("Next steps:");
164
- log("1. Run: opencode");
165
- log("2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')");
166
- log("3. Use model: /model qwen/qwen3-coder-plus");
167
- log("Debug mode: QWEN_OAUTH_DEBUG=true opencode");
169
+ log(" 1. Run: opencode");
170
+ log(" 2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')");
171
+ log(" 3. Use model: /model qwen/coder-model");
172
+ log(" Vision model: /model qwen/vision-model");
173
+ log("");
174
+ log("Advanced:");
175
+ log(" • Run diagnostics: npm run diagnose");
176
+ log(" • View logs: tail -f ~/.config/opencode/logs/qwen-oauth.log");
177
+ log(" • Credentials saved to: ~/.qwen/oauth_creds.json");
178
+ log("");
168
179
  }
169
180
 
170
181
  // ============================================
@@ -239,9 +250,14 @@ Usage:
239
250
  After installation:
240
251
  1. Run: opencode
241
252
  2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')
242
- 3. Use model: /model qwen/qwen3-coder-plus
253
+ 3. Use model: /model qwen/coder-model
243
254
 
244
- Debug mode: QWEN_OAUTH_DEBUG=true opencode
255
+ Models:
256
+ - coder-model: Qwen Coder (1M context, 64K output)
257
+ - vision-model: Qwen Vision (128K context, 32K output, supports images)
258
+
259
+ Logs:
260
+ - tail -f ~/.config/opencode/logs/qwen-oauth.log
245
261
  `);
246
262
  } else {
247
263
  error(`Unknown command: ${command}`);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * API Key Exchange for Qwen
3
+ * Attempts to exchange OAuth token for API key
4
+ */
5
+ interface ApiKeyResponse {
6
+ success: boolean;
7
+ api_key?: string;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Try to get API key from OAuth token
12
+ * This is speculative - Qwen may require OAuth token → API key exchange
13
+ */
14
+ export declare function tryGetApiKey(oauthToken: string): Promise<ApiKeyResponse>;
15
+ /**
16
+ * Check if we need to use OAuth token directly or exchange for API key
17
+ */
18
+ export declare function validateTokenWithApi(token: string, apiBaseUrl: string): Promise<boolean>;
19
+ export {};
20
+ //# sourceMappingURL=api-key-exchange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-exchange.d.ts","sourceRoot":"","sources":["../src/api-key-exchange.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,UAAU,cAAc;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAiD9E;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA2B9F"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * API Key Exchange for Qwen
3
+ * Attempts to exchange OAuth token for API key
4
+ */
5
+ import { debugLog, warnLog, infoLog } from "./logger.js";
6
+ import { QWEN_OAUTH_BASE_URL } from "./constants.js";
7
+ /**
8
+ * Potential endpoints for API key retrieval
9
+ */
10
+ const API_KEY_ENDPOINTS = [
11
+ "/api/v1/user/api-key",
12
+ "/api/v1/user/token",
13
+ "/api/v1/user/info",
14
+ "/api/v1/auth/api-key",
15
+ "/api/v1/oauth2/api-key",
16
+ ];
17
+ /**
18
+ * Try to get API key from OAuth token
19
+ * This is speculative - Qwen may require OAuth token → API key exchange
20
+ */
21
+ export async function tryGetApiKey(oauthToken) {
22
+ debugLog("Attempting to exchange OAuth token for API key");
23
+ for (const endpoint of API_KEY_ENDPOINTS) {
24
+ const url = `${QWEN_OAUTH_BASE_URL}${endpoint}`;
25
+ try {
26
+ debugLog(`Trying endpoint: ${url}`);
27
+ const response = await fetch(url, {
28
+ method: "GET",
29
+ headers: {
30
+ "Authorization": `Bearer ${oauthToken}`,
31
+ "Content-Type": "application/json",
32
+ },
33
+ });
34
+ debugLog(`Endpoint ${endpoint} responded with ${response.status}`);
35
+ if (response.ok) {
36
+ const data = await response.json();
37
+ // Look for API key in various possible fields
38
+ const apiKey = data.api_key || data.apiKey || data.key || data.token;
39
+ if (apiKey && typeof apiKey === "string") {
40
+ infoLog(`Found API key via endpoint: ${endpoint}`);
41
+ return {
42
+ success: true,
43
+ api_key: apiKey,
44
+ };
45
+ }
46
+ debugLog(`Endpoint ${endpoint} returned data but no API key found`, {
47
+ fields: Object.keys(data),
48
+ });
49
+ }
50
+ }
51
+ catch (error) {
52
+ debugLog(`Error trying endpoint ${endpoint}:`, {
53
+ error: String(error),
54
+ });
55
+ }
56
+ }
57
+ warnLog("Could not find API key exchange endpoint");
58
+ return {
59
+ success: false,
60
+ error: "No API key exchange endpoint found",
61
+ };
62
+ }
63
+ /**
64
+ * Check if we need to use OAuth token directly or exchange for API key
65
+ */
66
+ export async function validateTokenWithApi(token, apiBaseUrl) {
67
+ try {
68
+ debugLog("Validating token with API endpoint");
69
+ // Try a simple API call to see if token works
70
+ const response = await fetch(`${apiBaseUrl}/models`, {
71
+ method: "GET",
72
+ headers: {
73
+ "Authorization": `Bearer ${token}`,
74
+ },
75
+ });
76
+ if (response.ok) {
77
+ infoLog("Token is valid for API endpoint");
78
+ return true;
79
+ }
80
+ debugLog("Token validation failed", {
81
+ status: response.status,
82
+ statusText: response.statusText,
83
+ });
84
+ return false;
85
+ }
86
+ catch (error) {
87
+ debugLog("Error validating token", { error: String(error) });
88
+ return false;
89
+ }
90
+ }
91
+ //# sourceMappingURL=api-key-exchange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-exchange.js","sourceRoot":"","sources":["../src/api-key-exchange.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Credential storage for Qwen OAuth
3
+ * Saves/loads credentials from ~/.qwen/oauth_creds.json
4
+ */
5
+ export interface QwenCredentials {
6
+ accessToken: string;
7
+ refreshToken?: string;
8
+ expiryDate?: number;
9
+ tokenType?: string;
10
+ resourceUrl?: string;
11
+ scope?: string;
12
+ }
13
+ export declare function saveCredentials(credentials: QwenCredentials): void;
14
+ export declare function loadCredentials(): QwenCredentials | null;
15
+ export declare function deleteCredentials(): void;
16
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD,wBAAgB,eAAe,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAmBlE;AAED,wBAAgB,eAAe,IAAI,eAAe,GAAG,IAAI,CA2BxD;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAWxC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Credential storage for Qwen OAuth
3
+ * Saves/loads credentials from ~/.qwen/oauth_creds.json
4
+ */
5
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { homedir } from "node:os";
8
+ const CREDENTIALS_DIR = join(homedir(), ".qwen");
9
+ const CREDENTIALS_FILE = join(CREDENTIALS_DIR, "oauth_creds.json");
10
+ function ensureCredentialsDir() {
11
+ if (!existsSync(CREDENTIALS_DIR)) {
12
+ mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
13
+ }
14
+ }
15
+ export function saveCredentials(credentials) {
16
+ try {
17
+ ensureCredentialsDir();
18
+ // Save in snake_case format (matches OAuth response)
19
+ const data = {
20
+ access_token: credentials.accessToken,
21
+ refresh_token: credentials.refreshToken,
22
+ expiry_date: credentials.expiryDate,
23
+ token_type: credentials.tokenType,
24
+ resource_url: credentials.resourceUrl,
25
+ scope: credentials.scope,
26
+ };
27
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2), {
28
+ encoding: "utf-8",
29
+ mode: 0o600,
30
+ });
31
+ }
32
+ catch (error) {
33
+ console.error("Failed to save credentials:", error);
34
+ }
35
+ }
36
+ export function loadCredentials() {
37
+ try {
38
+ if (!existsSync(CREDENTIALS_FILE)) {
39
+ return null;
40
+ }
41
+ const data = readFileSync(CREDENTIALS_FILE, "utf-8");
42
+ const fileCreds = JSON.parse(data);
43
+ // Convert snake_case to camelCase
44
+ const credentials = {
45
+ accessToken: fileCreds.access_token,
46
+ refreshToken: fileCreds.refresh_token,
47
+ expiryDate: fileCreds.expiry_date,
48
+ tokenType: fileCreds.token_type,
49
+ resourceUrl: fileCreds.resource_url,
50
+ scope: fileCreds.scope,
51
+ };
52
+ // Check if token is expired
53
+ if (credentials.expiryDate && Date.now() > credentials.expiryDate) {
54
+ return null;
55
+ }
56
+ return credentials;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ export function deleteCredentials() {
63
+ try {
64
+ if (existsSync(CREDENTIALS_FILE)) {
65
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify({}, null, 2), {
66
+ encoding: "utf-8",
67
+ mode: 0o600,
68
+ });
69
+ }
70
+ }
71
+ catch {
72
+ // Ignore errors
73
+ }
74
+ }
75
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAqBlC,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AACjD,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;AAEnE,SAAS,oBAAoB;IAC3B,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAA4B;IAC1D,IAAI,CAAC;QACH,oBAAoB,EAAE,CAAC;QACvB,qDAAqD;QACrD,MAAM,IAAI,GAAwB;YAChC,YAAY,EAAE,WAAW,CAAC,WAAW;YACrC,aAAa,EAAE,WAAW,CAAC,YAAY;YACvC,WAAW,EAAE,WAAW,CAAC,UAAU;YACnC,UAAU,EAAE,WAAW,CAAC,SAAS;YACjC,YAAY,EAAE,WAAW,CAAC,WAAW;YACrC,KAAK,EAAE,WAAW,CAAC,KAAK;SACzB,CAAC;QACF,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;YAC7D,QAAQ,EAAE,OAAO;YACjB,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAwB,CAAC;QAE1D,kCAAkC;QAClC,MAAM,WAAW,GAAoB;YACnC,WAAW,EAAE,SAAS,CAAC,YAAY;YACnC,YAAY,EAAE,SAAS,CAAC,aAAa;YACrC,UAAU,EAAE,SAAS,CAAC,WAAW;YACjC,SAAS,EAAE,SAAS,CAAC,UAAU;YAC/B,WAAW,EAAE,SAAS,CAAC,YAAY;YACnC,KAAK,EAAE,SAAS,CAAC,KAAK;SACvB,CAAC;QAEF,4BAA4B;QAC5B,IAAI,WAAW,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,UAAU,EAAE,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACjC,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;gBAC3D,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Diagnostic utilities for testing Qwen OAuth endpoints
3
+ */
4
+ export interface DiagnosticResult {
5
+ success: boolean;
6
+ endpoint: string;
7
+ status?: number;
8
+ statusText?: string;
9
+ error?: string;
10
+ data?: any;
11
+ responseTime?: number;
12
+ }
13
+ /**
14
+ * Test if Qwen OAuth device code endpoint is accessible
15
+ */
16
+ export declare function testDeviceCodeEndpoint(): Promise<DiagnosticResult>;
17
+ /**
18
+ * Test if Qwen API endpoint is accessible
19
+ * Tests the /chat/completions endpoint (OpenAI-compatible)
20
+ */
21
+ export declare function testAPIEndpoint(baseURL: string, token?: string): Promise<DiagnosticResult>;
22
+ /**
23
+ * Test if the base OAuth URL is accessible
24
+ */
25
+ export declare function testBaseURL(): Promise<DiagnosticResult>;
26
+ /**
27
+ * Run all diagnostic tests
28
+ */
29
+ export declare function runDiagnostics(token?: string): Promise<{
30
+ baseURL: DiagnosticResult;
31
+ deviceCode: DiagnosticResult;
32
+ apis: Record<string, DiagnosticResult>;
33
+ }>;
34
+ /**
35
+ * Run the OAuth device flow to get a token
36
+ */
37
+ export declare function runOAuthFlow(): Promise<{
38
+ access_token: string;
39
+ refresh_token: string;
40
+ expires_in: number;
41
+ } | null>;
42
+ /**
43
+ * Refresh an access token
44
+ */
45
+ export declare function refreshToken(refreshToken: string): Promise<{
46
+ access_token: string;
47
+ expires_in: number;
48
+ } | null>;
49
+ //# sourceMappingURL=diagnostic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic.d.ts","sourceRoot":"","sources":["../src/diagnostic.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAuCxE;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmDhG;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,gBAAgB,CAAC,CA2B7D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5D,OAAO,EAAE,gBAAgB,CAAC;IAC1B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACxC,CAAC,CAgDD;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,IAAI,CAAC,CA6HR;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA8B9D"}