opencode-qwen-oauth 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/install.js +29 -15
- package/dist/browser.d.ts +5 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +24 -0
- package/dist/browser.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/index.d.ts.map +1 -1
- package/dist/index.js +9 -171
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +6 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +37 -0
- package/dist/logger.js.map +1 -0
- package/dist/oauth.d.ts +21 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +91 -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/package.json +1 -1
package/bin/install.js
CHANGED
|
@@ -2,13 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Qwen OAuth Plugin Installer
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* npx opencode-qwen-oauth install
|
|
8
8
|
* bunx opencode-qwen-oauth install
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
readFileSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
appendFileSync,
|
|
17
|
+
} from "node:fs";
|
|
12
18
|
import { join } from "node:path";
|
|
13
19
|
import { homedir } from "node:os";
|
|
14
20
|
|
|
@@ -64,8 +70,12 @@ function install() {
|
|
|
64
70
|
ensureDir(opencodeDir);
|
|
65
71
|
|
|
66
72
|
// Read or create config
|
|
67
|
-
let config = {
|
|
68
|
-
|
|
73
|
+
let config = {
|
|
74
|
+
$schema: "https://opencode.ai/config.json",
|
|
75
|
+
plugin: [],
|
|
76
|
+
provider: {},
|
|
77
|
+
};
|
|
78
|
+
|
|
69
79
|
if (existsSync(configPath)) {
|
|
70
80
|
try {
|
|
71
81
|
const content = readFileSync(configPath, "utf-8");
|
|
@@ -123,7 +133,7 @@ function install() {
|
|
|
123
133
|
// Install npm package in .opencode
|
|
124
134
|
const opencodePackagePath = join(opencodeDir, "package.json");
|
|
125
135
|
let opencodePackage = { dependencies: {} };
|
|
126
|
-
|
|
136
|
+
|
|
127
137
|
if (existsSync(opencodePackagePath)) {
|
|
128
138
|
try {
|
|
129
139
|
opencodePackage = JSON.parse(readFileSync(opencodePackagePath, "utf-8"));
|
|
@@ -139,18 +149,22 @@ function install() {
|
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
try {
|
|
142
|
-
writeFileSync(
|
|
152
|
+
writeFileSync(
|
|
153
|
+
opencodePackagePath,
|
|
154
|
+
JSON.stringify(opencodePackage, null, 2) + "\n",
|
|
155
|
+
"utf-8",
|
|
156
|
+
);
|
|
143
157
|
} catch (e) {
|
|
144
158
|
error(`Failed to write package.json: ${e.message}`);
|
|
145
159
|
process.exit(1);
|
|
146
160
|
}
|
|
147
161
|
|
|
148
|
-
log("
|
|
149
|
-
log("
|
|
150
|
-
log("
|
|
151
|
-
log("
|
|
152
|
-
log("
|
|
153
|
-
log("
|
|
162
|
+
log("Installation complete!");
|
|
163
|
+
log("Next steps:");
|
|
164
|
+
log("1. Run: opencode");
|
|
165
|
+
log("2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')");
|
|
166
|
+
log("3. Use model: /model qwen/qwen3-coder-plus");
|
|
167
|
+
log("Debug mode: QWEN_OAUTH_DEBUG=true opencode");
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
// ============================================
|
|
@@ -161,7 +175,7 @@ function uninstall() {
|
|
|
161
175
|
log("Uninstalling Qwen OAuth plugin...");
|
|
162
176
|
|
|
163
177
|
const configPath = getOpencodeConfigPath();
|
|
164
|
-
|
|
178
|
+
|
|
165
179
|
if (!existsSync(configPath)) {
|
|
166
180
|
log("No config file found, nothing to uninstall");
|
|
167
181
|
return;
|
|
@@ -198,9 +212,9 @@ function uninstall() {
|
|
|
198
212
|
process.exit(1);
|
|
199
213
|
}
|
|
200
214
|
|
|
201
|
-
log("
|
|
215
|
+
log("Uninstallation complete!");
|
|
202
216
|
log("Note: You may want to manually remove the npm package:");
|
|
203
|
-
log("
|
|
217
|
+
log("npm uninstall opencode-qwen-auth");
|
|
204
218
|
}
|
|
205
219
|
|
|
206
220
|
// ============================================
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAmB7C"}
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser utilities for opening URLs
|
|
3
|
+
*/
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
export function openBrowser(url) {
|
|
6
|
+
try {
|
|
7
|
+
const platform = process.platform;
|
|
8
|
+
const command = platform === "darwin"
|
|
9
|
+
? "open"
|
|
10
|
+
: platform === "win32"
|
|
11
|
+
? "rundll32"
|
|
12
|
+
: "xdg-open";
|
|
13
|
+
const args = platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
|
|
14
|
+
const child = spawn(command, args, {
|
|
15
|
+
stdio: "ignore",
|
|
16
|
+
detached: true,
|
|
17
|
+
});
|
|
18
|
+
child.unref?.();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Ignore errors
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,OAAO,GACX,QAAQ,KAAK,QAAQ;YACnB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,QAAQ,KAAK,OAAO;gBACpB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,UAAU,CAAC;QACnB,MAAM,IAAI,GACR,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACtE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACjC,KAAK,EAAE,QAAQ;YACf,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,gBAAgB;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Qwen OAuth Plugin
|
|
3
|
+
*/
|
|
4
|
+
export declare const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
|
5
|
+
export declare const QWEN_DEVICE_CODE_ENDPOINT = "/api/v1/oauth2/device/code";
|
|
6
|
+
export declare const QWEN_TOKEN_ENDPOINT = "/api/v1/oauth2/token";
|
|
7
|
+
export declare const QWEN_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56";
|
|
8
|
+
export declare const QWEN_SCOPES: string[];
|
|
9
|
+
export declare const QWEN_API_BASE_URL = "https://portal.qwen.ai/v1";
|
|
10
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAC1D,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AACtE,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAC1D,eAAO,MAAM,cAAc,qCAAqC,CAAC;AACjE,eAAO,MAAM,WAAW,UAAqD,CAAC;AAC9E,eAAO,MAAM,iBAAiB,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Qwen OAuth Plugin
|
|
3
|
+
*/
|
|
4
|
+
export const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
|
5
|
+
export const QWEN_DEVICE_CODE_ENDPOINT = "/api/v1/oauth2/device/code";
|
|
6
|
+
export const QWEN_TOKEN_ENDPOINT = "/api/v1/oauth2/token";
|
|
7
|
+
export const QWEN_CLIENT_ID = "f0304373b74a44d2b584a3fb70ca9e56";
|
|
8
|
+
export const QWEN_SCOPES = ["openid", "profile", "email", "model.completion"];
|
|
9
|
+
export const QWEN_API_BASE_URL = "https://portal.qwen.ai/v1";
|
|
10
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAC1D,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,sBAAsB,CAAC;AAC1D,MAAM,CAAC,MAAM,cAAc,GAAG,kCAAkC,CAAC;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAG,2BAA2B,CAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAM/D,eAAO,MAAM,eAAe,EAAE,MAiG7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,175 +4,16 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @packageDocumentation
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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" || process.env.QWEN_OAUTH_DEBUG === "1";
|
|
49
|
-
function debugLog(message, data) {
|
|
50
|
-
const logMessage = data ? `${message} ${JSON.stringify(data)}` : message;
|
|
51
|
-
writeLog(logMessage);
|
|
52
|
-
if (DEBUG) {
|
|
53
|
-
console.log(`[Qwen OAuth] ${message}`, data ? JSON.stringify(data, null, 2) : "");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// ============================================
|
|
57
|
-
// PKCE
|
|
58
|
-
// ============================================
|
|
59
|
-
function base64UrlEncode(buffer) {
|
|
60
|
-
return buffer
|
|
61
|
-
.toString("base64")
|
|
62
|
-
.replace(/\+/g, "-")
|
|
63
|
-
.replace(/\//g, "_")
|
|
64
|
-
.replace(/=+$/, "");
|
|
65
|
-
}
|
|
66
|
-
function createPkcePair() {
|
|
67
|
-
const verifier = base64UrlEncode(randomBytes(32));
|
|
68
|
-
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
69
|
-
return { verifier, challenge };
|
|
70
|
-
}
|
|
71
|
-
// ============================================
|
|
72
|
-
// Browser
|
|
73
|
-
// ============================================
|
|
74
|
-
function openBrowser(url) {
|
|
75
|
-
try {
|
|
76
|
-
const platform = process.platform;
|
|
77
|
-
const command = platform === "darwin"
|
|
78
|
-
? "open"
|
|
79
|
-
: platform === "win32"
|
|
80
|
-
? "rundll32"
|
|
81
|
-
: "xdg-open";
|
|
82
|
-
const args = platform === "win32"
|
|
83
|
-
? ["url.dll,FileProtocolHandler", url]
|
|
84
|
-
: [url];
|
|
85
|
-
const child = spawn(command, args, {
|
|
86
|
-
stdio: "ignore",
|
|
87
|
-
detached: true,
|
|
88
|
-
});
|
|
89
|
-
child.unref?.();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
// Ignore errors
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async function authorizeDevice() {
|
|
96
|
-
const { verifier, challenge } = createPkcePair();
|
|
97
|
-
const params = new URLSearchParams({
|
|
98
|
-
client_id: QWEN_CLIENT_ID,
|
|
99
|
-
scope: QWEN_SCOPES.join(" "),
|
|
100
|
-
code_challenge: challenge,
|
|
101
|
-
code_challenge_method: "S256",
|
|
7
|
+
import { QWEN_API_BASE_URL } from "./constants.js";
|
|
8
|
+
import { debugLog } from "./logger.js";
|
|
9
|
+
import { openBrowser } from "./browser.js";
|
|
10
|
+
import { authorizeDevice, pollForToken } from "./oauth.js";
|
|
11
|
+
export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree, }) => {
|
|
12
|
+
debugLog("Plugin initialized", {
|
|
13
|
+
directory,
|
|
14
|
+
worktree,
|
|
15
|
+
project: project?.name || "N/A",
|
|
102
16
|
});
|
|
103
|
-
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_DEVICE_CODE_ENDPOINT}`, {
|
|
104
|
-
method: "POST",
|
|
105
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
106
|
-
body: params.toString(),
|
|
107
|
-
});
|
|
108
|
-
if (!response.ok) {
|
|
109
|
-
throw new Error(`Failed to start device flow: ${response.statusText}`);
|
|
110
|
-
}
|
|
111
|
-
const data = (await response.json());
|
|
112
|
-
return {
|
|
113
|
-
device_code: data.device_code,
|
|
114
|
-
user_code: data.user_code,
|
|
115
|
-
verification_uri: data.verification_uri,
|
|
116
|
-
verification_uri_complete: data.verification_uri_complete,
|
|
117
|
-
expires_in: data.expires_in,
|
|
118
|
-
interval: data.interval || 5,
|
|
119
|
-
verifier,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
async function pollForToken(deviceCode, codeVerifier, intervalSeconds, expiresIn) {
|
|
123
|
-
const timeoutMs = expiresIn * 1000;
|
|
124
|
-
const startTime = Date.now();
|
|
125
|
-
let currentInterval = intervalSeconds * 1000;
|
|
126
|
-
debugLog("Starting token polling", { timeoutMs, interval: currentInterval });
|
|
127
|
-
while (Date.now() - startTime < timeoutMs) {
|
|
128
|
-
await new Promise((resolve) => setTimeout(resolve, currentInterval));
|
|
129
|
-
const params = new URLSearchParams({
|
|
130
|
-
client_id: QWEN_CLIENT_ID,
|
|
131
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
132
|
-
device_code: deviceCode,
|
|
133
|
-
code_verifier: codeVerifier,
|
|
134
|
-
});
|
|
135
|
-
debugLog("Polling for token...");
|
|
136
|
-
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_TOKEN_ENDPOINT}`, {
|
|
137
|
-
method: "POST",
|
|
138
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
139
|
-
body: params.toString(),
|
|
140
|
-
});
|
|
141
|
-
if (response.ok) {
|
|
142
|
-
const data = (await response.json());
|
|
143
|
-
debugLog("Token received successfully");
|
|
144
|
-
return {
|
|
145
|
-
success: true,
|
|
146
|
-
access_token: data.access_token,
|
|
147
|
-
refresh_token: data.refresh_token,
|
|
148
|
-
expires_in: data.expires_in,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
const error = (await response.json().catch(() => ({})));
|
|
152
|
-
if (error.error === "authorization_pending") {
|
|
153
|
-
debugLog("Authorization pending, retrying...");
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
if (error.error === "slow_down") {
|
|
157
|
-
currentInterval += 5000;
|
|
158
|
-
debugLog("Server requested slow down, new interval:", { interval: currentInterval });
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
if (error.error === "expired_token") {
|
|
162
|
-
debugLog("Device code expired");
|
|
163
|
-
return { success: false, error: "Device code expired. Please try again." };
|
|
164
|
-
}
|
|
165
|
-
debugLog(`Token polling failed: ${error.error_description || "unknown error"}`);
|
|
166
|
-
return { success: false, error: error.error_description || "Authentication failed" };
|
|
167
|
-
}
|
|
168
|
-
debugLog("Polling timeout exceeded");
|
|
169
|
-
return { success: false, error: "Polling timeout - device code expired" };
|
|
170
|
-
}
|
|
171
|
-
// ============================================
|
|
172
|
-
// Plugin
|
|
173
|
-
// ============================================
|
|
174
|
-
export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
175
|
-
debugLog("Plugin initialized", { directory, worktree, project: project?.name || "N/A" });
|
|
176
17
|
return {
|
|
177
18
|
auth: {
|
|
178
19
|
provider: "qwen",
|
|
@@ -192,9 +33,6 @@ export const QwenOAuthPlugin = async ({ project, client, $, directory, worktree
|
|
|
192
33
|
expires_in: device.expires_in,
|
|
193
34
|
interval: device.interval,
|
|
194
35
|
});
|
|
195
|
-
// Show essential info to user
|
|
196
|
-
console.log(`\n📱 Qwen OAuth: Visit ${url}`);
|
|
197
|
-
console.log(`🔑 Enter code: ${device.user_code}\n`);
|
|
198
36
|
return {
|
|
199
37
|
url,
|
|
200
38
|
instructions: `Enter code: ${device.user_code}`,
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiCH,eAAO,MAAM,KAAK,SAEoB,CAAC;AAEvC,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAG9E"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
function getLogDir() {
|
|
8
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
9
|
+
return join(xdgConfig, "opencode", "logs");
|
|
10
|
+
}
|
|
11
|
+
function getLogFilePath() {
|
|
12
|
+
return join(getLogDir(), "qwen-oauth.log");
|
|
13
|
+
}
|
|
14
|
+
function ensureLogDir() {
|
|
15
|
+
const logDir = getLogDir();
|
|
16
|
+
if (!existsSync(logDir)) {
|
|
17
|
+
mkdirSync(logDir, { recursive: true, mode: 0o700 });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function writeLog(message) {
|
|
21
|
+
try {
|
|
22
|
+
ensureLogDir();
|
|
23
|
+
const timestamp = new Date().toISOString();
|
|
24
|
+
const logLine = `[${timestamp}] ${message}\n`;
|
|
25
|
+
appendFileSync(getLogFilePath(), logLine, { encoding: "utf-8" });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Silently ignore log write errors
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export const DEBUG = process.env.QWEN_OAUTH_DEBUG === "true" ||
|
|
32
|
+
process.env.QWEN_OAUTH_DEBUG === "1";
|
|
33
|
+
export function debugLog(message, data) {
|
|
34
|
+
const logMessage = data ? `${message} ${JSON.stringify(data)}` : message;
|
|
35
|
+
writeLog(logMessage);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,SAAS,SAAS;IAChB,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,IAAI,CAAC,SAAS,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,OAAe;IAC/B,IAAI,CAAC;QACH,YAAY,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,SAAS,KAAK,OAAO,IAAI,CAAC;QAC9C,cAAc,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,MAAM;IACvC,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,GAAG,CAAC;AAEvC,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAA8B;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACzE,QAAQ,CAAC,UAAU,CAAC,CAAC;AACvB,CAAC"}
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiBD,wBAAsB,eAAe,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAiCpE;AAED,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CA4ED"}
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Device Flow implementation for Qwen
|
|
3
|
+
*/
|
|
4
|
+
import { QWEN_OAUTH_BASE_URL, QWEN_DEVICE_CODE_ENDPOINT, QWEN_TOKEN_ENDPOINT, QWEN_CLIENT_ID, QWEN_SCOPES, } from "./constants.js";
|
|
5
|
+
import { createPkcePair } from "./pkce.js";
|
|
6
|
+
import { debugLog } from "./logger.js";
|
|
7
|
+
export async function authorizeDevice() {
|
|
8
|
+
const { verifier, challenge } = createPkcePair();
|
|
9
|
+
const params = new URLSearchParams({
|
|
10
|
+
client_id: QWEN_CLIENT_ID,
|
|
11
|
+
scope: QWEN_SCOPES.join(" "),
|
|
12
|
+
code_challenge: challenge,
|
|
13
|
+
code_challenge_method: "S256",
|
|
14
|
+
});
|
|
15
|
+
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_DEVICE_CODE_ENDPOINT}`, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
18
|
+
body: params.toString(),
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`Failed to start device flow: ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = (await response.json());
|
|
24
|
+
return {
|
|
25
|
+
device_code: data.device_code,
|
|
26
|
+
user_code: data.user_code,
|
|
27
|
+
verification_uri: data.verification_uri,
|
|
28
|
+
verification_uri_complete: data.verification_uri_complete,
|
|
29
|
+
expires_in: data.expires_in,
|
|
30
|
+
interval: data.interval || 5,
|
|
31
|
+
verifier,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export async function pollForToken(deviceCode, codeVerifier, intervalSeconds, expiresIn) {
|
|
35
|
+
const timeoutMs = expiresIn * 1000;
|
|
36
|
+
const startTime = Date.now();
|
|
37
|
+
let currentInterval = intervalSeconds * 1000;
|
|
38
|
+
debugLog("Starting token polling", { timeoutMs, interval: currentInterval });
|
|
39
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, currentInterval));
|
|
41
|
+
const params = new URLSearchParams({
|
|
42
|
+
client_id: QWEN_CLIENT_ID,
|
|
43
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
44
|
+
device_code: deviceCode,
|
|
45
|
+
code_verifier: codeVerifier,
|
|
46
|
+
});
|
|
47
|
+
debugLog("Polling for token...");
|
|
48
|
+
const response = await fetch(`${QWEN_OAUTH_BASE_URL}${QWEN_TOKEN_ENDPOINT}`, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
51
|
+
body: params.toString(),
|
|
52
|
+
});
|
|
53
|
+
if (response.ok) {
|
|
54
|
+
const data = (await response.json());
|
|
55
|
+
debugLog("Token received successfully");
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
access_token: data.access_token,
|
|
59
|
+
refresh_token: data.refresh_token,
|
|
60
|
+
expires_in: data.expires_in,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const error = (await response.json().catch(() => ({})));
|
|
64
|
+
if (error.error === "authorization_pending") {
|
|
65
|
+
debugLog("Authorization pending, retrying...");
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (error.error === "slow_down") {
|
|
69
|
+
currentInterval += 5000;
|
|
70
|
+
debugLog("Server requested slow down, new interval:", {
|
|
71
|
+
interval: currentInterval,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (error.error === "expired_token") {
|
|
76
|
+
debugLog("Device code expired");
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
error: "Device code expired. Please try again.",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
debugLog(`Token polling failed: ${error.error_description || "unknown error"}`);
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: error.error_description || "Authentication failed",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
debugLog("Polling timeout exceeded");
|
|
89
|
+
return { success: false, error: "Polling timeout - device code expired" };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|
package/dist/pkce.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,wBAAgB,cAAc,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMxE"}
|
package/dist/pkce.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) utilities
|
|
3
|
+
*/
|
|
4
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
5
|
+
function base64UrlEncode(buffer) {
|
|
6
|
+
return buffer
|
|
7
|
+
.toString("base64")
|
|
8
|
+
.replace(/\+/g, "-")
|
|
9
|
+
.replace(/\//g, "_")
|
|
10
|
+
.replace(/=+$/, "");
|
|
11
|
+
}
|
|
12
|
+
export function createPkcePair() {
|
|
13
|
+
const verifier = base64UrlEncode(randomBytes(32));
|
|
14
|
+
const challenge = base64UrlEncode(createHash("sha256").update(verifier).digest());
|
|
15
|
+
return { verifier, challenge };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=pkce.js.map
|
package/dist/pkce.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM;SACV,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,QAAQ,GAAG,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,eAAe,CAC/B,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAC/C,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
|
package/package.json
CHANGED