opencode-qwen-cli-auth 1.0.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 +21 -0
- package/README.md +162 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +65 -0
- package/dist/lib/auth/auth.d.ts.map +1 -0
- package/dist/lib/auth/auth.js +419 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/browser.d.ts +16 -0
- package/dist/lib/auth/browser.d.ts.map +1 -0
- package/dist/lib/auth/browser.js +37 -0
- package/dist/lib/auth/browser.js.map +1 -0
- package/dist/lib/config.d.ts +28 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +63 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +105 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +105 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/logger.d.ts +33 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +100 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/prompts/fallback/opencode-qwen-prompt.txt +109 -0
- package/dist/lib/prompts/opencode-qwen.d.ts +14 -0
- package/dist/lib/prompts/opencode-qwen.d.ts.map +1 -0
- package/dist/lib/prompts/opencode-qwen.js +121 -0
- package/dist/lib/prompts/opencode-qwen.js.map +1 -0
- package/dist/lib/prompts/qwen-code.d.ts +25 -0
- package/dist/lib/prompts/qwen-code.d.ts.map +1 -0
- package/dist/lib/prompts/qwen-code.js +307 -0
- package/dist/lib/prompts/qwen-code.js.map +1 -0
- package/dist/lib/prompts/qwen-opencode-bridge.d.ts +15 -0
- package/dist/lib/prompts/qwen-opencode-bridge.d.ts.map +1 -0
- package/dist/lib/prompts/qwen-opencode-bridge.js +81 -0
- package/dist/lib/prompts/qwen-opencode-bridge.js.map +1 -0
- package/dist/lib/request/fetch-helpers.d.ts +19 -0
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/request/fetch-helpers.js +50 -0
- package/dist/lib/request/fetch-helpers.js.map +1 -0
- package/dist/lib/request/header-utils.d.ts +38 -0
- package/dist/lib/request/header-utils.d.ts.map +1 -0
- package/dist/lib/request/header-utils.js +75 -0
- package/dist/lib/request/header-utils.js.map +1 -0
- package/dist/lib/request/openai-chunk-builder.d.ts +68 -0
- package/dist/lib/request/openai-chunk-builder.d.ts.map +1 -0
- package/dist/lib/request/openai-chunk-builder.js +110 -0
- package/dist/lib/request/openai-chunk-builder.js.map +1 -0
- package/dist/lib/request/payload-analyzer.d.ts +34 -0
- package/dist/lib/request/payload-analyzer.d.ts.map +1 -0
- package/dist/lib/request/payload-analyzer.js +114 -0
- package/dist/lib/request/payload-analyzer.js.map +1 -0
- package/dist/lib/request/request-transformer.d.ts +39 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -0
- package/dist/lib/request/request-transformer.js +108 -0
- package/dist/lib/request/request-transformer.js.map +1 -0
- package/dist/lib/request/response-handler.d.ts +15 -0
- package/dist/lib/request/response-handler.d.ts.map +1 -0
- package/dist/lib/request/response-handler.js +90 -0
- package/dist/lib/request/response-handler.js.map +1 -0
- package/dist/lib/request/sse-parser.d.ts +36 -0
- package/dist/lib/request/sse-parser.d.ts.map +1 -0
- package/dist/lib/request/sse-parser.js +85 -0
- package/dist/lib/request/sse-parser.js.map +1 -0
- package/dist/lib/request/stream-normalizer.d.ts +18 -0
- package/dist/lib/request/stream-normalizer.d.ts.map +1 -0
- package/dist/lib/request/stream-normalizer.js +140 -0
- package/dist/lib/request/stream-normalizer.js.map +1 -0
- package/dist/lib/types.d.ts +180 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { generatePKCE } from "@openauthjs/openauth/pkce";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from "fs";
|
|
3
|
+
import { QWEN_OAUTH, DEFAULT_QWEN_BASE_URL, TOKEN_REFRESH_BUFFER_MS, VERIFICATION_URI } from "../constants.js";
|
|
4
|
+
import { getTokenPath, getConfigDir } from "../config.js";
|
|
5
|
+
import { logError, logWarn, logInfo, LOGGING_ENABLED } from "../logger.js";
|
|
6
|
+
// So lan retry toi da khi refresh token that bai
|
|
7
|
+
const MAX_REFRESH_RETRIES = 2;
|
|
8
|
+
const REFRESH_RETRY_DELAY_MS = 1000;
|
|
9
|
+
/**
|
|
10
|
+
* Normalize and validate resource_url from OAuth response
|
|
11
|
+
* @param resourceUrl - Resource URL from token response
|
|
12
|
+
* @returns Normalized URL or undefined if invalid
|
|
13
|
+
*/
|
|
14
|
+
function normalizeResourceUrl(resourceUrl) {
|
|
15
|
+
if (!resourceUrl)
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
// Qwen returns resource_url without protocol (e.g., "portal.qwen.ai")
|
|
19
|
+
// Normalize it by adding https:// if missing
|
|
20
|
+
let normalizedUrl = resourceUrl;
|
|
21
|
+
if (!normalizedUrl.startsWith("http://") && !normalizedUrl.startsWith("https://")) {
|
|
22
|
+
normalizedUrl = `https://${normalizedUrl}`;
|
|
23
|
+
}
|
|
24
|
+
// Validate the normalized URL
|
|
25
|
+
new URL(normalizedUrl);
|
|
26
|
+
if (LOGGING_ENABLED) {
|
|
27
|
+
logInfo("Valid resource_url found and normalized:", normalizedUrl);
|
|
28
|
+
}
|
|
29
|
+
return normalizedUrl;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logWarn("invalid resource_url:", { original: resourceUrl, error });
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate token response fields
|
|
38
|
+
* @param json - Token response JSON
|
|
39
|
+
* @param context - Context for logging (e.g., "token response" or "refresh response")
|
|
40
|
+
* @returns True if valid, false otherwise
|
|
41
|
+
*/
|
|
42
|
+
function validateTokenResponse(json, context) {
|
|
43
|
+
// Check required fields
|
|
44
|
+
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
45
|
+
logError(`${context} missing fields:`, json);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// Validate expires_in is positive
|
|
49
|
+
if (json.expires_in <= 0) {
|
|
50
|
+
logError(`invalid expires_in value in ${context}:`, json.expires_in);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Request device authorization code
|
|
57
|
+
* @param pkce - PKCE challenge/verifier pair
|
|
58
|
+
* @returns Device authorization response with user code and verification URL
|
|
59
|
+
*/
|
|
60
|
+
export async function requestDeviceCode(pkce) {
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(QWEN_OAUTH.DEVICE_CODE_URL, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
65
|
+
body: new URLSearchParams({
|
|
66
|
+
client_id: QWEN_OAUTH.CLIENT_ID,
|
|
67
|
+
scope: QWEN_OAUTH.SCOPE,
|
|
68
|
+
code_challenge: pkce.challenge,
|
|
69
|
+
code_challenge_method: "S256",
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const text = await res.text().catch(() => "");
|
|
74
|
+
logError("device code request failed:", { status: res.status, text });
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const json = await res.json();
|
|
78
|
+
if (LOGGING_ENABLED) {
|
|
79
|
+
logInfo("Device code response received:", json);
|
|
80
|
+
}
|
|
81
|
+
if (!json.device_code || !json.user_code || !json.verification_uri) {
|
|
82
|
+
logError("device code response missing fields:", json);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
// Ensure verification_uri_complete includes the client parameter
|
|
86
|
+
// Qwen's OAuth server requires client=qwen-code for proper authentication
|
|
87
|
+
if (!json.verification_uri_complete || !json.verification_uri_complete.includes(VERIFICATION_URI.CLIENT_PARAM_KEY)) {
|
|
88
|
+
const baseUrl = json.verification_uri_complete || json.verification_uri;
|
|
89
|
+
const separator = baseUrl.includes('?') ? '&' : '?';
|
|
90
|
+
json.verification_uri_complete = `${baseUrl}${separator}${VERIFICATION_URI.CLIENT_PARAM_VALUE}`;
|
|
91
|
+
if (LOGGING_ENABLED) {
|
|
92
|
+
logInfo("Fixed verification_uri_complete:", json.verification_uri_complete);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return json;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
logError("device code request error:", error);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Poll for token using device code
|
|
104
|
+
* @param deviceCode - Device code from authorization response
|
|
105
|
+
* @param verifier - PKCE verifier
|
|
106
|
+
* @param interval - Polling interval in seconds (from device response)
|
|
107
|
+
* @returns Token result or null if still pending
|
|
108
|
+
*/
|
|
109
|
+
export async function pollForToken(deviceCode, verifier, interval = 2) {
|
|
110
|
+
try {
|
|
111
|
+
const res = await fetch(QWEN_OAUTH.TOKEN_URL, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
114
|
+
body: new URLSearchParams({
|
|
115
|
+
grant_type: QWEN_OAUTH.GRANT_TYPE_DEVICE,
|
|
116
|
+
client_id: QWEN_OAUTH.CLIENT_ID,
|
|
117
|
+
device_code: deviceCode,
|
|
118
|
+
code_verifier: verifier,
|
|
119
|
+
}),
|
|
120
|
+
});
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
const json = await res.json().catch(() => ({}));
|
|
123
|
+
const error = json.error;
|
|
124
|
+
// Handle expected errors
|
|
125
|
+
if (error === "authorization_pending") {
|
|
126
|
+
return { type: "pending" };
|
|
127
|
+
}
|
|
128
|
+
if (error === "slow_down") {
|
|
129
|
+
return { type: "slow_down" };
|
|
130
|
+
}
|
|
131
|
+
if (error === "expired_token") {
|
|
132
|
+
return { type: "expired" };
|
|
133
|
+
}
|
|
134
|
+
if (error === "access_denied") {
|
|
135
|
+
return { type: "denied" };
|
|
136
|
+
}
|
|
137
|
+
logError("token poll failed:", { status: res.status, json });
|
|
138
|
+
return { type: "failed" };
|
|
139
|
+
}
|
|
140
|
+
const json = await res.json();
|
|
141
|
+
if (LOGGING_ENABLED) {
|
|
142
|
+
// Log the full token response for debugging
|
|
143
|
+
logInfo("Token response received:", {
|
|
144
|
+
has_access_token: !!json.access_token,
|
|
145
|
+
has_refresh_token: !!json.refresh_token,
|
|
146
|
+
expires_in: json.expires_in,
|
|
147
|
+
resource_url: json.resource_url,
|
|
148
|
+
all_fields: Object.keys(json),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Validate token response fields
|
|
152
|
+
if (!validateTokenResponse(json, "token response")) {
|
|
153
|
+
return { type: "failed" };
|
|
154
|
+
}
|
|
155
|
+
// Validate and normalize resource_url if present
|
|
156
|
+
json.resource_url = normalizeResourceUrl(json.resource_url);
|
|
157
|
+
if (!json.resource_url) {
|
|
158
|
+
logWarn("No valid resource_url in token response, will use default DashScope endpoint");
|
|
159
|
+
}
|
|
160
|
+
// At this point, validation ensures these fields exist
|
|
161
|
+
return {
|
|
162
|
+
type: "success",
|
|
163
|
+
access: json.access_token,
|
|
164
|
+
refresh: json.refresh_token,
|
|
165
|
+
expires: Date.now() + json.expires_in * 1000,
|
|
166
|
+
resourceUrl: json.resource_url, // Dynamic API base URL
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
logError("token poll error:", error);
|
|
171
|
+
return { type: "failed" };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Refresh access token using refresh token (1 lan duy nhat, khong retry)
|
|
176
|
+
* @param refreshToken - Refresh token
|
|
177
|
+
* @returns Token result
|
|
178
|
+
*/
|
|
179
|
+
async function refreshAccessTokenOnce(refreshToken) {
|
|
180
|
+
try {
|
|
181
|
+
const res = await fetch(QWEN_OAUTH.TOKEN_URL, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
184
|
+
body: new URLSearchParams({
|
|
185
|
+
grant_type: QWEN_OAUTH.GRANT_TYPE_REFRESH,
|
|
186
|
+
client_id: QWEN_OAUTH.CLIENT_ID,
|
|
187
|
+
refresh_token: refreshToken,
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
const text = await res.text().catch(() => "");
|
|
192
|
+
logError("token refresh failed:", { status: res.status, text });
|
|
193
|
+
return { type: "failed", status: res.status };
|
|
194
|
+
}
|
|
195
|
+
const json = await res.json();
|
|
196
|
+
if (LOGGING_ENABLED) {
|
|
197
|
+
logInfo("Token refresh response received:", {
|
|
198
|
+
has_access_token: !!json.access_token,
|
|
199
|
+
has_refresh_token: !!json.refresh_token,
|
|
200
|
+
expires_in: json.expires_in,
|
|
201
|
+
resource_url: json.resource_url,
|
|
202
|
+
all_fields: Object.keys(json),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
// Validate token response fields
|
|
206
|
+
if (!validateTokenResponse(json, "refresh response")) {
|
|
207
|
+
return { type: "failed" };
|
|
208
|
+
}
|
|
209
|
+
// Validate and normalize resource_url if present
|
|
210
|
+
json.resource_url = normalizeResourceUrl(json.resource_url);
|
|
211
|
+
if (!json.resource_url) {
|
|
212
|
+
logWarn("No valid resource_url in refresh response, will use default DashScope endpoint");
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
type: "success",
|
|
216
|
+
access: json.access_token,
|
|
217
|
+
refresh: json.refresh_token,
|
|
218
|
+
expires: Date.now() + json.expires_in * 1000,
|
|
219
|
+
resourceUrl: json.resource_url,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
logError("token refresh error:", error);
|
|
224
|
+
return { type: "failed" };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Refresh access token voi retry logic
|
|
229
|
+
* Retry toi da MAX_REFRESH_RETRIES lan voi delay giua cac lan
|
|
230
|
+
* @param refreshToken - Refresh token
|
|
231
|
+
* @returns Token result
|
|
232
|
+
*/
|
|
233
|
+
export async function refreshAccessToken(refreshToken) {
|
|
234
|
+
for (let attempt = 0; attempt <= MAX_REFRESH_RETRIES; attempt++) {
|
|
235
|
+
const result = await refreshAccessTokenOnce(refreshToken);
|
|
236
|
+
if (result.type === "success") {
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
// Neu loi 401/403 thi refresh token da bi revoke, khong can retry
|
|
240
|
+
if (result.status === 401 || result.status === 403) {
|
|
241
|
+
logError("Refresh token bi reject (" + result.status + "), can dang nhap lai");
|
|
242
|
+
return { type: "failed" };
|
|
243
|
+
}
|
|
244
|
+
// Con retry thi cho delay roi thu lai
|
|
245
|
+
if (attempt < MAX_REFRESH_RETRIES) {
|
|
246
|
+
if (LOGGING_ENABLED) {
|
|
247
|
+
logInfo(`Token refresh that bai, thu lai lan ${attempt + 2}/${MAX_REFRESH_RETRIES + 1}...`);
|
|
248
|
+
}
|
|
249
|
+
await new Promise(resolve => setTimeout(resolve, REFRESH_RETRY_DELAY_MS));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
logError("Token refresh that bai sau " + (MAX_REFRESH_RETRIES + 1) + " lan thu");
|
|
253
|
+
return { type: "failed" };
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Generate PKCE challenge and verifier
|
|
257
|
+
* @returns PKCE pair
|
|
258
|
+
*/
|
|
259
|
+
export async function createPKCE() {
|
|
260
|
+
const { challenge, verifier } = await generatePKCE();
|
|
261
|
+
return { challenge, verifier };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Load stored token from disk
|
|
265
|
+
* @returns Stored token data or null if not found
|
|
266
|
+
*/
|
|
267
|
+
export function loadStoredToken() {
|
|
268
|
+
const tokenPath = getTokenPath();
|
|
269
|
+
if (!existsSync(tokenPath)) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const content = readFileSync(tokenPath, "utf-8");
|
|
274
|
+
const data = JSON.parse(content);
|
|
275
|
+
// Validate required fields
|
|
276
|
+
if (!data.access_token || !data.refresh_token || typeof data.expires !== "number") {
|
|
277
|
+
logWarn("Invalid token data, re-authentication required");
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
return data;
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
logError("Failed to load token:", error);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Xoa token luu tren disk khi token khong con hop le
|
|
289
|
+
*/
|
|
290
|
+
export function clearStoredToken() {
|
|
291
|
+
const tokenPath = getTokenPath();
|
|
292
|
+
if (existsSync(tokenPath)) {
|
|
293
|
+
try {
|
|
294
|
+
unlinkSync(tokenPath);
|
|
295
|
+
logWarn("Da xoa token cu, can dang nhap lai");
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
logError("Khong the xoa token file:", error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Save token to disk
|
|
304
|
+
* @param tokenResult - Token result from OAuth flow
|
|
305
|
+
*/
|
|
306
|
+
export function saveToken(tokenResult) {
|
|
307
|
+
if (tokenResult.type !== "success") {
|
|
308
|
+
throw new Error("Cannot save non-success token result");
|
|
309
|
+
}
|
|
310
|
+
const configDir = getConfigDir();
|
|
311
|
+
// Ensure directory exists
|
|
312
|
+
if (!existsSync(configDir)) {
|
|
313
|
+
mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
314
|
+
}
|
|
315
|
+
const tokenData = {
|
|
316
|
+
access_token: tokenResult.access,
|
|
317
|
+
refresh_token: tokenResult.refresh,
|
|
318
|
+
expires: tokenResult.expires,
|
|
319
|
+
resource_url: tokenResult.resourceUrl,
|
|
320
|
+
};
|
|
321
|
+
const tokenPath = getTokenPath();
|
|
322
|
+
try {
|
|
323
|
+
writeFileSync(tokenPath, JSON.stringify(tokenData, null, 2), {
|
|
324
|
+
encoding: "utf-8",
|
|
325
|
+
mode: 0o600, // Secure permissions
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
logError("Failed to save token:", error);
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Check if token is expired (with 5 minute buffer)
|
|
335
|
+
* @param expiresAt - Expiration timestamp in milliseconds
|
|
336
|
+
* @returns True if token is expired or will expire soon
|
|
337
|
+
*/
|
|
338
|
+
export function isTokenExpired(expiresAt) {
|
|
339
|
+
return Date.now() >= expiresAt - TOKEN_REFRESH_BUFFER_MS;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get valid access token, refreshing if necessary
|
|
343
|
+
* Khi refresh that bai, xoa token cu de user biet can dang nhap lai
|
|
344
|
+
* @returns Access token and resource URL, or null if authentication required
|
|
345
|
+
*/
|
|
346
|
+
export async function getValidToken() {
|
|
347
|
+
const stored = loadStoredToken();
|
|
348
|
+
if (!stored) {
|
|
349
|
+
return null; // Khong co token, can dang nhap
|
|
350
|
+
}
|
|
351
|
+
// Token con hieu luc
|
|
352
|
+
if (!isTokenExpired(stored.expires)) {
|
|
353
|
+
return {
|
|
354
|
+
accessToken: stored.access_token,
|
|
355
|
+
resourceUrl: stored.resource_url,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
// Token het han, thu refresh (co retry ben trong)
|
|
359
|
+
if (LOGGING_ENABLED) {
|
|
360
|
+
logInfo("Token expired, refreshing...");
|
|
361
|
+
}
|
|
362
|
+
const refreshResult = await refreshAccessToken(stored.refresh_token);
|
|
363
|
+
if (refreshResult.type !== "success") {
|
|
364
|
+
logError("Token refresh failed, re-authentication required");
|
|
365
|
+
// Xoa token cu de tranh loop loi
|
|
366
|
+
clearStoredToken();
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
// Luu token moi
|
|
370
|
+
saveToken(refreshResult);
|
|
371
|
+
return {
|
|
372
|
+
accessToken: refreshResult.access,
|
|
373
|
+
resourceUrl: refreshResult.resourceUrl,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get Portal API base URL from token or use default
|
|
378
|
+
* @param resourceUrl - Resource URL from token (optional)
|
|
379
|
+
* @returns Portal API base URL
|
|
380
|
+
*
|
|
381
|
+
* IMPORTANT: Portal API uses /v1 path (not /api/v1)
|
|
382
|
+
* - OAuth endpoints: /api/v1/oauth2/ (for authentication)
|
|
383
|
+
* - Chat API: /v1/ (for completions)
|
|
384
|
+
*/
|
|
385
|
+
export function getApiBaseUrl(resourceUrl) {
|
|
386
|
+
if (resourceUrl) {
|
|
387
|
+
// Validate URL format
|
|
388
|
+
try {
|
|
389
|
+
const url = new URL(resourceUrl);
|
|
390
|
+
if (!url.protocol.startsWith('http')) {
|
|
391
|
+
logWarn('Invalid resource_url protocol, using default Portal API URL');
|
|
392
|
+
return DEFAULT_QWEN_BASE_URL;
|
|
393
|
+
}
|
|
394
|
+
// Construct the Portal API endpoint from resource_url
|
|
395
|
+
// Qwen returns "portal.qwen.ai" which should become "https://portal.qwen.ai/v1"
|
|
396
|
+
// Remove trailing slash if present
|
|
397
|
+
let baseUrl = resourceUrl.replace(/\/$/, "");
|
|
398
|
+
// Add /v1 suffix if not already present
|
|
399
|
+
const suffix = '/v1';
|
|
400
|
+
if (!baseUrl.endsWith(suffix)) {
|
|
401
|
+
baseUrl = `${baseUrl}${suffix}`;
|
|
402
|
+
}
|
|
403
|
+
if (LOGGING_ENABLED) {
|
|
404
|
+
logInfo('Constructed Portal API base URL from resource_url:', baseUrl);
|
|
405
|
+
}
|
|
406
|
+
return baseUrl;
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
logWarn('Invalid resource_url format, using default Portal API URL:', error);
|
|
410
|
+
return DEFAULT_QWEN_BASE_URL;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Fall back to default Portal API URL
|
|
414
|
+
if (LOGGING_ENABLED) {
|
|
415
|
+
logInfo('No resource_url provided, using default Portal API URL');
|
|
416
|
+
}
|
|
417
|
+
return DEFAULT_QWEN_BASE_URL;
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../lib/auth/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser utilities for OAuth flow
|
|
3
|
+
* Handles platform-specific browser opening
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Gets the platform-specific command to open a URL in the default browser
|
|
7
|
+
* @returns Browser opener command for the current platform
|
|
8
|
+
*/
|
|
9
|
+
export declare function getBrowserOpener(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Opens a URL in the default browser
|
|
12
|
+
* Silently fails if browser cannot be opened (user can copy URL manually)
|
|
13
|
+
* @param url - URL to open
|
|
14
|
+
*/
|
|
15
|
+
export declare function openBrowserUrl(url: string): void;
|
|
16
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../lib/auth/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAUhD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser utilities for OAuth flow
|
|
3
|
+
* Handles platform-specific browser opening
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { PLATFORM_OPENERS } from "../constants.js";
|
|
7
|
+
/**
|
|
8
|
+
* Gets the platform-specific command to open a URL in the default browser
|
|
9
|
+
* @returns Browser opener command for the current platform
|
|
10
|
+
*/
|
|
11
|
+
export function getBrowserOpener() {
|
|
12
|
+
const platform = process.platform;
|
|
13
|
+
if (platform === "darwin")
|
|
14
|
+
return PLATFORM_OPENERS.darwin;
|
|
15
|
+
if (platform === "win32")
|
|
16
|
+
return PLATFORM_OPENERS.win32;
|
|
17
|
+
return PLATFORM_OPENERS.linux;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Opens a URL in the default browser
|
|
21
|
+
* Silently fails if browser cannot be opened (user can copy URL manually)
|
|
22
|
+
* @param url - URL to open
|
|
23
|
+
*/
|
|
24
|
+
export function openBrowserUrl(url) {
|
|
25
|
+
try {
|
|
26
|
+
const opener = getBrowserOpener();
|
|
27
|
+
spawn(opener, [url], {
|
|
28
|
+
stdio: "ignore",
|
|
29
|
+
shell: process.platform === "win32",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// Log canh bao de ho tro debug, user van co the mo URL thu cong
|
|
34
|
+
console.warn("[qwen-oauth-plugin] Khong the mo trinh duyet:", error?.message || error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../lib/auth/browser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,gBAAgB,CAAC,MAAM,CAAC;IAC1D,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,gBAAgB,CAAC,KAAK,CAAC;IACxD,OAAO,gBAAgB,CAAC,KAAK,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACzC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAClC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;YACpB,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;SACnC,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,mEAAmE;IACpE,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PluginConfig } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get plugin configuration directory
|
|
4
|
+
*/
|
|
5
|
+
export declare function getConfigDir(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get plugin configuration file path
|
|
8
|
+
*/
|
|
9
|
+
export declare function getConfigPath(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Load plugin configuration from ~/.opencode/qwen/auth-config.json
|
|
12
|
+
* Returns default config if file doesn't exist
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadPluginConfig(): PluginConfig;
|
|
15
|
+
/**
|
|
16
|
+
* Get QWEN_MODE setting
|
|
17
|
+
* Priority: QWEN_MODE env var > config file > default (true)
|
|
18
|
+
*/
|
|
19
|
+
export declare function getQwenMode(config: PluginConfig): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Get token storage path
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTokenPath(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Get cache directory for prompts
|
|
26
|
+
*/
|
|
27
|
+
export declare function getCacheDir(): string;
|
|
28
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,YAAY,CAc/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAMzD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { readFileSync, existsSync } from "fs";
|
|
4
|
+
/**
|
|
5
|
+
* Get plugin configuration directory
|
|
6
|
+
*/
|
|
7
|
+
export function getConfigDir() {
|
|
8
|
+
return join(homedir(), ".opencode", "qwen");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get plugin configuration file path
|
|
12
|
+
*/
|
|
13
|
+
export function getConfigPath() {
|
|
14
|
+
return join(getConfigDir(), "auth-config.json");
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Load plugin configuration from ~/.opencode/qwen/auth-config.json
|
|
18
|
+
* Returns default config if file doesn't exist
|
|
19
|
+
*/
|
|
20
|
+
export function loadPluginConfig() {
|
|
21
|
+
const configPath = getConfigPath();
|
|
22
|
+
if (!existsSync(configPath)) {
|
|
23
|
+
return { qwenMode: true }; // Default to QWEN_MODE enabled
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const content = readFileSync(configPath, "utf-8");
|
|
27
|
+
return JSON.parse(content);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.warn(`[qwen-oauth-plugin] Failed to load config from ${configPath}:`, error);
|
|
31
|
+
return { qwenMode: true };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get QWEN_MODE setting
|
|
36
|
+
* Priority: QWEN_MODE env var > config file > default (true)
|
|
37
|
+
*/
|
|
38
|
+
export function getQwenMode(config) {
|
|
39
|
+
const envValue = process.env.QWEN_MODE;
|
|
40
|
+
if (envValue !== undefined) {
|
|
41
|
+
return envValue === "1" || envValue.toLowerCase() === "true";
|
|
42
|
+
}
|
|
43
|
+
// Ep kieu boolean chac chan, tranh string "false" bi truthy
|
|
44
|
+
const val = config.qwenMode;
|
|
45
|
+
if (val === undefined || val === null) return true; // mac dinh bat
|
|
46
|
+
if (typeof val === "string") {
|
|
47
|
+
return val === "1" || val.toLowerCase() === "true";
|
|
48
|
+
}
|
|
49
|
+
return !!val;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get token storage path
|
|
53
|
+
*/
|
|
54
|
+
export function getTokenPath() {
|
|
55
|
+
return join(getConfigDir(), "oauth_token.json");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get cache directory for prompts
|
|
59
|
+
*/
|
|
60
|
+
export function getCacheDir() {
|
|
61
|
+
return join(homedir(), ".opencode", "cache");
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAG9C;;GAEG;AACH,MAAM,UAAU,YAAY;IAC3B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,kBAAkB,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC/B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,+BAA+B;IAC3D,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,kDAAkD,UAAU,GAAG,EAAE,KAAK,CAAC,CAAC;QACrF,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC3B,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,kBAAkB,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IAC1B,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC"}
|