opencode-qwen-cli-auth 2.2.9 → 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 +261 -62
- package/README.vi.md +261 -0
- package/dist/index.js +270 -79
- package/dist/lib/auth/auth.js +192 -2
- package/dist/lib/auth/browser.js +14 -4
- package/dist/lib/config.js +34 -0
- package/dist/lib/constants.js +99 -18
- package/dist/lib/logger.js +58 -12
- package/package.json +1 -1
package/dist/lib/constants.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Constants for Qwen OAuth Plugin
|
|
2
|
+
* @fileoverview Constants for Qwen OAuth Plugin
|
|
3
|
+
* Centralized configuration for OAuth endpoints, headers, error codes, and other constants
|
|
4
|
+
* @license MIT
|
|
3
5
|
*/
|
|
4
|
-
|
|
6
|
+
|
|
7
|
+
/** Plugin identifier for logging and debugging */
|
|
5
8
|
export const PLUGIN_NAME = "qwen-oauth-plugin";
|
|
6
|
-
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Provider ID for opencode configuration
|
|
12
|
+
* Used in model references like qwen-code/coder-model
|
|
13
|
+
*/
|
|
7
14
|
export const PROVIDER_ID = "qwen-code";
|
|
8
|
-
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Dummy API key placeholder
|
|
18
|
+
* Actual authentication is handled via OAuth flow, not API key
|
|
19
|
+
*/
|
|
9
20
|
export const DUMMY_API_KEY = "qwen-oauth";
|
|
21
|
+
|
|
10
22
|
/**
|
|
11
23
|
* Default Qwen DashScope base URL (fallback if resource_url is missing)
|
|
12
24
|
* Note: This plugin is for OAuth authentication only. For API key authentication,
|
|
@@ -15,27 +27,43 @@ export const DUMMY_API_KEY = "qwen-oauth";
|
|
|
15
27
|
* IMPORTANT: OAuth endpoints use /api/v1, DashScope OpenAI-compatible uses /compatible-mode/v1
|
|
16
28
|
* - OAuth endpoints: /api/v1/oauth2/ (for authentication)
|
|
17
29
|
* - Chat API: /v1/ (for completions)
|
|
30
|
+
*
|
|
31
|
+
* @constant {string}
|
|
18
32
|
*/
|
|
19
33
|
// NOTE:
|
|
20
34
|
// qwen-code (official CLI) defaults to DashScope OpenAI-compatible endpoint when
|
|
21
35
|
// `resource_url` is missing. This is required for the free OAuth flow to behave
|
|
22
36
|
// the same as the CLI.
|
|
23
37
|
export const DEFAULT_QWEN_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
24
|
-
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Qwen OAuth endpoints and configuration
|
|
41
|
+
* Source: Qwen Code CLI (https://github.com/QwenLM/qwen-code)
|
|
42
|
+
* @namespace
|
|
43
|
+
*/
|
|
25
44
|
export const QWEN_OAUTH = {
|
|
45
|
+
/** OAuth 2.0 Device Code endpoint */
|
|
26
46
|
DEVICE_CODE_URL: "https://chat.qwen.ai/api/v1/oauth2/device/code",
|
|
47
|
+
/** OAuth 2.0 Token endpoint */
|
|
27
48
|
TOKEN_URL: "https://chat.qwen.ai/api/v1/oauth2/token",
|
|
28
49
|
/**
|
|
29
50
|
* Qwen OAuth Client ID
|
|
30
|
-
* Source: Qwen Code CLI (https://github.com/QwenLM/qwen-code)
|
|
31
51
|
* This is a public client ID used for OAuth Device Authorization Grant flow (RFC 8628)
|
|
52
|
+
* @constant {string}
|
|
32
53
|
*/
|
|
33
54
|
CLIENT_ID: "f0304373b74a44d2b584a3fb70ca9e56",
|
|
55
|
+
/** OAuth scopes requested: openid, profile, email, and model completion access */
|
|
34
56
|
SCOPE: "openid profile email model.completion",
|
|
57
|
+
/** OAuth 2.0 Device Code grant type (RFC 8628) */
|
|
35
58
|
GRANT_TYPE_DEVICE: "urn:ietf:params:oauth:grant-type:device_code",
|
|
59
|
+
/** OAuth 2.0 Refresh Token grant type */
|
|
36
60
|
GRANT_TYPE_REFRESH: "refresh_token",
|
|
37
61
|
};
|
|
38
|
-
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* HTTP Status Codes for error handling
|
|
65
|
+
* @namespace
|
|
66
|
+
*/
|
|
39
67
|
export const HTTP_STATUS = {
|
|
40
68
|
OK: 200,
|
|
41
69
|
BAD_REQUEST: 400,
|
|
@@ -43,21 +71,37 @@ export const HTTP_STATUS = {
|
|
|
43
71
|
FORBIDDEN: 403,
|
|
44
72
|
TOO_MANY_REQUESTS: 429,
|
|
45
73
|
};
|
|
74
|
+
|
|
46
75
|
/**
|
|
47
|
-
* DashScope headers
|
|
76
|
+
* DashScope headers for OAuth authentication
|
|
48
77
|
* Note: OAuth requires X-DashScope-AuthType to indicate qwen-oauth authentication
|
|
78
|
+
* @namespace
|
|
49
79
|
*/
|
|
50
80
|
export const PORTAL_HEADERS = {
|
|
81
|
+
/** Header name for auth type specification */
|
|
51
82
|
AUTH_TYPE: "X-DashScope-AuthType",
|
|
83
|
+
/** Header value for qwen-oauth authentication */
|
|
52
84
|
AUTH_TYPE_VALUE: "qwen-oauth",
|
|
53
85
|
};
|
|
54
|
-
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Device flow polling configuration
|
|
89
|
+
* Controls backoff strategy for OAuth token polling
|
|
90
|
+
* @namespace
|
|
91
|
+
*/
|
|
55
92
|
export const DEVICE_FLOW = {
|
|
93
|
+
/** Initial polling interval in milliseconds */
|
|
56
94
|
INITIAL_POLL_INTERVAL: 2000, // 2 seconds
|
|
95
|
+
/** Maximum polling interval in milliseconds */
|
|
57
96
|
MAX_POLL_INTERVAL: 10000, // 10 seconds
|
|
97
|
+
/** Backoff multiplier for exponential backoff */
|
|
58
98
|
BACKOFF_MULTIPLIER: 1.5,
|
|
59
99
|
};
|
|
60
|
-
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Error messages for user-facing errors
|
|
103
|
+
* @namespace
|
|
104
|
+
*/
|
|
61
105
|
export const ERROR_MESSAGES = {
|
|
62
106
|
TOKEN_REFRESH_FAILED: "Failed to refresh token, authentication required",
|
|
63
107
|
DEVICE_AUTH_TIMEOUT: "Device authorization timed out",
|
|
@@ -65,14 +109,27 @@ export const ERROR_MESSAGES = {
|
|
|
65
109
|
REQUEST_PARSE_ERROR: "Error parsing request",
|
|
66
110
|
NO_RESOURCE_URL: "No resource_url in token response, using default",
|
|
67
111
|
};
|
|
68
|
-
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* OAuth error codes from RFC 8628 Device Flow
|
|
115
|
+
* @namespace
|
|
116
|
+
*/
|
|
69
117
|
export const OAUTH_ERRORS = {
|
|
118
|
+
/** User has not yet authorized the device code */
|
|
70
119
|
AUTHORIZATION_PENDING: "authorization_pending",
|
|
120
|
+
/** Server requests slower polling (slow_down error) */
|
|
71
121
|
SLOW_DOWN: "slow_down",
|
|
122
|
+
/** User denied the authorization request */
|
|
72
123
|
ACCESS_DENIED: "access_denied",
|
|
124
|
+
/** Device code has expired */
|
|
73
125
|
EXPIRED_TOKEN: "expired_token",
|
|
74
126
|
};
|
|
75
|
-
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Log stages for request logging
|
|
130
|
+
* Used for debugging and tracing request lifecycle
|
|
131
|
+
* @namespace
|
|
132
|
+
*/
|
|
76
133
|
export const LOG_STAGES = {
|
|
77
134
|
BEFORE_TRANSFORM: "before-transform",
|
|
78
135
|
AFTER_TRANSFORM: "after-transform",
|
|
@@ -81,29 +138,53 @@ export const LOG_STAGES = {
|
|
|
81
138
|
DEVICE_CODE_REQUEST: "device-code-request",
|
|
82
139
|
TOKEN_POLL: "token-poll",
|
|
83
140
|
};
|
|
84
|
-
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Platform-specific browser opener commands
|
|
144
|
+
* Used for opening OAuth verification URL in default browser
|
|
145
|
+
* @namespace
|
|
146
|
+
*/
|
|
85
147
|
export const PLATFORM_OPENERS = {
|
|
86
148
|
darwin: "open",
|
|
87
149
|
win32: "start",
|
|
88
150
|
linux: "xdg-open",
|
|
89
151
|
};
|
|
90
|
-
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* OAuth authorization labels for UI display
|
|
155
|
+
* @namespace
|
|
156
|
+
*/
|
|
91
157
|
export const AUTH_LABELS = {
|
|
158
|
+
/** Label shown in OpenCode auth provider selection */
|
|
92
159
|
OAUTH: "Qwen Code (qwen.ai OAuth)",
|
|
160
|
+
/** Instructions shown to user during OAuth flow */
|
|
93
161
|
INSTRUCTIONS: "Visit the URL shown in your browser to complete authentication.",
|
|
94
162
|
};
|
|
95
|
-
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* OAuth verification URI parameters
|
|
166
|
+
* Used to construct complete verification URL with client identification
|
|
167
|
+
* @namespace
|
|
168
|
+
*/
|
|
96
169
|
export const VERIFICATION_URI = {
|
|
97
170
|
/** Query parameter key for client identification */
|
|
98
171
|
CLIENT_PARAM_KEY: "client=",
|
|
99
172
|
/** Full query parameter for Qwen Code client */
|
|
100
173
|
CLIENT_PARAM_VALUE: "client=qwen-code",
|
|
101
174
|
};
|
|
102
|
-
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Token refresh buffer in milliseconds
|
|
178
|
+
* Tokens are refreshed 30 seconds before expiry to avoid race conditions
|
|
179
|
+
* @constant {number}
|
|
180
|
+
*/
|
|
103
181
|
export const TOKEN_REFRESH_BUFFER_MS = 30 * 1000; // 30 seconds
|
|
104
|
-
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Stream processing configuration
|
|
185
|
+
* @namespace
|
|
186
|
+
*/
|
|
105
187
|
export const STREAM_CONFIG = {
|
|
106
188
|
/** Maximum buffer size for SSE pass-through mode (1MB) */
|
|
107
189
|
MAX_BUFFER_SIZE: 1024 * 1024,
|
|
108
190
|
};
|
|
109
|
-
//# sourceMappingURL=constants.js.map
|
package/dist/lib/logger.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Logging utilities for Qwen OAuth Plugin
|
|
3
|
+
* Provides configurable logging for debugging and request tracing
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
2
8
|
import { join } from "node:path";
|
|
3
9
|
import { homedir } from "node:os";
|
|
4
|
-
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Flag to enable request logging to file
|
|
13
|
+
* Controlled by ENABLE_PLUGIN_REQUEST_LOGGING environment variable
|
|
14
|
+
* @constant {boolean}
|
|
15
|
+
*/
|
|
5
16
|
export const LOGGING_ENABLED = process.env.ENABLE_PLUGIN_REQUEST_LOGGING === "1";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Flag to enable debug logging to console
|
|
20
|
+
* Controlled by DEBUG_QWEN_PLUGIN or ENABLE_PLUGIN_REQUEST_LOGGING environment variables
|
|
21
|
+
* @constant {boolean}
|
|
22
|
+
*/
|
|
6
23
|
export const DEBUG_ENABLED = process.env.DEBUG_QWEN_PLUGIN === "1" || LOGGING_ENABLED;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Directory path for log files
|
|
27
|
+
* Logs are stored in ~/.opencode/logs/qwen-plugin/
|
|
28
|
+
* @constant {string}
|
|
29
|
+
*/
|
|
7
30
|
const LOG_DIR = join(homedir(), ".opencode", "logs", "qwen-plugin");
|
|
31
|
+
|
|
8
32
|
// Log startup message about logging state
|
|
9
33
|
if (LOGGING_ENABLED) {
|
|
10
34
|
console.log("[qwen-oauth-plugin] Request logging ENABLED - logs will be saved to:", LOG_DIR);
|
|
@@ -12,23 +36,34 @@ if (LOGGING_ENABLED) {
|
|
|
12
36
|
if (DEBUG_ENABLED && !LOGGING_ENABLED) {
|
|
13
37
|
console.log("[qwen-oauth-plugin] Debug logging ENABLED");
|
|
14
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Request counter for generating unique request IDs in logs
|
|
42
|
+
* @type {number}
|
|
43
|
+
*/
|
|
15
44
|
let requestCounter = 0;
|
|
45
|
+
|
|
16
46
|
/**
|
|
17
47
|
* Log request data to file (only when LOGGING_ENABLED is true)
|
|
18
|
-
*
|
|
19
|
-
* @param
|
|
48
|
+
* Creates JSON files with request/response data for debugging
|
|
49
|
+
* @param {string} stage - The stage of the request (e.g., "before-transform", "after-transform", "response")
|
|
50
|
+
* @param {Object} data - The data to log (request/response objects, metadata, etc.)
|
|
51
|
+
* @returns {void}
|
|
20
52
|
*/
|
|
21
53
|
export function logRequest(stage, data) {
|
|
22
54
|
// Only log if explicitly enabled via environment variable
|
|
23
55
|
if (!LOGGING_ENABLED)
|
|
24
56
|
return;
|
|
57
|
+
|
|
25
58
|
// Ensure log directory exists on first log
|
|
26
59
|
if (!existsSync(LOG_DIR)) {
|
|
27
60
|
mkdirSync(LOG_DIR, { recursive: true });
|
|
28
61
|
}
|
|
62
|
+
|
|
29
63
|
const timestamp = new Date().toISOString();
|
|
30
64
|
const requestId = ++requestCounter;
|
|
31
65
|
const filename = join(LOG_DIR, `request-${requestId}-${stage}.json`);
|
|
66
|
+
|
|
32
67
|
try {
|
|
33
68
|
writeFileSync(filename, JSON.stringify({
|
|
34
69
|
timestamp,
|
|
@@ -43,10 +78,13 @@ export function logRequest(stage, data) {
|
|
|
43
78
|
console.error("[qwen-oauth-plugin] Failed to write log:", error.message);
|
|
44
79
|
}
|
|
45
80
|
}
|
|
81
|
+
|
|
46
82
|
/**
|
|
47
83
|
* Log debug information (only when DEBUG_ENABLED is true)
|
|
48
|
-
*
|
|
49
|
-
* @param
|
|
84
|
+
* Used for detailed debugging during development
|
|
85
|
+
* @param {string} message - Debug message describing the context
|
|
86
|
+
* @param {*} [data] - Optional data to log (objects, values, etc.)
|
|
87
|
+
* @returns {void}
|
|
50
88
|
*/
|
|
51
89
|
export function logDebug(message, data) {
|
|
52
90
|
if (!DEBUG_ENABLED)
|
|
@@ -58,10 +96,13 @@ export function logDebug(message, data) {
|
|
|
58
96
|
console.log(`[qwen-oauth-plugin] ${message}`);
|
|
59
97
|
}
|
|
60
98
|
}
|
|
99
|
+
|
|
61
100
|
/**
|
|
62
101
|
* Log error (always enabled for important issues)
|
|
63
|
-
*
|
|
64
|
-
* @param
|
|
102
|
+
* Used for critical errors that need attention
|
|
103
|
+
* @param {string} message - Error message describing what went wrong
|
|
104
|
+
* @param {*} [data] - Optional data to log (error objects, context, etc.)
|
|
105
|
+
* @returns {void}
|
|
65
106
|
*/
|
|
66
107
|
export function logError(message, data) {
|
|
67
108
|
if (data !== undefined) {
|
|
@@ -71,10 +112,13 @@ export function logError(message, data) {
|
|
|
71
112
|
console.error(`[qwen-oauth-plugin] ${message}`);
|
|
72
113
|
}
|
|
73
114
|
}
|
|
115
|
+
|
|
74
116
|
/**
|
|
75
117
|
* Log warning (always enabled for important issues)
|
|
76
|
-
*
|
|
77
|
-
* @param
|
|
118
|
+
* Used for non-critical issues that may need attention
|
|
119
|
+
* @param {string} message - Warning message describing the issue
|
|
120
|
+
* @param {*} [data] - Optional data to log (context, values, etc.)
|
|
121
|
+
* @returns {void}
|
|
78
122
|
*/
|
|
79
123
|
export function logWarn(message, data) {
|
|
80
124
|
if (data !== undefined) {
|
|
@@ -84,10 +128,13 @@ export function logWarn(message, data) {
|
|
|
84
128
|
console.warn(`[qwen-oauth-plugin] ${message}`);
|
|
85
129
|
}
|
|
86
130
|
}
|
|
131
|
+
|
|
87
132
|
/**
|
|
88
133
|
* Log info message (always enabled)
|
|
89
|
-
*
|
|
90
|
-
* @param
|
|
134
|
+
* Used for general informational messages
|
|
135
|
+
* @param {string} message - Info message describing the event
|
|
136
|
+
* @param {*} [data] - Optional data to log (context, values, etc.)
|
|
137
|
+
* @returns {void}
|
|
91
138
|
*/
|
|
92
139
|
export function logInfo(message, data) {
|
|
93
140
|
if (data !== undefined) {
|
|
@@ -97,4 +144,3 @@ export function logInfo(message, data) {
|
|
|
97
144
|
console.log(`[qwen-oauth-plugin] ${message}`);
|
|
98
145
|
}
|
|
99
146
|
}
|
|
100
|
-
//# sourceMappingURL=logger.js.map
|
package/package.json
CHANGED