@oevortex/opencode-qwen-auth 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # OpenCode Qwen Auth Plugin
2
+
3
+ Authenticate the OpenCode CLI with your Qwen Code account so you can use Qwen models with your existing quota.
4
+
5
+ ## Features
6
+
7
+ - **OAuth Authentication** - Authenticate with Qwen using the same flow as Qwen Code CLI
8
+ - **Automatic Token Refresh** - Handles token refresh transparently with no manual intervention
9
+ - **Thinking Block Support** - Parses `<think>...</think>` blocks from Qwen models that support reasoning
10
+ - **Rate Limit Handling** - Automatic retry with exponential backoff for rate limits
11
+ - **API Key Fallback** - Option to use DashScope API key directly
12
+
13
+ ## Setup
14
+
15
+ 1. Add the plugin to your OpenCode config:
16
+
17
+ ```json
18
+ {
19
+ "$schema": "https://opencode.ai/config.json",
20
+ "plugin": ["@oevortex/opencode-qwen-auth"]
21
+ }
22
+
23
+ ```
24
+
25
+ 2. Run `opencode auth login`.
26
+
27
+ 3. Choose the Qwen provider and select **OAuth with Qwen (Alibaba Cloud)**.
28
+
29
+ 4. Authenticate with your Qwen account in the browser.
30
+
31
+ The plugin spins up a local callback listener on `http://localhost:36743/oauth-callback`, so after approving in the browser you'll land on an "Authentication complete" page with no URL copy/paste required. If that port is already taken or you're in a headless environment, the CLI automatically falls back to the copy/paste flow.
32
+
33
+ ## Supported Models
34
+
35
+ This plugin works with Qwen Code models accessible through the OAuth flow:
36
+
37
+ | Model ID | Alias | Context Window | Max Output |
38
+ |----------|-------|----------------|------------|
39
+ | `qwen3-coder-plus` | `qwen-coder-plus` | 1,000,000 | 65,536 |
40
+ | `qwen3-coder-flash` | `qwen-coder-flash` | 1,000,000 | 65,536 |
41
+
42
+ All models are **free** when using OAuth authentication.
43
+
44
+ ## Example OpenCode Config
45
+
46
+ ```json
47
+ {
48
+ "$schema": "https://opencode.ai/config.json",
49
+ "plugin": ["@oevortex/opencode-qwen-auth"],
50
+
51
+ "provider": {
52
+ "qwencode": {
53
+ "npm": "@ai-sdk/openai-compatible",
54
+ "options": {
55
+ "baseURL": "https://portal.qwen.ai/v1"
56
+ },
57
+ "models": {
58
+ "qwen3-coder-plus": {
59
+ "id": "qwen3-coder-plus",
60
+ "name": "Qwen3 Coder Plus",
61
+ "limit": { "context": 1000000, "output": 65536 },
62
+ "cost": { "input": 0, "output": 0 }
63
+ },
64
+ "qwen3-coder-flash": {
65
+ "id": "qwen3-coder-flash",
66
+ "name": "Qwen3 Coder Flash",
67
+ "limit": { "context": 1000000, "output": 65536 },
68
+ "cost": { "input": 0, "output": 0 }
69
+ },
70
+ "qwen-coder-plus": {
71
+ "id": "qwen3-coder-plus",
72
+ "name": "Qwen Coder Plus (Alias)",
73
+ "limit": { "context": 1000000, "output": 65536 },
74
+ "cost": { "input": 0, "output": 0 }
75
+ },
76
+ "qwen-coder-flash": {
77
+ "id": "qwen3-coder-flash",
78
+ "name": "Qwen Coder Flash (Alias)",
79
+ "limit": { "context": 1000000, "output": 65536 },
80
+ "cost": { "input": 0, "output": 0 }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Prerequisites
89
+
90
+ Before using this plugin, you need to have authenticated with the Qwen Code CLI at least once. This creates the OAuth credentials that the plugin uses.
91
+
92
+ ```bash
93
+ # Install Qwen Code CLI if not already installed
94
+ npm install -g @qwen-code/qwen-code@latest
95
+
96
+ # Authenticate with Qwen
97
+ qwen
98
+ ```
99
+
100
+ The plugin reads credentials from `~/.qwen/oauth_creds.json`.
101
+
102
+ ## Alternative: API Key Authentication
103
+
104
+ If you prefer to use an API key directly instead of OAuth:
105
+
106
+ 1. Get an API key from [DashScope Console](https://dashscope.console.aliyun.com/)
107
+
108
+ 2. During `opencode auth login`, select **Manually enter API Key**
109
+
110
+ 3. Enter your DashScope API key
111
+
112
+ ## Local Development
113
+
114
+ ```bash
115
+ git clone https://github.com/OEvortex/opencode-qwen-auth.git
116
+ cd opencode-qwen-auth
117
+ bun install
118
+ ```
119
+
120
+ To load a local checkout in OpenCode:
121
+
122
+ ```json
123
+ {
124
+ "$schema": "https://opencode.ai/config.json",
125
+ "plugin": ["file:///absolute/path/to/opencode-qwen-auth"]
126
+ }
127
+ ```
128
+
129
+ ## Thinking Block Support
130
+
131
+ Qwen models support thinking/reasoning through `<think>...</think>` blocks. The plugin automatically parses these and separates reasoning content from regular output.
132
+
133
+ Example model response:
134
+ ```
135
+ <think>
136
+ Let me analyze this code...
137
+ The function appears to have a bug in the loop condition.
138
+ </think>
139
+
140
+ I found an issue in your code. The loop condition should be...
141
+ ```
142
+
143
+ The plugin exposes utilities for working with thinking blocks:
144
+
145
+ ```typescript
146
+ import { parseThinkingBlocks, stripThinkingBlocks } from "@oevortex/opencode-qwen-auth";
147
+
148
+
149
+ const result = parseThinkingBlocks(text);
150
+ console.log(result.regularContent); // Main response
151
+ console.log(result.reasoningContent); // Thinking content
152
+ ```
153
+
154
+ ## Debugging
155
+
156
+ Enable debug logging by setting the environment variable:
157
+
158
+ ```bash
159
+ OPENCODE_QWEN_CONSOLE_LOG=1 opencode
160
+ ```
161
+
162
+ Or use OpenCode's built-in logging:
163
+
164
+ ```bash
165
+ opencode --log-level DEBUG --print-logs
166
+ ```
167
+
168
+ ## Troubleshooting
169
+
170
+ ### "Qwen OAuth credentials not found"
171
+
172
+ Make sure you've authenticated with the Qwen Code CLI first:
173
+
174
+ ```bash
175
+ qwen-code auth login
176
+ ```
177
+
178
+ ### Token refresh failures
179
+
180
+ If token refresh keeps failing, try re-authenticating:
181
+
182
+ ```bash
183
+ opencode auth login
184
+ ```
185
+
186
+ And select the Qwen OAuth option again.
187
+
188
+ ### Rate limiting
189
+
190
+ The plugin handles rate limits automatically with exponential backoff. If you're hitting limits frequently, consider:
191
+
192
+ - Using `qwen3-coder-flash` for faster responses
193
+ - Waiting between requests
194
+ - Upgrading your Qwen account
195
+
196
+ ## How It Works
197
+
198
+ 1. **Initial Auth**: When you run `opencode auth login`, the plugin starts a local HTTP server and opens your browser to the Qwen OAuth page.
199
+
200
+ 2. **Token Storage**: After successful auth, tokens are stored in OpenCode's auth system and in `~/.qwen/oauth_creds.json`.
201
+
202
+ 3. **Request Handling**: All API requests go through a custom fetch wrapper that:
203
+ - Adds authorization headers
204
+ - Refreshes expired tokens automatically
205
+ - Handles rate limits with retry logic
206
+ - Transforms requests for Qwen API compatibility
207
+
208
+ 4. **Token Refresh**: Access tokens are refreshed automatically before they expire (with a 60-second buffer).
209
+
210
+ ## Credits
211
+
212
+ This plugin is based on:
213
+
214
+ - [revibe](https://github.com/OEvortex/revibe) - Original Python implementation of Qwen OAuth
215
+ - [Roo-Code](https://github.com/RooCodeInc/Roo-Code) - Reference implementation for Qwen Code API
216
+ - [opencode-google-antigravity-auth](https://github.com/shekohex/opencode-google-antigravity-auth) - OpenCode plugin architecture reference
217
+
218
+ ## License
219
+
220
+ MIT
package/index.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * OpenCode Qwen Auth Plugin
3
+ *
4
+ * Provides OAuth authentication for Qwen/Alibaba Cloud DashScope API
5
+ * allowing OpenCode to use Qwen models with your existing credentials.
6
+ *
7
+ * @module opencode-qwen-auth
8
+ */
9
+
10
+ // Main plugin export
11
+ export { QwenOAuthPlugin, QwenOAuthPlugin as default } from "./src/plugin";
12
+
13
+ // OAuth utilities
14
+ export {
15
+ QwenOAuthManager,
16
+ getQwenCachedCredentialPath,
17
+ hasQwenCredentials,
18
+ } from "./src/qwen/oauth";
19
+
20
+ // Thinking block parser
21
+ export {
22
+ ThinkingBlockParser,
23
+ parseThinkingBlocks,
24
+ stripThinkingBlocks,
25
+ extractThinkingContent,
26
+ hasThinkingBlocks,
27
+ } from "./src/qwen/thinking-parser";
28
+
29
+ // Auth utilities
30
+ export {
31
+ isOAuthAuth,
32
+ accessTokenExpired,
33
+ parseRefreshParts,
34
+ formatRefreshParts,
35
+ createAuthDetails,
36
+ } from "./src/plugin/auth";
37
+
38
+ // Token utilities
39
+ export {
40
+ refreshAccessToken,
41
+ ensureValidToken,
42
+ needsTokenRefresh,
43
+ } from "./src/plugin/token";
44
+
45
+ // Models configuration
46
+ export {
47
+ QWEN_CODE_MODELS,
48
+ MODEL_ALIASES,
49
+ DEFAULT_MODEL,
50
+ getModelInfo,
51
+ resolveModelId,
52
+ getAllModelIds,
53
+ isValidModel,
54
+ generateProviderConfig,
55
+ } from "./src/models";
56
+
57
+ // Types
58
+ export type {
59
+ QwenOAuthCredentials,
60
+ QwenTokenResponse,
61
+ QwenModelInfo,
62
+ ChatMessage,
63
+ ChatCompletionRequest,
64
+ ChatCompletionResponse,
65
+ Tool,
66
+ ToolCall,
67
+ ToolChoice,
68
+ AuthDetails,
69
+ OAuthAuthDetails,
70
+ PluginContext,
71
+ PluginResult,
72
+ TokenExchangeResult,
73
+ } from "./src/types";
74
+
75
+ // Constants
76
+ export {
77
+ QWEN_PROVIDER_ID,
78
+ QWEN_OAUTH_BASE_URL,
79
+ QWEN_DEFAULT_BASE_URL,
80
+ QWEN_INTL_BASE_URL,
81
+ QWEN_PORTAL_BASE_URL,
82
+ } from "./src/constants";
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@oevortex/opencode-qwen-auth",
3
+ "module": "index.ts",
4
+ "version": "0.1.0",
5
+ "scripts": {
6
+ "test": "bun test",
7
+ "test:unit": "bun test src/**/*.test.ts"
8
+ },
9
+ "description": "Opencode plugin providing Qwen Code OAuth authentication for Qwen/Alibaba Cloud models",
10
+ "author": "",
11
+ "keywords": [
12
+ "opencode",
13
+ "qwen",
14
+ "alibaba",
15
+ "dashscope",
16
+ "oauth",
17
+ "authentication",
18
+ "ai",
19
+ "plugin"
20
+ ],
21
+ "repository": "",
22
+ "files": [
23
+ "index.ts",
24
+ "src"
25
+ ],
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "devDependencies": {
29
+ "@opencode-ai/sdk": "^1.0.182",
30
+ "@types/bun": "latest",
31
+ "@types/node": "^22.0.0"
32
+ },
33
+ "peerDependencies": {
34
+ "typescript": "^5.9.3"
35
+ },
36
+ "dependencies": {
37
+ "@opencode-ai/plugin": "^1.0.182",
38
+ "open": "^11.0.0"
39
+ }
40
+ }
@@ -0,0 +1,43 @@
1
+ // Qwen OAuth Configuration
2
+ export const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
3
+ export const QWEN_OAUTH_TOKEN_ENDPOINT = `${QWEN_OAUTH_BASE_URL}/api/v1/oauth2/token`;
4
+ export const QWEN_OAUTH_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56";
5
+
6
+ // Credential storage
7
+ export const QWEN_DIR = ".qwen";
8
+ export const QWEN_CREDENTIAL_FILENAME = "oauth_creds.json";
9
+
10
+ // Default API Base URLs
11
+ export const QWEN_DEFAULT_BASE_URL =
12
+ "https://dashscope.aliyuncs.com/compatible-mode/v1";
13
+ export const QWEN_INTL_BASE_URL =
14
+ "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
15
+ export const QWEN_PORTAL_BASE_URL = "https://portal.qwen.ai/v1";
16
+
17
+ // Token refresh buffer (30 seconds in milliseconds)
18
+ export const TOKEN_REFRESH_BUFFER_MS = 30 * 1000;
19
+
20
+ // HTTP Status codes
21
+ export const HTTP_OK = 200;
22
+ export const HTTP_UNAUTHORIZED = 401;
23
+
24
+ // Provider ID for OpenCode
25
+ export const QWEN_PROVIDER_ID = "qwencode";
26
+
27
+ // OAuth callback configuration
28
+ export const QWEN_CALLBACK_PORT = 36743;
29
+ export const QWEN_REDIRECT_URI = `http://localhost:${QWEN_CALLBACK_PORT}/oauth-callback`;
30
+
31
+ // User Agent
32
+ export const QWEN_USER_AGENT = "qwen-code/1.0.0";
33
+
34
+ // Rate limit configuration
35
+ export const RATE_LIMIT_BACKOFF_BASE_MS = 1000;
36
+ export const RATE_LIMIT_BACKOFF_MAX_MS = 60 * 60 * 1000;
37
+ export const RATE_LIMIT_MAX_RETRIES = 5;
38
+
39
+ // Request timeout (in milliseconds)
40
+ export const REQUEST_TIMEOUT_MS = 720 * 1000;
41
+
42
+ // Console log environment variable
43
+ export const ENV_CONSOLE_LOG = "OPENCODE_QWEN_CONSOLE_LOG";
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Global type declarations for Node.js modules
3
+ * These help TypeScript resolve Node.js built-in modules
4
+ */
5
+
6
+ /// <reference types="node" />
7
+
8
+ declare module "readline" {
9
+ import { EventEmitter } from "events";
10
+
11
+ interface ReadLineOptions {
12
+ input: NodeJS.ReadableStream;
13
+ output?: NodeJS.WritableStream;
14
+ terminal?: boolean;
15
+ historySize?: number;
16
+ prompt?: string;
17
+ crlfDelay?: number;
18
+ removeHistoryDuplicates?: boolean;
19
+ escapeCodeTimeout?: number;
20
+ }
21
+
22
+ interface Interface extends EventEmitter {
23
+ question(query: string, callback: (answer: string) => void): void;
24
+ close(): void;
25
+ pause(): this;
26
+ resume(): this;
27
+ setPrompt(prompt: string): void;
28
+ prompt(preserveCursor?: boolean): void;
29
+ write(
30
+ data: string | Buffer,
31
+ key?: { ctrl?: boolean; meta?: boolean; shift?: boolean; name: string },
32
+ ): void;
33
+ }
34
+
35
+ function createInterface(options: ReadLineOptions): Interface;
36
+ }
37
+
38
+ declare module "node:readline" {
39
+ export * from "readline";
40
+ }
41
+
42
+ declare module "node:process" {
43
+ export const stdin: NodeJS.ReadableStream;
44
+ export const stdout: NodeJS.WritableStream;
45
+ export const stderr: NodeJS.WritableStream;
46
+ export const env: NodeJS.ProcessEnv;
47
+ export const platform: NodeJS.Platform;
48
+ export function cwd(): string;
49
+ export function exit(code?: number): never;
50
+ }
51
+
52
+ declare module "node:os" {
53
+ export function homedir(): string;
54
+ export function tmpdir(): string;
55
+ export function hostname(): string;
56
+ export function platform(): NodeJS.Platform;
57
+ export function type(): string;
58
+ export function release(): string;
59
+ export function arch(): string;
60
+ export function cpus(): Array<{
61
+ model: string;
62
+ speed: number;
63
+ times: object;
64
+ }>;
65
+ export function totalmem(): number;
66
+ export function freemem(): number;
67
+ export function networkInterfaces(): NodeJS.Dict<
68
+ Array<{
69
+ address: string;
70
+ netmask: string;
71
+ family: string;
72
+ mac: string;
73
+ internal: boolean;
74
+ }>
75
+ >;
76
+ }
77
+
78
+ declare module "node:path" {
79
+ export function join(...paths: string[]): string;
80
+ export function resolve(...paths: string[]): string;
81
+ export function dirname(path: string): string;
82
+ export function basename(path: string, ext?: string): string;
83
+ export function extname(path: string): string;
84
+ export function normalize(path: string): string;
85
+ export function isAbsolute(path: string): boolean;
86
+ export function relative(from: string, to: string): string;
87
+ export function parse(path: string): {
88
+ root: string;
89
+ dir: string;
90
+ base: string;
91
+ ext: string;
92
+ name: string;
93
+ };
94
+ export const sep: string;
95
+ export const delimiter: string;
96
+ }
97
+
98
+ declare module "node:fs" {
99
+ export function readFileSync(path: string, encoding: BufferEncoding): string;
100
+ export function readFileSync(path: string): Buffer;
101
+ export function writeFileSync(
102
+ path: string,
103
+ data: string | Buffer,
104
+ options?:
105
+ | { encoding?: BufferEncoding; mode?: number; flag?: string }
106
+ | BufferEncoding,
107
+ ): void;
108
+ export function existsSync(path: string): boolean;
109
+ export function mkdirSync(
110
+ path: string,
111
+ options?: { recursive?: boolean; mode?: number },
112
+ ): string | undefined;
113
+ export function readdirSync(path: string): string[];
114
+ export function statSync(path: string): {
115
+ isFile(): boolean;
116
+ isDirectory(): boolean;
117
+ size: number;
118
+ mtime: Date;
119
+ };
120
+ export function unlinkSync(path: string): void;
121
+ export function rmdirSync(
122
+ path: string,
123
+ options?: { recursive?: boolean },
124
+ ): void;
125
+ export function copyFileSync(src: string, dest: string): void;
126
+ export function renameSync(oldPath: string, newPath: string): void;
127
+ }
128
+
129
+ declare module "node:http" {
130
+ import { EventEmitter } from "events";
131
+ import { Duplex } from "stream";
132
+
133
+ export interface IncomingMessage extends Duplex {
134
+ url?: string;
135
+ method?: string;
136
+ headers: { [key: string]: string | string[] | undefined };
137
+ httpVersion: string;
138
+ statusCode?: number;
139
+ statusMessage?: string;
140
+ }
141
+
142
+ export interface ServerResponse extends Duplex {
143
+ statusCode: number;
144
+ statusMessage: string;
145
+ writeHead(
146
+ statusCode: number,
147
+ headers?: { [key: string]: string | string[] },
148
+ ): this;
149
+ setHeader(name: string, value: string | string[]): this;
150
+ getHeader(name: string): string | string[] | undefined;
151
+ removeHeader(name: string): void;
152
+ write(chunk: any, encoding?: BufferEncoding): boolean;
153
+ end(data?: any, encoding?: BufferEncoding): this;
154
+ }
155
+
156
+ export interface Server extends EventEmitter {
157
+ listen(port: number, hostname?: string, callback?: () => void): this;
158
+ listen(port: number, callback?: () => void): this;
159
+ close(callback?: (err?: Error) => void): this;
160
+ address():
161
+ | { address: string; family: string; port: number }
162
+ | string
163
+ | null;
164
+ on(event: string, listener: (...args: any[]) => void): this;
165
+ on(event: "error", listener: (err: Error) => void): this;
166
+ on(event: "listening", listener: () => void): this;
167
+ on(event: "close", listener: () => void): this;
168
+ }
169
+
170
+ export function createServer(
171
+ requestListener?: (req: IncomingMessage, res: ServerResponse) => void,
172
+ ): Server;
173
+ }
174
+
175
+ declare module "open" {
176
+ interface Options {
177
+ wait?: boolean;
178
+ background?: boolean;
179
+ newInstance?: boolean;
180
+ allowNonzeroExitCode?: boolean;
181
+ app?: { name: string; arguments?: string[] } | string;
182
+ }
183
+
184
+ function open(
185
+ target: string,
186
+ options?: Options,
187
+ ): Promise<import("child_process").ChildProcess>;
188
+ export default open;
189
+ }
190
+
191
+ declare var process: NodeJS.Process;
192
+
193
+ declare namespace NodeJS {
194
+ interface Dict<T> {
195
+ [key: string]: T | undefined;
196
+ }
197
+
198
+ interface ProcessEnv {
199
+ [key: string]: string | undefined;
200
+ }
201
+
202
+ interface Process {
203
+ stdin: ReadableStream;
204
+ stdout: WritableStream;
205
+ stderr: WritableStream;
206
+ env: ProcessEnv;
207
+ platform: Platform;
208
+ cwd(): string;
209
+ exit(code?: number): never;
210
+ }
211
+
212
+ interface ReadableStream {
213
+ read(size?: number): string | Buffer | null;
214
+ on(event: string, listener: (...args: any[]) => void): this;
215
+ pipe<T extends WritableStream>(destination: T): T;
216
+ }
217
+
218
+ interface WritableStream {
219
+ write(chunk: any): boolean;
220
+ end(): void;
221
+ on(event: string, listener: (...args: any[]) => void): this;
222
+ }
223
+
224
+ interface ErrnoException extends Error {
225
+ errno?: number;
226
+ code?: string;
227
+ path?: string;
228
+ syscall?: string;
229
+ stack?: string;
230
+ }
231
+
232
+ type Platform =
233
+ | "aix"
234
+ | "android"
235
+ | "darwin"
236
+ | "freebsd"
237
+ | "haiku"
238
+ | "linux"
239
+ | "openbsd"
240
+ | "sunos"
241
+ | "win32"
242
+ | "cygwin"
243
+ | "netbsd";
244
+ }
245
+
246
+ type BufferEncoding =
247
+ | "ascii"
248
+ | "utf8"
249
+ | "utf-8"
250
+ | "utf16le"
251
+ | "ucs2"
252
+ | "ucs-2"
253
+ | "base64"
254
+ | "base64url"
255
+ | "latin1"
256
+ | "binary"
257
+ | "hex";