qwen-opencode-provider 2.0.0 → 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/index.js +49 -32
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenCode Qwen API Plugin
|
|
3
3
|
*
|
|
4
|
-
* Uses local proxy approach
|
|
5
|
-
* No manual config needed!
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* 1. Add to opencode.json: { "plugin": ["qwen-opencode-provider"] }
|
|
9
|
-
* 2. Run /connect -> Select "Other" -> Enter "qwen"
|
|
4
|
+
* Uses local proxy approach to handle auth - reads API key from OpenCode auth storage.
|
|
10
5
|
*/
|
|
11
6
|
|
|
12
7
|
import http from 'http';
|
|
8
|
+
import https from 'https';
|
|
13
9
|
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import os from 'os';
|
|
14
12
|
|
|
15
13
|
const LOG_FILE = '/tmp/opencode_qwen_plugin.log';
|
|
16
14
|
const PROVIDER_ID = 'qwen';
|
|
@@ -43,13 +41,32 @@ function log(msg) {
|
|
|
43
41
|
} catch (e) { }
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
function getAuthFilePath() {
|
|
45
|
+
const home = os.homedir();
|
|
46
|
+
return path.join(home, '.local', 'share', 'opencode', 'auth.json');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readApiKeyFromAuth() {
|
|
50
|
+
try {
|
|
51
|
+
const authPath = getAuthFilePath();
|
|
52
|
+
if (fs.existsSync(authPath)) {
|
|
53
|
+
const content = fs.readFileSync(authPath, 'utf-8');
|
|
54
|
+
const auth = JSON.parse(content);
|
|
55
|
+
|
|
56
|
+
if (auth[PROVIDER_ID]?.apiKey) {
|
|
57
|
+
log(`[Auth] Found API key for ${PROVIDER_ID}`);
|
|
58
|
+
return auth[PROVIDER_ID].apiKey;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {
|
|
62
|
+
log(`[Auth] Error reading auth: ${e.message}`);
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
47
66
|
|
|
48
67
|
function startProxy() {
|
|
49
68
|
return new Promise((resolve) => {
|
|
50
69
|
const server = http.createServer(async (req, res) => {
|
|
51
|
-
log(`[Proxy] Request: ${req.method} ${req.url}`);
|
|
52
|
-
|
|
53
70
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
54
71
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
55
72
|
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
@@ -61,27 +78,37 @@ function startProxy() {
|
|
|
61
78
|
}
|
|
62
79
|
|
|
63
80
|
if (req.method === 'GET' && req.url === '/health') {
|
|
81
|
+
const apiKey = readApiKeyFromAuth();
|
|
64
82
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
65
|
-
res.end(JSON.stringify({
|
|
83
|
+
res.end(JSON.stringify({
|
|
84
|
+
status: 'ok',
|
|
85
|
+
provider: 'qwen',
|
|
86
|
+
hasKey: !!apiKey
|
|
87
|
+
}));
|
|
66
88
|
return;
|
|
67
89
|
}
|
|
68
90
|
|
|
69
91
|
if (req.url.startsWith('/v1/')) {
|
|
70
|
-
const
|
|
92
|
+
const apiKey = readApiKeyFromAuth();
|
|
71
93
|
|
|
94
|
+
log(`[Proxy] Request: ${req.method} ${req.url}, hasKey: ${!!apiKey}`);
|
|
95
|
+
|
|
72
96
|
const options = {
|
|
73
|
-
hostname:
|
|
97
|
+
hostname: 'qwen.aikit.club',
|
|
74
98
|
port: 443,
|
|
75
99
|
path: req.url,
|
|
76
100
|
method: req.method,
|
|
77
101
|
headers: {
|
|
78
102
|
...req.headers,
|
|
79
|
-
'Host':
|
|
103
|
+
'Host': 'qwen.aikit.club'
|
|
80
104
|
}
|
|
81
105
|
};
|
|
82
106
|
|
|
83
|
-
if (
|
|
84
|
-
options.headers['Authorization'] = `Bearer ${
|
|
107
|
+
if (apiKey) {
|
|
108
|
+
options.headers['Authorization'] = `Bearer ${apiKey}`;
|
|
109
|
+
log(`[Proxy] Added Authorization header`);
|
|
110
|
+
} else {
|
|
111
|
+
log(`[Proxy] No API key found!`);
|
|
85
112
|
}
|
|
86
113
|
|
|
87
114
|
const proxyReq = https.request(options, (proxyRes) => {
|
|
@@ -90,7 +117,7 @@ function startProxy() {
|
|
|
90
117
|
});
|
|
91
118
|
|
|
92
119
|
proxyReq.on('error', (e) => {
|
|
93
|
-
log(`[Proxy] Error: ${e}`);
|
|
120
|
+
log(`[Proxy] Error: ${e.message}`);
|
|
94
121
|
res.writeHead(500);
|
|
95
122
|
res.end(JSON.stringify({ error: String(e) }));
|
|
96
123
|
});
|
|
@@ -110,20 +137,20 @@ function startProxy() {
|
|
|
110
137
|
});
|
|
111
138
|
|
|
112
139
|
server.on('error', (e) => {
|
|
113
|
-
log(`[Proxy] Fatal Error: ${e}`);
|
|
140
|
+
log(`[Proxy] Fatal Error: ${e.message}`);
|
|
114
141
|
resolve(0);
|
|
115
142
|
});
|
|
116
143
|
});
|
|
117
144
|
}
|
|
118
145
|
|
|
119
|
-
import https from 'https';
|
|
120
|
-
|
|
121
146
|
export const QwenPlugin = async (ctx) => {
|
|
122
147
|
log('[Plugin] Initializing Qwen Plugin...');
|
|
123
148
|
|
|
124
149
|
const port = await startProxy();
|
|
125
150
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
126
151
|
|
|
152
|
+
log(`[Plugin] Local proxy URL: ${localBaseUrl}`);
|
|
153
|
+
|
|
127
154
|
return {
|
|
128
155
|
async config(config) {
|
|
129
156
|
log('[Hook] config() called');
|
|
@@ -140,22 +167,12 @@ export const QwenPlugin = async (ctx) => {
|
|
|
140
167
|
log(`[Hook] Registered provider with ${Object.keys(QWEN_MODELS).length} models`);
|
|
141
168
|
},
|
|
142
169
|
|
|
143
|
-
async 'installation.updated'({ installation }) {
|
|
144
|
-
log('[Hook] installation.updated() called');
|
|
145
|
-
|
|
146
|
-
if (installation?.auth?.[PROVIDER_ID]?.apiKey) {
|
|
147
|
-
cachedApiKey = installation.auth[PROVIDER_ID].apiKey;
|
|
148
|
-
log('[Hook] API Key updated');
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
|
|
152
170
|
auth: {
|
|
153
171
|
provider: PROVIDER_ID,
|
|
154
172
|
loader: async (auth) => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
return { apiKey: cachedApiKey };
|
|
173
|
+
const apiKey = auth?.apiKey || '';
|
|
174
|
+
log(`[Auth] loader called, hasKey: ${!!apiKey}`);
|
|
175
|
+
return { apiKey };
|
|
159
176
|
},
|
|
160
177
|
methods: [
|
|
161
178
|
{ type: 'api', label: 'API Key' }
|