opencode-qwen-cli-auth 2.2.9 → 2.3.1
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 +273 -62
- package/README.vi.md +273 -0
- package/dist/index.js +413 -92
- package/dist/lib/auth/auth.d.ts +51 -1
- package/dist/lib/auth/auth.js +738 -3
- package/dist/lib/auth/browser.js +14 -4
- package/dist/lib/config.d.ts +8 -0
- package/dist/lib/config.js +99 -1
- package/dist/lib/constants.js +99 -18
- package/dist/lib/logger.js +58 -12
- package/package.json +1 -1
package/dist/lib/auth/browser.js
CHANGED
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Browser utilities for OAuth flow
|
|
3
|
-
* Handles platform-specific browser opening
|
|
2
|
+
* @fileoverview Browser utilities for OAuth flow
|
|
3
|
+
* Handles platform-specific browser opening for OAuth authorization URL
|
|
4
|
+
* @license MIT
|
|
4
5
|
*/
|
|
6
|
+
|
|
5
7
|
import { spawn } from "node:child_process";
|
|
6
8
|
import { PLATFORM_OPENERS } from "../constants.js";
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* Gets the platform-specific command to open a URL in the default browser
|
|
9
|
-
* @returns Browser opener command for the current platform
|
|
12
|
+
* @returns {string} Browser opener command for the current platform (darwin: 'open', win32: 'start', linux: 'xdg-open')
|
|
10
13
|
*/
|
|
11
14
|
export function getBrowserOpener() {
|
|
12
15
|
const platform = process.platform;
|
|
16
|
+
// macOS uses 'open' command
|
|
13
17
|
if (platform === "darwin")
|
|
14
18
|
return PLATFORM_OPENERS.darwin;
|
|
19
|
+
// Windows uses 'start' command
|
|
15
20
|
if (platform === "win32")
|
|
16
21
|
return PLATFORM_OPENERS.win32;
|
|
22
|
+
// Linux uses 'xdg-open' command
|
|
17
23
|
return PLATFORM_OPENERS.linux;
|
|
18
24
|
}
|
|
25
|
+
|
|
19
26
|
/**
|
|
20
27
|
* Opens a URL in the default browser
|
|
21
28
|
* Silently fails if browser cannot be opened (user can copy URL manually)
|
|
22
|
-
* @param url - URL to open
|
|
29
|
+
* @param {string} url - The URL to open in browser (typically OAuth verification URL)
|
|
30
|
+
* @returns {void}
|
|
23
31
|
*/
|
|
24
32
|
export function openBrowserUrl(url) {
|
|
25
33
|
try {
|
|
26
34
|
const opener = getBrowserOpener();
|
|
35
|
+
// Spawn browser process with detached stdio to avoid blocking
|
|
27
36
|
spawn(opener, [url], {
|
|
28
37
|
stdio: "ignore",
|
|
38
|
+
// Use shell on Windows for 'start' command to work properly
|
|
29
39
|
shell: process.platform === "win32",
|
|
30
40
|
});
|
|
31
41
|
}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -29,6 +29,14 @@ export declare function getTokenPath(): string;
|
|
|
29
29
|
* Get token lock path for multi-process refresh coordination
|
|
30
30
|
*/
|
|
31
31
|
export declare function getTokenLockPath(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get multi-account storage path
|
|
34
|
+
*/
|
|
35
|
+
export declare function getAccountsPath(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Get multi-account lock path
|
|
38
|
+
*/
|
|
39
|
+
export declare function getAccountsLockPath(): string;
|
|
32
40
|
/**
|
|
33
41
|
* Get legacy token storage path used by old plugin versions
|
|
34
42
|
*/
|
package/dist/lib/config.js
CHANGED
|
@@ -1,30 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Configuration utilities for Qwen OAuth Plugin
|
|
3
|
+
* Manages paths for configuration, tokens, and cache directories
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { homedir } from "os";
|
|
2
|
-
import { join } from "path";
|
|
8
|
+
import { join, resolve, relative, isAbsolute, parse, dirname } from "path";
|
|
3
9
|
import { readFileSync, existsSync } from "fs";
|
|
10
|
+
|
|
4
11
|
/**
|
|
5
12
|
* Get plugin configuration directory
|
|
13
|
+
* @returns {string} Path to ~/.opencode/qwen/
|
|
6
14
|
*/
|
|
7
15
|
export function getConfigDir() {
|
|
8
16
|
return join(homedir(), ".opencode", "qwen");
|
|
9
17
|
}
|
|
18
|
+
|
|
10
19
|
/**
|
|
11
20
|
* Get Qwen CLI credential directory (~/.qwen)
|
|
21
|
+
* This directory is shared with the official qwen-code CLI for token storage
|
|
22
|
+
* @returns {string} Path to ~/.qwen/
|
|
12
23
|
*/
|
|
13
24
|
export function getQwenDir() {
|
|
14
25
|
return join(homedir(), ".qwen");
|
|
15
26
|
}
|
|
27
|
+
|
|
28
|
+
const ACCOUNTS_FILENAME = "oauth_accounts.json";
|
|
29
|
+
const DEFAULT_ACCOUNTS_PATH = join(getQwenDir(), ACCOUNTS_FILENAME);
|
|
30
|
+
|
|
31
|
+
function isRootPath(pathValue) {
|
|
32
|
+
const parsed = parse(pathValue);
|
|
33
|
+
return pathValue === parsed.root;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isPathInsideBase(pathValue, baseDir) {
|
|
37
|
+
const rel = relative(baseDir, pathValue);
|
|
38
|
+
if (rel === "") {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveAccountsPathFromEnv() {
|
|
45
|
+
const envPath = process.env.OPENCODE_QWEN_ACCOUNTS_PATH;
|
|
46
|
+
if (typeof envPath !== "string" || envPath.trim().length === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const trimmed = envPath.trim();
|
|
50
|
+
if (trimmed.includes("\0")) {
|
|
51
|
+
console.warn("[qwen-oauth-plugin] Ignoring OPENCODE_QWEN_ACCOUNTS_PATH with invalid null-byte");
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const resolved = resolve(trimmed);
|
|
55
|
+
if (isRootPath(resolved)) {
|
|
56
|
+
console.warn("[qwen-oauth-plugin] Ignoring OPENCODE_QWEN_ACCOUNTS_PATH pointing to root path");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const baseDir = getQwenDir();
|
|
60
|
+
if (!isPathInsideBase(resolved, baseDir)) {
|
|
61
|
+
console.warn("[qwen-oauth-plugin] Ignoring OPENCODE_QWEN_ACCOUNTS_PATH outside ~/.qwen for safety");
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const parsed = parse(resolved);
|
|
65
|
+
if (!parsed.base || parsed.base.length === 0) {
|
|
66
|
+
console.warn("[qwen-oauth-plugin] Ignoring OPENCODE_QWEN_ACCOUNTS_PATH without filename");
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return resolved;
|
|
70
|
+
}
|
|
71
|
+
|
|
16
72
|
/**
|
|
17
73
|
* Get plugin configuration file path
|
|
74
|
+
* @returns {string} Path to ~/.opencode/qwen/auth-config.json
|
|
18
75
|
*/
|
|
19
76
|
export function getConfigPath() {
|
|
20
77
|
return join(getConfigDir(), "auth-config.json");
|
|
21
78
|
}
|
|
79
|
+
|
|
22
80
|
/**
|
|
23
81
|
* Load plugin configuration from ~/.opencode/qwen/auth-config.json
|
|
24
82
|
* Returns default config if file doesn't exist
|
|
83
|
+
* @returns {{ qwenMode: boolean }} Configuration object with qwenMode flag
|
|
25
84
|
*/
|
|
26
85
|
export function loadPluginConfig() {
|
|
27
86
|
const configPath = getConfigPath();
|
|
87
|
+
// Return default config if config file doesn't exist
|
|
28
88
|
if (!existsSync(configPath)) {
|
|
29
89
|
return { qwenMode: true }; // Default: QWEN_MODE enabled
|
|
30
90
|
}
|
|
@@ -33,15 +93,20 @@ export function loadPluginConfig() {
|
|
|
33
93
|
return JSON.parse(content);
|
|
34
94
|
}
|
|
35
95
|
catch (error) {
|
|
96
|
+
// Log warning and return default config on parse error
|
|
36
97
|
console.warn(`[qwen-oauth-plugin] Failed to load config from ${configPath}:`, error);
|
|
37
98
|
return { qwenMode: true };
|
|
38
99
|
}
|
|
39
100
|
}
|
|
101
|
+
|
|
40
102
|
/**
|
|
41
103
|
* Get QWEN_MODE setting
|
|
42
104
|
* Priority: QWEN_MODE env var > config file > default (true)
|
|
105
|
+
* @param {{ qwenMode?: boolean|string|null }} config - Configuration object from file
|
|
106
|
+
* @returns {boolean} True if QWEN_MODE is enabled, false otherwise
|
|
43
107
|
*/
|
|
44
108
|
export function getQwenMode(config) {
|
|
109
|
+
// Environment variable takes highest priority
|
|
45
110
|
const envValue = process.env.QWEN_MODE;
|
|
46
111
|
if (envValue !== undefined) {
|
|
47
112
|
return envValue === "1" || envValue.toLowerCase() === "true";
|
|
@@ -49,31 +114,64 @@ export function getQwenMode(config) {
|
|
|
49
114
|
// Ensure boolean type, avoid string "false" being truthy
|
|
50
115
|
const val = config.qwenMode;
|
|
51
116
|
if (val === undefined || val === null) return true; // default: enabled
|
|
117
|
+
// Handle string values from config file
|
|
52
118
|
if (typeof val === "string") {
|
|
53
119
|
return val === "1" || val.toLowerCase() === "true";
|
|
54
120
|
}
|
|
121
|
+
// Convert to boolean for actual boolean values
|
|
55
122
|
return !!val;
|
|
56
123
|
}
|
|
124
|
+
|
|
57
125
|
/**
|
|
58
126
|
* Get token storage path
|
|
127
|
+
* Token file contains OAuth credentials: access_token, refresh_token, expiry_date, resource_url
|
|
128
|
+
* @returns {string} Path to ~/.qwen/oauth_creds.json
|
|
59
129
|
*/
|
|
60
130
|
export function getTokenPath() {
|
|
61
131
|
return join(getQwenDir(), "oauth_creds.json");
|
|
62
132
|
}
|
|
133
|
+
|
|
63
134
|
/**
|
|
64
135
|
* Get token lock path for multi-process refresh coordination
|
|
136
|
+
* Prevents concurrent token refresh operations across multiple processes
|
|
137
|
+
* @returns {string} Path to ~/.qwen/oauth_creds.lock
|
|
65
138
|
*/
|
|
66
139
|
export function getTokenLockPath() {
|
|
67
140
|
return join(getQwenDir(), "oauth_creds.lock");
|
|
68
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get multi-account storage path
|
|
145
|
+
* @returns {string} Path to ~/.qwen/oauth_accounts.json or OPENCODE_QWEN_ACCOUNTS_PATH override
|
|
146
|
+
*/
|
|
147
|
+
export function getAccountsPath() {
|
|
148
|
+
return resolveAccountsPathFromEnv() || DEFAULT_ACCOUNTS_PATH;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get multi-account lock path
|
|
153
|
+
* @returns {string} Path to ~/.qwen/oauth_accounts.lock (or sidecar lock for override path)
|
|
154
|
+
*/
|
|
155
|
+
export function getAccountsLockPath() {
|
|
156
|
+
const accountsPath = getAccountsPath();
|
|
157
|
+
if (dirname(accountsPath) !== getQwenDir()) {
|
|
158
|
+
return `${accountsPath}.lock`;
|
|
159
|
+
}
|
|
160
|
+
return join(getQwenDir(), "oauth_accounts.lock");
|
|
161
|
+
}
|
|
162
|
+
|
|
69
163
|
/**
|
|
70
164
|
* Get legacy token storage path used by old plugin versions
|
|
165
|
+
* Used for backward compatibility and token migration
|
|
166
|
+
* @returns {string} Path to ~/.opencode/qwen/oauth_token.json
|
|
71
167
|
*/
|
|
72
168
|
export function getLegacyTokenPath() {
|
|
73
169
|
return join(getConfigDir(), "oauth_token.json");
|
|
74
170
|
}
|
|
171
|
+
|
|
75
172
|
/**
|
|
76
173
|
* Get cache directory for prompts
|
|
174
|
+
* @returns {string} Path to ~/.opencode/cache/
|
|
77
175
|
*/
|
|
78
176
|
export function getCacheDir() {
|
|
79
177
|
return join(homedir(), ".opencode", "cache");
|
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