opencode-qwen-oauth 1.0.1 → 2.0.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 +59 -1
- package/dist/browser.d.ts +10 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +87 -0
- package/dist/browser.js.map +1 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +79 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +105 -205
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +15 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +79 -0
- package/dist/logger.js.map +1 -0
- package/dist/mutex.d.ts +30 -0
- package/dist/mutex.d.ts.map +1 -0
- package/dist/mutex.js +90 -0
- package/dist/mutex.js.map +1 -0
- package/dist/oauth.d.ts +33 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +262 -0
- package/dist/oauth.js.map +1 -0
- package/dist/pkce.d.ts +8 -0
- package/dist/pkce.d.ts.map +1 -0
- package/dist/pkce.js +17 -0
- package/dist/pkce.js.map +1 -0
- package/dist/retry.d.ts +18 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +74 -0
- package/dist/retry.js.map +1 -0
- package/dist/validation.d.ts +39 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +125 -0
- package/dist/validation.js.map +1 -0
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -4,177 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @packageDocumentation
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
// ============================================
|
|
15
|
-
const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
|
16
|
-
const QWEN_DEVICE_CODE_ENDPOINT = "/api/v1/oauth2/device/code";
|
|
17
|
-
const QWEN_TOKEN_ENDPOINT = "/api/v1/oauth2/token";
|
|
18
|
-
const QWEN_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56";
|
|
19
|
-
const QWEN_SCOPES = ["openid", "profile", "email", "model.completion"];
|
|
20
|
-
const QWEN_API_BASE_URL = "https://portal.qwen.ai/v1";
|
|
21
|
-
// ============================================
|
|
22
|
-
// Logging
|
|
23
|
-
// ============================================
|
|
24
|
-
function getLogDir() {
|
|
25
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
26
|
-
return join(xdgConfig, "opencode", "logs");
|
|
27
|
-
}
|
|
28
|
-
function getLogFilePath() {
|
|
29
|
-
return join(getLogDir(), "qwen-oauth.log");
|
|
30
|
-
}
|
|
31
|
-
function ensureLogDir() {
|
|
32
|
-
const logDir = getLogDir();
|
|
33
|
-
if (!existsSync(logDir)) {
|
|
34
|
-
mkdirSync(logDir, { recursive: true, mode: 0o700 });
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function writeLog(message) {
|
|
38
|
-
try {
|
|
39
|
-
ensureLogDir();
|
|
40
|
-
const timestamp = new Date().toISOString();
|
|
41
|
-
const logLine = `[${timestamp}] ${message}\n`;
|
|
42
|
-
appendFileSync(getLogFilePath(), logLine, { encoding: "utf-8" });
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Silently ignore log write errors
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const DEBUG = process.env.QWEN_OAUTH_DEBUG === "true" ||
|
|
49
|
-
process.env.QWEN_OAUTH_DEBUG === "1";
|
|
50
|
-
function debugLog(message, data) {
|
|
51
|
-
const logMessage = data ? `${message} ${JSON.stringify(data)}` : message;
|
|
52
|
-
writeLog(logMessage);
|
|
53
|
-
}
|
|
54
|
-
// ============================================
|
|
55
|
-
// PKCE
|
|
56
|
-
// ============================================
|
|
57
|
-
function base64UrlEncode(buffer) {
|
|
58
|
-
return buffer
|
|
59
|
-
.toString("base64")
|
|
60
|
-
.replace(/\+/g, "-")
|
|
61
|
-
.replace(/\//g, "_")
|
|
62
|
-
.replace(/=+$/, "");
|
|
63
|
-
}
|
|
64
|
-
function createPkcePair() {
|
|
65
|
-
const verifier = base64UrlEncode(randomBytes(32));
|
|
66
|
-
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
67
|
-
return { verifier, challenge };
|
|
68
|
-
}
|
|
69
|
-
// ============================================
|
|
70
|
-
// Browser
|
|
71
|
-
// ============================================
|
|
72
|
-
function openBrowser(url) {
|
|
73
|
-
try {
|
|
74
|
-
const platform = process.platform;
|
|
75
|
-
const command = platform === "darwin"
|
|
76
|
-
? "open"
|
|
77
|
-
: platform === "win32"
|
|
78
|
-
? "rundll32"
|
|
79
|
-
: "xdg-open";
|
|
80
|
-
const args = platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
81
|
-
const child = spawn(command, args, {
|
|
82
|
-
stdio: "ignore",
|
|
83
|
-
detached: true,
|
|
84
|
-
});
|
|
85
|
-
child.unref?.();
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
// Ignore errors
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function authorizeDevice() {
|
|
92
|
-
const { verifier, challenge } = createPkcePair();
|
|
93
|
-
const params = new URLSearchParams({
|
|
94
|
-
client_id: QWEN_CLIENT_ID,
|
|
95
|
-
scope: QWEN_SCOPES.join(" "),
|
|
96
|
-
code_challenge: challenge,
|
|
97
|
-
code_challenge_method: "S256",
|
|
98
|
-
});
|
|
99
|
-
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_DEVICE_CODE_ENDPOINT}`, {
|
|
100
|
-
method: "POST",
|
|
101
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
102
|
-
body: params.toString(),
|
|
103
|
-
});
|
|
104
|
-
if (!response.ok) {
|
|
105
|
-
throw new Error(`Failed to start device flow: ${response.statusText}`);
|
|
106
|
-
}
|
|
107
|
-
const data = (await response.json());
|
|
108
|
-
return {
|
|
109
|
-
device_code: data.device_code,
|
|
110
|
-
user_code: data.user_code,
|
|
111
|
-
verification_uri: data.verification_uri,
|
|
112
|
-
verification_uri_complete: data.verification_uri_complete,
|
|
113
|
-
expires_in: data.expires_in,
|
|
114
|
-
interval: data.interval || 5,
|
|
115
|
-
verifier,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
async function pollForToken(deviceCode, codeVerifier, intervalSeconds, expiresIn) {
|
|
119
|
-
const timeoutMs = expiresIn * 1000;
|
|
120
|
-
const startTime = Date.now();
|
|
121
|
-
let currentInterval = intervalSeconds * 1000;
|
|
122
|
-
debugLog("Starting token polling", { timeoutMs, interval: currentInterval });
|
|
123
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
124
|
-
await new Promise((resolve) => setTimeout(resolve, currentInterval));
|
|
125
|
-
const params = new URLSearchParams({
|
|
126
|
-
client_id: QWEN_CLIENT_ID,
|
|
127
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
128
|
-
device_code: deviceCode,
|
|
129
|
-
code_verifier: codeVerifier,
|
|
130
|
-
});
|
|
131
|
-
debugLog("Polling for token...");
|
|
132
|
-
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_TOKEN_ENDPOINT}`, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
135
|
-
body: params.toString(),
|
|
136
|
-
});
|
|
137
|
-
if (response.ok) {
|
|
138
|
-
const data = (await response.json());
|
|
139
|
-
debugLog("Token received successfully");
|
|
140
|
-
return {
|
|
141
|
-
success: true,
|
|
142
|
-
access_token: data.access_token,
|
|
143
|
-
refresh_token: data.refresh_token,
|
|
144
|
-
expires_in: data.expires_in,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
const error = (await response.json().catch(() => ({})));
|
|
148
|
-
if (error.error === "authorization_pending") {
|
|
149
|
-
debugLog("Authorization pending, retrying...");
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
if (error.error === "slow_down") {
|
|
153
|
-
currentInterval += 5000;
|
|
154
|
-
debugLog("Server requested slow down, new interval:", {
|
|
155
|
-
interval: currentInterval,
|
|
156
|
-
});
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (error.error === "expired_token") {
|
|
160
|
-
debugLog("Device code expired");
|
|
161
|
-
return {
|
|
162
|
-
success: false,
|
|
163
|
-
error: "Device code expired. Please try again.",
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
debugLog(`Token polling failed: ${error.error_description || "unknown error"}`);
|
|
167
|
-
return {
|
|
168
|
-
success: false,
|
|
169
|
-
error: error.error_description || "Authentication failed",
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
debugLog("Polling timeout exceeded");
|
|
173
|
-
return { success: false, error: "Polling timeout - device code expired" };
|
|
174
|
-
}
|
|
175
|
-
// ============================================
|
|
176
|
-
// Plugin
|
|
177
|
-
// ============================================
|
|
7
|
+
import { QWEN_API_BASE_URL } from "./constants.js";
|
|
8
|
+
import { debugLog, warnLog } from "./logger.js";
|
|
9
|
+
import { openBrowser } from "./browser.js";
|
|
10
|
+
import { authorizeDevice, pollForToken } from "./oauth.js";
|
|
11
|
+
import { Mutex } from "./mutex.js";
|
|
12
|
+
// Mutex to prevent multiple concurrent authorization flows
|
|
13
|
+
const authorizationMutex = new Mutex();
|
|
178
14
|
export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree, }) => {
|
|
179
15
|
debugLog("Plugin initialized", {
|
|
180
16
|
directory,
|
|
@@ -189,40 +25,47 @@ export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree,
|
|
|
189
25
|
type: "oauth",
|
|
190
26
|
label: "Qwen Code (qwen.ai OAuth)",
|
|
191
27
|
authorize: async () => {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
28
|
+
// Check if authorization is already in progress
|
|
29
|
+
if (authorizationMutex.isLocked()) {
|
|
30
|
+
warnLog("Authorization already in progress");
|
|
31
|
+
throw new Error("Authorization already in progress. Please wait for the current flow to complete.");
|
|
32
|
+
}
|
|
33
|
+
return authorizationMutex.runExclusive(async () => {
|
|
34
|
+
debugLog("Starting Qwen OAuth device flow...");
|
|
35
|
+
const device = await authorizeDevice();
|
|
36
|
+
const url = device.verification_uri_complete || device.verification_uri;
|
|
37
|
+
// Try to open browser automatically
|
|
38
|
+
openBrowser(url);
|
|
39
|
+
debugLog("Device authorization received", {
|
|
40
|
+
user_code: device.user_code,
|
|
41
|
+
verification_uri: device.verification_uri,
|
|
42
|
+
expires_in: device.expires_in,
|
|
43
|
+
interval: device.interval,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
url,
|
|
47
|
+
instructions: `Enter code: ${device.user_code}`,
|
|
48
|
+
method: "auto",
|
|
49
|
+
callback: async () => {
|
|
50
|
+
debugLog("Polling for OAuth token...");
|
|
51
|
+
const result = await pollForToken(device.device_code, device.verifier, device.interval, device.expires_in);
|
|
52
|
+
if (result.success) {
|
|
53
|
+
debugLog("Qwen authentication successful!", {
|
|
54
|
+
expires_in: result.expires_in,
|
|
55
|
+
has_refresh: !!result.refresh_token,
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
type: "success",
|
|
59
|
+
access: result.access_token,
|
|
60
|
+
refresh: result.refresh_token,
|
|
61
|
+
expires: Date.now() + result.expires_in * 1000,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
debugLog(`Authentication failed: ${result.error}`);
|
|
65
|
+
return { type: "failed", error: result.error };
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}); // End runExclusive
|
|
226
69
|
},
|
|
227
70
|
},
|
|
228
71
|
],
|
|
@@ -249,6 +92,63 @@ export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree,
|
|
|
249
92
|
},
|
|
250
93
|
};
|
|
251
94
|
},
|
|
95
|
+
// Event monitoring hook
|
|
96
|
+
event: async ({ event }) => {
|
|
97
|
+
// Log important events for debugging
|
|
98
|
+
if (event.type === "session.error") {
|
|
99
|
+
debugLog("Session error occurred", {
|
|
100
|
+
type: event.type,
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (event.type === "session.created") {
|
|
105
|
+
debugLog("New session created", {
|
|
106
|
+
type: event.type,
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
// Inject custom headers for Qwen API requests
|
|
112
|
+
"chat.headers": async (input, output) => {
|
|
113
|
+
// Only add headers for Qwen provider
|
|
114
|
+
if (input.provider.info.id === "qwen") {
|
|
115
|
+
debugLog("Adding custom headers for Qwen request", {
|
|
116
|
+
model: input.model.id,
|
|
117
|
+
session: input.sessionID,
|
|
118
|
+
});
|
|
119
|
+
// Add any custom headers needed for Qwen
|
|
120
|
+
// OpenCode will automatically handle the Authorization header
|
|
121
|
+
output.headers["X-Qwen-Client"] = "OpenCode";
|
|
122
|
+
output.headers["X-Qwen-Plugin-Version"] = "1.1.0";
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
// Customize model parameters for Qwen
|
|
126
|
+
"chat.params": async (input, output) => {
|
|
127
|
+
if (input.provider.info.id === "qwen") {
|
|
128
|
+
debugLog("Customizing parameters for Qwen model", {
|
|
129
|
+
model: input.model.id,
|
|
130
|
+
current_temp: output.temperature,
|
|
131
|
+
});
|
|
132
|
+
// Qwen models work well with these defaults
|
|
133
|
+
// Users can override these in their config
|
|
134
|
+
if (output.temperature === undefined) {
|
|
135
|
+
output.temperature = 0.7;
|
|
136
|
+
}
|
|
137
|
+
if (output.topP === undefined) {
|
|
138
|
+
output.topP = 0.95;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
// Expose Qwen credentials as environment variables if needed
|
|
143
|
+
"shell.env": async (input, output) => {
|
|
144
|
+
debugLog("Setting up shell environment", {
|
|
145
|
+
cwd: input.cwd,
|
|
146
|
+
hasSession: !!input.sessionID,
|
|
147
|
+
});
|
|
148
|
+
// Add Qwen-specific environment variables
|
|
149
|
+
output.env.QWEN_API_BASE_URL = QWEN_API_BASE_URL;
|
|
150
|
+
output.env.QWEN_PROVIDER = "qwen";
|
|
151
|
+
},
|
|
252
152
|
};
|
|
253
153
|
};
|
|
254
154
|
export default QwenOAuthPlugin;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for Qwen OAuth Plugin
|
|
3
|
+
*/
|
|
4
|
+
export declare enum LogLevel {
|
|
5
|
+
DEBUG = 0,
|
|
6
|
+
INFO = 1,
|
|
7
|
+
WARN = 2,
|
|
8
|
+
ERROR = 3
|
|
9
|
+
}
|
|
10
|
+
export declare const DEBUG: boolean;
|
|
11
|
+
export declare function debugLog(message: string, data?: Record<string, unknown>): void;
|
|
12
|
+
export declare function infoLog(message: string, data?: Record<string, unknown>): void;
|
|
13
|
+
export declare function warnLog(message: string, data?: Record<string, unknown>): void;
|
|
14
|
+
export declare function errorLog(message: string, data?: Record<string, unknown>): void;
|
|
15
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AA2DD,eAAO,MAAM,KAAK,SAEoB,CAAC;AAEvC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE9E;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE7E;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE7E;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE9E"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for Qwen OAuth Plugin
|
|
3
|
+
*/
|
|
4
|
+
import { appendFileSync, mkdirSync, existsSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { sanitizeLogData } from "./validation.js";
|
|
8
|
+
export var LogLevel;
|
|
9
|
+
(function (LogLevel) {
|
|
10
|
+
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
|
|
11
|
+
LogLevel[LogLevel["INFO"] = 1] = "INFO";
|
|
12
|
+
LogLevel[LogLevel["WARN"] = 2] = "WARN";
|
|
13
|
+
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
|
|
14
|
+
})(LogLevel || (LogLevel = {}));
|
|
15
|
+
function getLogDir() {
|
|
16
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
17
|
+
return join(xdgConfig, "opencode", "logs");
|
|
18
|
+
}
|
|
19
|
+
function getLogFilePath() {
|
|
20
|
+
return join(getLogDir(), "qwen-oauth.log");
|
|
21
|
+
}
|
|
22
|
+
function ensureLogDir() {
|
|
23
|
+
const logDir = getLogDir();
|
|
24
|
+
if (!existsSync(logDir)) {
|
|
25
|
+
mkdirSync(logDir, { recursive: true, mode: 0o700 });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getCurrentLogLevel() {
|
|
29
|
+
const level = process.env.QWEN_OAUTH_LOG_LEVEL?.toUpperCase();
|
|
30
|
+
switch (level) {
|
|
31
|
+
case "ERROR":
|
|
32
|
+
return LogLevel.ERROR;
|
|
33
|
+
case "WARN":
|
|
34
|
+
return LogLevel.WARN;
|
|
35
|
+
case "INFO":
|
|
36
|
+
return LogLevel.INFO;
|
|
37
|
+
case "DEBUG":
|
|
38
|
+
return LogLevel.DEBUG;
|
|
39
|
+
default:
|
|
40
|
+
// Default to INFO, unless DEBUG env var is set
|
|
41
|
+
return process.env.QWEN_OAUTH_DEBUG === "true"
|
|
42
|
+
? LogLevel.DEBUG
|
|
43
|
+
: LogLevel.INFO;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function writeLog(level, message, data) {
|
|
47
|
+
try {
|
|
48
|
+
const currentLevel = getCurrentLogLevel();
|
|
49
|
+
if (level < currentLevel) {
|
|
50
|
+
return; // Skip logs below current level
|
|
51
|
+
}
|
|
52
|
+
ensureLogDir();
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
const levelName = LogLevel[level];
|
|
55
|
+
// Sanitize sensitive data
|
|
56
|
+
const sanitizedData = data ? sanitizeLogData(data) : undefined;
|
|
57
|
+
const dataStr = sanitizedData ? ` ${JSON.stringify(sanitizedData)}` : "";
|
|
58
|
+
const logLine = `[${timestamp}] [${levelName}] ${message}${dataStr}\n`;
|
|
59
|
+
appendFileSync(getLogFilePath(), logLine, { encoding: "utf-8" });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Silently ignore log write errors
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export const DEBUG = process.env.QWEN_OAUTH_DEBUG === "true" ||
|
|
66
|
+
process.env.QWEN_OAUTH_DEBUG === "1";
|
|
67
|
+
export function debugLog(message, data) {
|
|
68
|
+
writeLog(LogLevel.DEBUG, message, data);
|
|
69
|
+
}
|
|
70
|
+
export function infoLog(message, data) {
|
|
71
|
+
writeLog(LogLevel.INFO, message, data);
|
|
72
|
+
}
|
|
73
|
+
export function warnLog(message, data) {
|
|
74
|
+
writeLog(LogLevel.WARN, message, data);
|
|
75
|
+
}
|
|
76
|
+
export function errorLog(message, data) {
|
|
77
|
+
writeLog(LogLevel.ERROR, message, data);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/mutex.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple mutex implementation for preventing race conditions
|
|
3
|
+
*/
|
|
4
|
+
export declare class Mutex {
|
|
5
|
+
private locked;
|
|
6
|
+
private queue;
|
|
7
|
+
acquire(): Promise<void>;
|
|
8
|
+
release(): void;
|
|
9
|
+
runExclusive<T>(fn: () => Promise<T>): Promise<T>;
|
|
10
|
+
isLocked(): boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Rate limiter to prevent rapid successive calls
|
|
14
|
+
*/
|
|
15
|
+
export declare class RateLimiter {
|
|
16
|
+
private lastCall;
|
|
17
|
+
private minInterval;
|
|
18
|
+
constructor(minIntervalMs?: number);
|
|
19
|
+
throttle(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Debouncer to prevent rapid repeated calls
|
|
23
|
+
*/
|
|
24
|
+
export declare class Debouncer {
|
|
25
|
+
private timeoutId;
|
|
26
|
+
private lastCallTime;
|
|
27
|
+
debounce<T extends (...args: any[]) => any>(fn: T, delayMs?: number): (...args: Parameters<T>) => Promise<ReturnType<T>>;
|
|
28
|
+
getLastCallTime(): number;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=mutex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.d.ts","sourceRoot":"","sources":["../src/mutex.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAyB;IAEhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,OAAO,IAAI,IAAI;IAST,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IASvD,QAAQ,IAAI,OAAO;CAGpB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,WAAW,CAAS;gBAEhB,aAAa,GAAE,MAAa;IAIlC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAYhC;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,YAAY,CAAa;IAEjC,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxC,EAAE,EAAE,CAAC,EACL,OAAO,GAAE,MAAY,GACpB,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAoBrD,eAAe,IAAI,MAAM;CAG1B"}
|
package/dist/mutex.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple mutex implementation for preventing race conditions
|
|
3
|
+
*/
|
|
4
|
+
import { debugLog } from "./logger.js";
|
|
5
|
+
export class Mutex {
|
|
6
|
+
locked = false;
|
|
7
|
+
queue = [];
|
|
8
|
+
async acquire() {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
if (!this.locked) {
|
|
11
|
+
this.locked = true;
|
|
12
|
+
resolve();
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.queue.push(resolve);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
release() {
|
|
20
|
+
if (this.queue.length > 0) {
|
|
21
|
+
const resolve = this.queue.shift();
|
|
22
|
+
resolve();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
this.locked = false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async runExclusive(fn) {
|
|
29
|
+
await this.acquire();
|
|
30
|
+
try {
|
|
31
|
+
return await fn();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
this.release();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
isLocked() {
|
|
38
|
+
return this.locked;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Rate limiter to prevent rapid successive calls
|
|
43
|
+
*/
|
|
44
|
+
export class RateLimiter {
|
|
45
|
+
lastCall = 0;
|
|
46
|
+
minInterval;
|
|
47
|
+
constructor(minIntervalMs = 1000) {
|
|
48
|
+
this.minInterval = minIntervalMs;
|
|
49
|
+
}
|
|
50
|
+
async throttle() {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
const timeSinceLastCall = now - this.lastCall;
|
|
53
|
+
if (timeSinceLastCall < this.minInterval) {
|
|
54
|
+
const waitTime = this.minInterval - timeSinceLastCall;
|
|
55
|
+
debugLog(`Rate limiting: waiting ${waitTime}ms`);
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
57
|
+
}
|
|
58
|
+
this.lastCall = Date.now();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Debouncer to prevent rapid repeated calls
|
|
63
|
+
*/
|
|
64
|
+
export class Debouncer {
|
|
65
|
+
timeoutId = null;
|
|
66
|
+
lastCallTime = 0;
|
|
67
|
+
debounce(fn, delayMs = 500) {
|
|
68
|
+
return (...args) => {
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
if (this.timeoutId) {
|
|
71
|
+
clearTimeout(this.timeoutId);
|
|
72
|
+
}
|
|
73
|
+
this.timeoutId = setTimeout(async () => {
|
|
74
|
+
try {
|
|
75
|
+
const result = await fn(...args);
|
|
76
|
+
this.lastCallTime = Date.now();
|
|
77
|
+
resolve(result);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
reject(error);
|
|
81
|
+
}
|
|
82
|
+
}, delayMs);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
getLastCallTime() {
|
|
87
|
+
return this.lastCallTime;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=mutex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutex.js","sourceRoot":"","sources":["../src/mutex.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Device Flow implementation for Qwen
|
|
3
|
+
*/
|
|
4
|
+
export interface DeviceAuthorization {
|
|
5
|
+
device_code: string;
|
|
6
|
+
user_code: string;
|
|
7
|
+
verification_uri: string;
|
|
8
|
+
verification_uri_complete: string;
|
|
9
|
+
expires_in: number;
|
|
10
|
+
interval: number;
|
|
11
|
+
verifier: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function authorizeDevice(): Promise<DeviceAuthorization>;
|
|
14
|
+
export declare function pollForToken(deviceCode: string, codeVerifier: string, intervalSeconds: number, expiresIn: number): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
access_token?: string;
|
|
17
|
+
refresh_token?: string;
|
|
18
|
+
expires_in?: number;
|
|
19
|
+
error?: string;
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Refresh an expired access token using a refresh token
|
|
23
|
+
* Uses mutex to prevent race conditions when multiple requests
|
|
24
|
+
* try to refresh the token simultaneously
|
|
25
|
+
*/
|
|
26
|
+
export declare function refreshAccessToken(refreshToken: string): Promise<{
|
|
27
|
+
success: boolean;
|
|
28
|
+
access_token?: string;
|
|
29
|
+
refresh_token?: string;
|
|
30
|
+
expires_in?: number;
|
|
31
|
+
error?: string;
|
|
32
|
+
}>;
|
|
33
|
+
//# sourceMappingURL=oauth.d.ts.map
|