qwen-opencode-provider 2.0.3 → 3.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/README.md +40 -24
- package/index.js +8 -50
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,32 +1,43 @@
|
|
|
1
1
|
# OpenCode Qwen Plugin
|
|
2
2
|
|
|
3
|
-
OpenCode plugin for Qwen
|
|
3
|
+
OpenCode plugin for Qwen API - auto-configures models.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Important
|
|
6
|
+
|
|
7
|
+
⚠️ **Plugin requires manual provider config** - OpenCode doesn't support auto-registering providers via plugins yet.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- ✅ **Local proxy** - Handles auth automatically
|
|
9
|
-
- ✅ **28+ Models** - All Qwen models pre-configured
|
|
10
|
-
- ✅ **Native auth** - Uses OpenCode's /connect
|
|
9
|
+
Plugin only auto-configures the model list.
|
|
11
10
|
|
|
12
11
|
## Installation
|
|
13
12
|
|
|
14
|
-
Add to
|
|
13
|
+
### Step 1: Add plugin to opencode.json
|
|
14
|
+
|
|
15
15
|
```json
|
|
16
16
|
{
|
|
17
17
|
"plugin": ["qwen-opencode-provider"]
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
### Step 2: Add Provider Config
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Add this to your `opencode.json`:
|
|
24
24
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"$schema": "https://opencode.ai/config.json",
|
|
28
|
+
"plugin": ["qwen-opencode-provider"],
|
|
29
|
+
"provider": {
|
|
30
|
+
"qwen": {
|
|
31
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
32
|
+
"name": "Qwen",
|
|
33
|
+
"options": {
|
|
34
|
+
"baseURL": "https://qwen.aikit.club/v1",
|
|
35
|
+
"apiKey": "YOUR_QWEN_TOKEN"
|
|
36
|
+
},
|
|
37
|
+
"models": {}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
30
41
|
```
|
|
31
42
|
|
|
32
43
|
### Get Token
|
|
@@ -35,32 +46,37 @@ Add to `opencode.json`:
|
|
|
35
46
|
2. Open Developer Console (F12)
|
|
36
47
|
3. Run: `localStorage.getItem('token')`
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
## Usage
|
|
39
50
|
|
|
40
51
|
```bash
|
|
52
|
+
/connect
|
|
53
|
+
# Select: Other
|
|
54
|
+
# Enter: qwen
|
|
55
|
+
|
|
41
56
|
/models
|
|
42
|
-
#
|
|
57
|
+
# Select Qwen model
|
|
43
58
|
```
|
|
44
59
|
|
|
45
60
|
## Supported Models
|
|
46
61
|
|
|
62
|
+
Plugin auto-configures these models:
|
|
63
|
+
|
|
47
64
|
| Model | Context | Output |
|
|
48
65
|
|-------|---------|--------|
|
|
49
66
|
| qwen3-max | 262K | 32K |
|
|
50
|
-
| qwen3-coder-plus | 1M | 65K |
|
|
51
67
|
| qwen3-vl-plus | 262K | 32K |
|
|
68
|
+
| qwen3-coder-plus | 1M | 65K |
|
|
52
69
|
| qwen3-vl-32b | 131K | 32K |
|
|
70
|
+
| qwen3-coder-flash | 262K | 65K |
|
|
53
71
|
| qwq-32b | - | - |
|
|
54
72
|
| qwen-deep-research | - | - |
|
|
55
73
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
This plugin uses a **local proxy server** (like Pollinations plugin):
|
|
74
|
+
## Features
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
- 👁️ Vision (image analysis)
|
|
77
|
+
- 🌐 Web Search
|
|
78
|
+
- 🧠 Thinking Mode
|
|
79
|
+
- 👨💻 Code Generation
|
|
64
80
|
|
|
65
81
|
## License
|
|
66
82
|
|
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenCode Qwen API Plugin
|
|
3
3
|
*
|
|
4
|
-
* Uses local proxy
|
|
4
|
+
* Uses local proxy (like Pollinations) to handle auth automatically.
|
|
5
|
+
* No manual config needed!
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import http from 'http';
|
|
@@ -59,39 +60,26 @@ function getAuthFilePath() {
|
|
|
59
60
|
function readApiKeyFromAuth() {
|
|
60
61
|
try {
|
|
61
62
|
const authPath = getAuthFilePath();
|
|
62
|
-
log(`[Auth] Checking auth file: ${authPath}`);
|
|
63
63
|
|
|
64
64
|
if (fs.existsSync(authPath)) {
|
|
65
65
|
const content = fs.readFileSync(authPath, 'utf-8');
|
|
66
66
|
const auth = JSON.parse(content);
|
|
67
67
|
|
|
68
|
-
log(`[Auth] Auth file contents keys: ${Object.keys(auth).join(', ')}`);
|
|
69
|
-
|
|
70
|
-
// Try both apiKey and key fields
|
|
71
68
|
const providerAuth = auth[PROVIDER_ID];
|
|
72
69
|
if (providerAuth) {
|
|
73
|
-
|
|
70
|
+
// Read BOTH key and apiKey fields - OpenCode can use either
|
|
71
|
+
const apiKey = providerAuth.key || providerAuth.apiKey || providerAuth.token || null;
|
|
74
72
|
if (apiKey) {
|
|
75
|
-
const keyPreview = apiKey.substring(0, 20) + '...';
|
|
76
|
-
log(`[Auth] Found API key for ${PROVIDER_ID}: ${keyPreview}`);
|
|
77
73
|
return apiKey;
|
|
78
|
-
} else {
|
|
79
|
-
log(`[Auth] Provider exists but no key field: ${JSON.stringify(providerAuth)}`);
|
|
80
74
|
}
|
|
81
|
-
} else {
|
|
82
|
-
log(`[Auth] No provider found for ${PROVIDER_ID}`);
|
|
83
75
|
}
|
|
84
|
-
} else {
|
|
85
|
-
log(`[Auth] Auth file does not exist`);
|
|
86
76
|
}
|
|
87
77
|
} catch (e) {
|
|
88
|
-
log(`[Auth] Error
|
|
78
|
+
log(`[Auth] Error: ${e.message}`);
|
|
89
79
|
}
|
|
90
80
|
return null;
|
|
91
81
|
}
|
|
92
82
|
|
|
93
|
-
let serverInstance = null;
|
|
94
|
-
|
|
95
83
|
function startProxy() {
|
|
96
84
|
return new Promise((resolve) => {
|
|
97
85
|
const server = http.createServer(async (req, res) => {
|
|
@@ -118,9 +106,6 @@ function startProxy() {
|
|
|
118
106
|
|
|
119
107
|
if (req.url.startsWith('/v1/')) {
|
|
120
108
|
const apiKey = readApiKeyFromAuth();
|
|
121
|
-
|
|
122
|
-
log(`[Proxy] Request: ${req.method} ${req.url}`);
|
|
123
|
-
log(`[Proxy] Has API key: ${!!apiKey}`);
|
|
124
109
|
|
|
125
110
|
const options = {
|
|
126
111
|
hostname: 'qwen.aikit.club',
|
|
@@ -136,19 +121,14 @@ function startProxy() {
|
|
|
136
121
|
|
|
137
122
|
if (apiKey) {
|
|
138
123
|
options.headers['Authorization'] = `Bearer ${apiKey}`;
|
|
139
|
-
log(`[Proxy] Added Authorization: Bearer ${apiKey.substring(0, 20)}...`);
|
|
140
|
-
} else {
|
|
141
|
-
log(`[Proxy] NO API KEY - this will likely fail!`);
|
|
142
124
|
}
|
|
143
125
|
|
|
144
126
|
const proxyReq = https.request(options, (proxyRes) => {
|
|
145
|
-
log(`[Proxy] Response status: ${proxyRes.statusCode}`);
|
|
146
127
|
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
147
128
|
proxyRes.pipe(res, { end: true });
|
|
148
129
|
});
|
|
149
130
|
|
|
150
131
|
proxyReq.on('error', (e) => {
|
|
151
|
-
log(`[Proxy] Error: ${e.message}`);
|
|
152
132
|
res.writeHead(500);
|
|
153
133
|
res.end(JSON.stringify({ error: String(e) }));
|
|
154
134
|
});
|
|
@@ -157,7 +137,6 @@ function startProxy() {
|
|
|
157
137
|
return;
|
|
158
138
|
}
|
|
159
139
|
|
|
160
|
-
log(`[Proxy] Unknown request: ${req.method} ${req.url}`);
|
|
161
140
|
res.writeHead(404);
|
|
162
141
|
res.end('Not Found');
|
|
163
142
|
});
|
|
@@ -165,33 +144,24 @@ function startProxy() {
|
|
|
165
144
|
server.listen(0, '127.0.0.1', () => {
|
|
166
145
|
const port = server.address().port;
|
|
167
146
|
log(`[Proxy] Started on port ${port}`);
|
|
168
|
-
serverInstance = server;
|
|
169
147
|
resolve(port);
|
|
170
148
|
});
|
|
171
149
|
|
|
172
150
|
server.on('error', (e) => {
|
|
173
|
-
log(`[Proxy]
|
|
151
|
+
log(`[Proxy] Error: ${e.message}`);
|
|
174
152
|
resolve(0);
|
|
175
153
|
});
|
|
176
154
|
});
|
|
177
155
|
}
|
|
178
156
|
|
|
179
157
|
export const QwenPlugin = async (ctx) => {
|
|
180
|
-
log('[Plugin]
|
|
181
|
-
log(`[Plugin] Home: ${os.homedir()}`);
|
|
182
|
-
log(`[Plugin] Log file: ${LOG_FILE}`);
|
|
183
|
-
|
|
184
|
-
// Check auth immediately
|
|
185
|
-
const existingKey = readApiKeyFromAuth();
|
|
186
|
-
log(`[Plugin] Initial auth check - has key: ${!!existingKey}`);
|
|
158
|
+
log('[Plugin] Starting Qwen Plugin...');
|
|
187
159
|
|
|
188
160
|
const port = await startProxy();
|
|
189
161
|
const localBaseUrl = `http://127.0.0.1:${port}/v1`;
|
|
190
162
|
|
|
191
|
-
log(`[Plugin] Local proxy URL: ${localBaseUrl}`);
|
|
192
|
-
|
|
193
163
|
return {
|
|
194
|
-
async
|
|
164
|
+
config: async (config) => {
|
|
195
165
|
log('[Hook] config() called');
|
|
196
166
|
|
|
197
167
|
if (!config.provider) config.provider = {};
|
|
@@ -204,18 +174,6 @@ export const QwenPlugin = async (ctx) => {
|
|
|
204
174
|
};
|
|
205
175
|
|
|
206
176
|
log(`[Hook] Registered provider with ${Object.keys(QWEN_MODELS).length} models`);
|
|
207
|
-
},
|
|
208
|
-
|
|
209
|
-
auth: {
|
|
210
|
-
provider: PROVIDER_ID,
|
|
211
|
-
loader: async (auth) => {
|
|
212
|
-
const apiKey = auth?.apiKey || '';
|
|
213
|
-
log(`[Auth] loader called, hasKey: ${!!apiKey}`);
|
|
214
|
-
return { apiKey };
|
|
215
|
-
},
|
|
216
|
-
methods: [
|
|
217
|
-
{ type: 'api', label: 'API Key' }
|
|
218
|
-
]
|
|
219
177
|
}
|
|
220
178
|
};
|
|
221
179
|
};
|