qwen-opencode-provider 1.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 +144 -0
- package/index.js +456 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# OpenCode Qwen Plugin
|
|
2
|
+
|
|
3
|
+
Plugin cho OpenCode để thêm Qwen AI provider với đầy đủ models.
|
|
4
|
+
|
|
5
|
+
## Tính năng
|
|
6
|
+
|
|
7
|
+
- ✅ **Tự động thêm Qwen provider** vào OpenCode config
|
|
8
|
+
- ✅ **28+ Qwen models** được tích hợp sẵn
|
|
9
|
+
- ✅ **Custom tools** để làm việc với Qwen API:
|
|
10
|
+
- `qwen-validate-token` - Validate Qwen token
|
|
11
|
+
- `qwen-list-models` - Liệt kê models
|
|
12
|
+
- `qwen-setup` - Hướng dẫn cài đặt
|
|
13
|
+
- `qwen-test` - Test kết nối API
|
|
14
|
+
|
|
15
|
+
## Cài đặt
|
|
16
|
+
|
|
17
|
+
### Cách 1: Local Plugin (Khuyến nghị)
|
|
18
|
+
|
|
19
|
+
1. **Tạo thư mục plugins:**
|
|
20
|
+
```bash
|
|
21
|
+
mkdir -p ~/.config/opencode/plugins
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. **Copy plugin vào thư mục:**
|
|
25
|
+
```bash
|
|
26
|
+
cp -r opencode-qwen-plugin/* ~/.config/opencode/plugins/qwen/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. **Tạo package.json trong config directory:**
|
|
30
|
+
```bash
|
|
31
|
+
mkdir -p ~/.config/opencode
|
|
32
|
+
cp opencode-qwen-plugin/package.json ~/.config/opencode/
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
4. **Khởi động lại OpenCode**
|
|
36
|
+
|
|
37
|
+
### Cách 2: NPM Package
|
|
38
|
+
|
|
39
|
+
Thêm vào `opencode.json`:
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"plugin": ["opencode-qwen-plugin"]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Cách lấy Qwen API Token
|
|
47
|
+
|
|
48
|
+
### Bước 1: Đăng nhập Qwen
|
|
49
|
+
Truy cập https://chat.qwen.ai và đăng nhập
|
|
50
|
+
|
|
51
|
+
### Bước 2: Lấy Token
|
|
52
|
+
Mở Developer Console (F12) và chạy:
|
|
53
|
+
```javascript
|
|
54
|
+
localStorage.getItem('token')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Hoặc sử dụng bookmarklet:
|
|
58
|
+
```javascript
|
|
59
|
+
javascript:(function(){if(window.location.hostname!=="chat.qwen.ai"){alert("🚀 This code is for chat.qwen.ai");window.open("https://chat.qwen.ai","_blank");return;}function getApiKeyData(){const token=localStorage.getItem(!token){alert("token");if("❌ qwen access_token not found !!!");return null;}return token;}async function copyToClipboard(text){try{await navigator.clipboard.writeText(text);return true;}catch(err){console.error("❌ Failed to copy to clipboard:",err);const textarea=document.createElement("textarea");textarea.value=text;textarea.style.position="fixed";textarea.style.opacity="0";document.body.appendChild(textarea);textarea.focus();textarea.select();const success=document.execCommand("copy");document.body.removeChild(textarea);return success;}}const apiKeyData=getApiKeyData();if(!apiKeyData)return;copyToClipboard(apiKeyData).then((success)=>{if(success){alert("🔑 Qwen access_token copied to clipboard !!! 🎉");}else{prompt("🔰 Qwen access_token:",apiKeyData);}});})();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Bước 3: Thêm vào OpenCode
|
|
63
|
+
```bash
|
|
64
|
+
/connect
|
|
65
|
+
```
|
|
66
|
+
Tìm "Qwen" và nhập token của bạn.
|
|
67
|
+
|
|
68
|
+
## Sử dụng
|
|
69
|
+
|
|
70
|
+
### Chọn model:
|
|
71
|
+
```bash
|
|
72
|
+
/models
|
|
73
|
+
```
|
|
74
|
+
Chọn model Qwen bạn muốn sử dụng.
|
|
75
|
+
|
|
76
|
+
### Sử dụng custom tools:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Validate token
|
|
80
|
+
qwen-validate-token --token YOUR_TOKEN
|
|
81
|
+
|
|
82
|
+
# List models
|
|
83
|
+
qwen-list-models --apiKey YOUR_API_KEY
|
|
84
|
+
|
|
85
|
+
# Test connection
|
|
86
|
+
qwen-test --apiKey YOUR_API_KEY --message "Hello!"
|
|
87
|
+
|
|
88
|
+
# Xem hướng dẫn setup
|
|
89
|
+
qwen-setup
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Models có sẵn
|
|
93
|
+
|
|
94
|
+
| Model | Description |
|
|
95
|
+
|-------|-------------|
|
|
96
|
+
| `qwen-max` | Latest Qwen Max |
|
|
97
|
+
| `qwen2.5-max` | Best overall with vision + web search |
|
|
98
|
+
| `qwen2.5-turbo` | Fast responses |
|
|
99
|
+
| `qwen2.5-plus` | Balanced performance |
|
|
100
|
+
| `qwen2.5-coder-32b` | Code generation |
|
|
101
|
+
| `qwen3-next-80b-a3b` | Next gen 80B |
|
|
102
|
+
| `qwen3-coder` | Code + tool calling |
|
|
103
|
+
| `qwen3-max` | Best Qwen3 |
|
|
104
|
+
| `qwq-32b` | Reasoning with thinking |
|
|
105
|
+
| `qwen-deep-research` | Research + web search |
|
|
106
|
+
| `q | Web development |
|
|
107
|
+
wen-web-dev`| `qwen-full-stack` | Full-stack apps |
|
|
108
|
+
|
|
109
|
+
## Tính năng Qwen API
|
|
110
|
+
|
|
111
|
+
- 👁️ **Vision** - Phân tích hình ảnh
|
|
112
|
+
- 🌐 **Web Search** - Tìm kiếm web
|
|
113
|
+
- 🧠 **Thinking Mode** - Chế độ suy nghĩ
|
|
114
|
+
- 🎨 **Image Generation** - Tạo hình ảnh
|
|
115
|
+
- 👨💻 **Code Generation** - Viết code
|
|
116
|
+
|
|
117
|
+
## Cấu hình thủ công
|
|
118
|
+
|
|
119
|
+
Nếu plugin không tự động thêm provider, thêm vào `~/.config/opencode/opencode.json`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"provider": {
|
|
124
|
+
"qwen": {
|
|
125
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
126
|
+
"name": "Qwen AI",
|
|
127
|
+
"options": {
|
|
128
|
+
"baseURL": "https://qwen.aikit.club/v1",
|
|
129
|
+
"headers": {
|
|
130
|
+
"Authorization": "Bearer ${QWEN_API_KEY}"
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"models": {
|
|
134
|
+
"qwen2.5-max": { "name": "Qwen2.5 Max" },
|
|
135
|
+
"qwen2.5-turbo": { "name": "Qwen2.5 Turbo" }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Qwen API Plugin
|
|
3
|
+
*
|
|
4
|
+
* This plugin adds Qwen AI provider to OpenCode and provides custom tools
|
|
5
|
+
* for validating tokens and listing models.
|
|
6
|
+
*
|
|
7
|
+
* Provider ID: qwen
|
|
8
|
+
* Base URL: https://qwen.aikit.club/v1
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
|
|
15
|
+
// Qwen API Configuration
|
|
16
|
+
const QWEN_BASE_URL = 'https://qwen.aikit.club/v1';
|
|
17
|
+
const QWEN_PROVIDER_ID = 'qwen';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the OpenCode config file path
|
|
21
|
+
*/
|
|
22
|
+
function getConfigPath() {
|
|
23
|
+
const homeDir = homedir();
|
|
24
|
+
return join(homeDir, '.config', 'opencode', 'opencode.json');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Read and parse OpenCode config
|
|
29
|
+
*/
|
|
30
|
+
function readConfig() {
|
|
31
|
+
const configPath = getConfigPath();
|
|
32
|
+
if (!existsSync(configPath)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
37
|
+
return JSON.parse(content);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Write OpenCode config
|
|
45
|
+
*/
|
|
46
|
+
function writeConfig(config) {
|
|
47
|
+
const configPath = getConfigPath();
|
|
48
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if Qwen provider is already configured
|
|
53
|
+
*/
|
|
54
|
+
function isQwenProviderConfigured(config) {
|
|
55
|
+
return config?.provider?.qwen !== undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Add Qwen provider to OpenCode config
|
|
60
|
+
*/
|
|
61
|
+
function addQwenProvider() {
|
|
62
|
+
const configPath = getConfigPath();
|
|
63
|
+
let config = readConfig();
|
|
64
|
+
|
|
65
|
+
if (!config) {
|
|
66
|
+
// new Create config with Qwen provider
|
|
67
|
+
config = {
|
|
68
|
+
$schema: "https://opencode.ai/config.json",
|
|
69
|
+
provider: {
|
|
70
|
+
[QWEN_PROVIDER_ID]: {
|
|
71
|
+
npm: "@ai-sdk/openai-compatible",
|
|
72
|
+
name: "Qwen AI",
|
|
73
|
+
options: {
|
|
74
|
+
baseURL: QWEN_BASE_URL,
|
|
75
|
+
headers: {
|
|
76
|
+
"Authorization": "Bearer ${QWEN_API_KEY}"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
models: {
|
|
80
|
+
// Chat Completions
|
|
81
|
+
"qwen-max": {
|
|
82
|
+
name: "Qwen Max",
|
|
83
|
+
description: "Latest Qwen Max model with best performance"
|
|
84
|
+
},
|
|
85
|
+
"qwen-max-latest": {
|
|
86
|
+
name: "Qwen Max Latest",
|
|
87
|
+
description: "Latest version of Qwen Max"
|
|
88
|
+
},
|
|
89
|
+
"qwen2.5-max": {
|
|
90
|
+
name: "Qwen2.5 Max",
|
|
91
|
+
description: "Qwen2.5 series with vision, reasoning and web search"
|
|
92
|
+
},
|
|
93
|
+
"qwen2.5-plus": {
|
|
94
|
+
name: "Qwen2.5 Plus",
|
|
95
|
+
description: "Balanced performance with vision support"
|
|
96
|
+
},
|
|
97
|
+
"qwen2.5-turbo": {
|
|
98
|
+
name: "Qwen2.5 Turbo",
|
|
99
|
+
description: "Fast responses with vision support"
|
|
100
|
+
},
|
|
101
|
+
"qwen2.5-14b-instruct-1m": {
|
|
102
|
+
name: "Qwen2.5 14B Instruct 1M",
|
|
103
|
+
description: "14B parameters with 1M context window"
|
|
104
|
+
},
|
|
105
|
+
"qwen2.5-72b-instruct": {
|
|
106
|
+
name: "Qwen2.5 72B Instruct",
|
|
107
|
+
description: "72B parameters for complex tasks"
|
|
108
|
+
},
|
|
109
|
+
"qwen2.5-coder-32b-instruct": {
|
|
110
|
+
name: "Qwen2.5 Coder 32B",
|
|
111
|
+
description: "Specialized code generation model"
|
|
112
|
+
},
|
|
113
|
+
"qwen2.5-omni-7b": {
|
|
114
|
+
name: "Qwen2.5 Omni 7B",
|
|
115
|
+
description: "Multimodal model with text, image, audio support"
|
|
116
|
+
},
|
|
117
|
+
"qwen2.5-vl-32b-instruct": {
|
|
118
|
+
name: "Qwen2.5 VL 32B",
|
|
119
|
+
description: "Vision-language model"
|
|
120
|
+
},
|
|
121
|
+
// Qwen3 Series
|
|
122
|
+
"qwen3-next-80b-a3b": {
|
|
123
|
+
name: "Qwen3 Next 80B A3B",
|
|
124
|
+
description: "Next generation with 80B parameters"
|
|
125
|
+
},
|
|
126
|
+
"qwen3-235b-a22b-2507": {
|
|
127
|
+
name: "Qwen3 235B A22B",
|
|
128
|
+
description: "Large model with 235B parameters"
|
|
129
|
+
},
|
|
130
|
+
"qwen3-30b-a3b-2507": {
|
|
131
|
+
name: "Qwen3 30B A3B",
|
|
132
|
+
description: "Compact high-performance model"
|
|
133
|
+
},
|
|
134
|
+
"qwen3-coder": {
|
|
135
|
+
name: "Qwen3 Coder",
|
|
136
|
+
description: "Code generation with tool calling"
|
|
137
|
+
},
|
|
138
|
+
"qwen3-coder-flash": {
|
|
139
|
+
name: "Qwen3 Coder Flash",
|
|
140
|
+
description: "Fast code generation"
|
|
141
|
+
},
|
|
142
|
+
"qwen3-max": {
|
|
143
|
+
name: "Qwen3 Max",
|
|
144
|
+
description: "Best Qwen3 performance"
|
|
145
|
+
},
|
|
146
|
+
"qwen3-omni-flash": {
|
|
147
|
+
name: "Qwen3 Omni Flash",
|
|
148
|
+
description: "Fast multimodal model"
|
|
149
|
+
},
|
|
150
|
+
"qwen3-vl-235b-a22b": {
|
|
151
|
+
name: "Qwen3 VL 235B A22B",
|
|
152
|
+
description: "Large vision-language model"
|
|
153
|
+
},
|
|
154
|
+
"qwen3-vl-32b": {
|
|
155
|
+
name: "Qwen3 VL 32B",
|
|
156
|
+
description: "Vision-language model 32B"
|
|
157
|
+
},
|
|
158
|
+
"qwen3-vl-30b-a3b": {
|
|
159
|
+
name: "Qwen3 VL 30B A3B",
|
|
160
|
+
description: "Compact vision-language model"
|
|
161
|
+
},
|
|
162
|
+
// Reasoning Models
|
|
163
|
+
"qvq-max": {
|
|
164
|
+
name: "QVQ Max",
|
|
165
|
+
description: "Vision reasoning model"
|
|
166
|
+
},
|
|
167
|
+
"qwq-32b": {
|
|
168
|
+
name: "QWQ 32B",
|
|
169
|
+
description: "Reasoning model with thinking"
|
|
170
|
+
},
|
|
171
|
+
"qwen-deep-research": {
|
|
172
|
+
name: "Qwen Deep Research",
|
|
173
|
+
description: "Research-focused model with web search"
|
|
174
|
+
},
|
|
175
|
+
// Development Models
|
|
176
|
+
"qwen-web-dev": {
|
|
177
|
+
name: "Qwen Web Dev",
|
|
178
|
+
description: "Web development specialized model"
|
|
179
|
+
},
|
|
180
|
+
"qwen-full-stack": {
|
|
181
|
+
name: "Qwen Full Stack",
|
|
182
|
+
description: "Full-stack application development"
|
|
183
|
+
},
|
|
184
|
+
// Image Generation
|
|
185
|
+
"qwen-cogview": {
|
|
186
|
+
name: "Qwen CogView",
|
|
187
|
+
description: "Image generation model"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
} else {
|
|
194
|
+
// Add Qwen provider to existing config
|
|
195
|
+
if (!config.provider) {
|
|
196
|
+
config.provider = {};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!isQwenProviderConfigured(config)) {
|
|
200
|
+
config.provider[QWEN_PROVIDER_ID] = {
|
|
201
|
+
npm: "@ai-sdk/openai-compatible",
|
|
202
|
+
name: "Qwen AI",
|
|
203
|
+
options: {
|
|
204
|
+
baseURL: QWEN_BASE_URL,
|
|
205
|
+
headers: {
|
|
206
|
+
"Authorization": "Bearer ${QWEN_API_KEY}"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
models: {
|
|
210
|
+
"qwen-max": { name: "Qwen Max" },
|
|
211
|
+
"qwen-max-latest": { name: "Qwen Max Latest" },
|
|
212
|
+
"qwen2.5-max": { name: "Qwen2.5 Max" },
|
|
213
|
+
"qwen2.5-plus": { name: "Qwen2.5 Plus" },
|
|
214
|
+
"qwen2.5-turbo": { name: "Qwen2.5 Turbo" },
|
|
215
|
+
"qwen2.5-14b-instruct-1m": { name: "Qwen2.5 14B Instruct 1M" },
|
|
216
|
+
"qwen2.5-72b-instruct": { name: "Qwen2.5 72B Instruct" },
|
|
217
|
+
"qwen2.5-coder-32b-instruct": { name: "Qwen2.5 Coder 32B" },
|
|
218
|
+
"qwen2.5-omni-7b": { name: "Qwen2.5 Omni 7B" },
|
|
219
|
+
"qwen2.5-vl-32b-instruct": { name: "Qwen2.5 VL 32B" },
|
|
220
|
+
"qwen3-next-80b-a3b": { name: "Qwen3 Next 80B A3B" },
|
|
221
|
+
"qwen3-235b-a22b-2507": { name: "Qwen3 235B A22B" },
|
|
222
|
+
"qwen3-30b-a3b-2507": { name: "Qwen3 30B A3B" },
|
|
223
|
+
"qwen3-coder": { name: "Qwen3 Coder" },
|
|
224
|
+
"qwen3-coder-flash": { name: "Qwen3 Coder Flash" },
|
|
225
|
+
"qwen3-max": { name: "Qwen3 Max" },
|
|
226
|
+
"qwen3-omni-flash": { name: "Qwen3 Omni Flash" },
|
|
227
|
+
"qwen3-vl-235b-a22b": { name: "Qwen3 VL 235B A22B" },
|
|
228
|
+
"qwen3-vl-32b": { name: "Qwen3 VL 32B" },
|
|
229
|
+
"qwen3-vl-30b-a3b": { name: "Qwen3 VL 30B A3B" },
|
|
230
|
+
"qvq-max": { name: "QVQ Max" },
|
|
231
|
+
"qwq-32b": { name: "QWQ 32B" },
|
|
232
|
+
"qwen-deep-research": { name: "Qwen Deep Research" },
|
|
233
|
+
"qwen-web-dev": { name: "Qwen Web Dev" },
|
|
234
|
+
"qwen-full-stack": { name: "Qwen Full Stack" },
|
|
235
|
+
"qwen-cogview": { name: "Qwen CogView" }
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
writeConfig(config);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* OpenCode Plugin
|
|
247
|
+
*/
|
|
248
|
+
export const QwenPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
249
|
+
// Automatically add Qwen provider on first load
|
|
250
|
+
try {
|
|
251
|
+
const configPath = getConfigPath();
|
|
252
|
+
if (!existsSync(dirname(configPath))) {
|
|
253
|
+
// Config directory doesn't exist yet, skip
|
|
254
|
+
} else {
|
|
255
|
+
const config = readConfig();
|
|
256
|
+
if (!isQwenProviderConfigured(config)) {
|
|
257
|
+
addQwenProvider();
|
|
258
|
+
await client.app.log({
|
|
259
|
+
body: {
|
|
260
|
+
service: "qwen-plugin",
|
|
261
|
+
level: "info",
|
|
262
|
+
message: "Qwen provider added to OpenCode config",
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
// Silently fail - provider can be added manually
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
// Custom tools for Qwen API
|
|
273
|
+
tool: {
|
|
274
|
+
/**
|
|
275
|
+
* Validate Qwen API token
|
|
276
|
+
*/
|
|
277
|
+
"qwen-validate-token": {
|
|
278
|
+
description: "Validate a Qwen API access token",
|
|
279
|
+
args: {
|
|
280
|
+
token: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "The Qwen access token to validate",
|
|
283
|
+
required: true
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
async execute(args, context) {
|
|
287
|
+
const { directory, worktree } = context;
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const response = await fetch(`${QWEN_BASE_URL}/validate?token=${args.token}`, {
|
|
291
|
+
method: "GET",
|
|
292
|
+
headers: {
|
|
293
|
+
"Content-Type": "application/json"
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const data = await response.json();
|
|
298
|
+
|
|
299
|
+
if (data.valid) {
|
|
300
|
+
return `✅ Token is valid!\nUser ID: ${data.user_id || 'N/A'}\nExpires: ${data.expires_at || 'N/A'}`;
|
|
301
|
+
} else {
|
|
302
|
+
return `❌ Token is invalid or expired`;
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return `Error validating token: ${error.message}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* List available Qwen models
|
|
312
|
+
*/
|
|
313
|
+
"qwen-list-models": {
|
|
314
|
+
description: "List all available Qwen models",
|
|
315
|
+
args: {
|
|
316
|
+
apiKey: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "Qwen API key (Bearer token)",
|
|
319
|
+
required: true
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
async execute(args, context) {
|
|
323
|
+
try {
|
|
324
|
+
const response = await fetch(`${QWEN_BASE_URL}/models`, {
|
|
325
|
+
method: "GET",
|
|
326
|
+
headers: {
|
|
327
|
+
"Authorization": `Bearer ${args.apiKey}`,
|
|
328
|
+
"Content-Type": "application/json"
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const data = await response.json();
|
|
333
|
+
|
|
334
|
+
if (data.data && Array.isArray(data.data)) {
|
|
335
|
+
const models = data.data.map(m => `• ${m.id} - ${m.description || 'No description'}`).join('\n');
|
|
336
|
+
return `Available Qwen Models:\n\n${models}`;
|
|
337
|
+
} else {
|
|
338
|
+
return `Error: Could not fetch models - ${JSON.stringify(data)}`;
|
|
339
|
+
}
|
|
340
|
+
} catch (error) {
|
|
341
|
+
return `Error listing models: ${error.message}`;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get Qwen API setup instructions
|
|
348
|
+
*/
|
|
349
|
+
"qwen-setup": {
|
|
350
|
+
description: "Get instructions for setting up Qwen API in OpenCode",
|
|
351
|
+
args: {},
|
|
352
|
+
async execute(args, context) {
|
|
353
|
+
return `
|
|
354
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
355
|
+
║ QWEN API SETUP GUIDE ║
|
|
356
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
357
|
+
║ ║
|
|
358
|
+
║ 1. GET YOUR TOKEN: ║
|
|
359
|
+
║ • Visit https://chat.qwen.ai ║
|
|
360
|
+
║ • Login and open Developer Console (F12) ║
|
|
361
|
+
║ • Run: localStorage.getItem('token') ║
|
|
362
|
+
║ • Or use the provided JavaScript bookmarklet ║
|
|
363
|
+
║ ║
|
|
364
|
+
║ 2. ADD TO OPENCODE: ║
|
|
365
|
+
║ Run: /connect ║
|
|
366
|
+
║ Search: qwen ║
|
|
367
|
+
║ Enter your token ║
|
|
368
|
+
║ ║
|
|
369
|
+
║ 3. SELECT MODEL: ║
|
|
370
|
+
║ Run: /models ║
|
|
371
|
+
║ Select a Qwen model ║
|
|
372
|
+
║ ║
|
|
373
|
+
║ AVAILABLE MODELS: ║
|
|
374
|
+
║ • qwen2.5-max - Best overall performance ║
|
|
375
|
+
║ • qwen2.5-turbo - Fast responses ║
|
|
376
|
+
║ • qwen2.5-plus - Balanced ║
|
|
377
|
+
║ • qwen3-coder - Code generation ║
|
|
378
|
+
║ • qwen3-max - Latest Qwen3 ║
|
|
379
|
+
║ • qwen-deep-research - Research tasks ║
|
|
380
|
+
║ • qwen-web-dev - Web development ║
|
|
381
|
+
║ • qwen-full-stack - Full-stack apps ║
|
|
382
|
+
║ ║
|
|
383
|
+
║ FEATURES: ║
|
|
384
|
+
║ ✅ Vision (image analysis) ║
|
|
385
|
+
║ ✅ Web Search ║
|
|
386
|
+
║ ✅ Thinking Mode ║
|
|
387
|
+
║ ✅ Image Generation ║
|
|
388
|
+
║ ✅ Code Generation ║
|
|
389
|
+
║ ║
|
|
390
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
391
|
+
`;
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Test Qwen API connection
|
|
397
|
+
*/
|
|
398
|
+
"qwen-test": {
|
|
399
|
+
description: "Test Qwen API connection with a simple prompt",
|
|
400
|
+
args: {
|
|
401
|
+
apiKey: {
|
|
402
|
+
type: "string",
|
|
403
|
+
description: "Qwen API key",
|
|
404
|
+
required: true
|
|
405
|
+
},
|
|
406
|
+
message: {
|
|
407
|
+
type: "string",
|
|
408
|
+
description: "Test message (default: 'Hello')",
|
|
409
|
+
required: false
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
async execute(args, context) {
|
|
413
|
+
const message = args.message || "Hello, how are you?";
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
const response = await fetch(`${QWEN_BASE_URL}/chat/completions`, {
|
|
417
|
+
method: "POST",
|
|
418
|
+
headers: {
|
|
419
|
+
"Authorization": `Bearer ${args.apiKey}`,
|
|
420
|
+
"Content-Type": "application/json"
|
|
421
|
+
},
|
|
422
|
+
body: JSON.stringify({
|
|
423
|
+
model: "qwen2.5-turbo",
|
|
424
|
+
messages: [{ role: "user", content: message }],
|
|
425
|
+
stream: false
|
|
426
|
+
})
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const data = await response.json();
|
|
430
|
+
|
|
431
|
+
if (data.choices && data.choices[0]) {
|
|
432
|
+
return `✅ Connection successful!\n\nModel: ${data.model}\nResponse: ${data.choices[0].message.content}`;
|
|
433
|
+
} else {
|
|
434
|
+
return `❌ Error: ${JSON.stringify(data)}`;
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
return `❌ Connection failed: ${error.message}`;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
// Event hooks
|
|
444
|
+
"session.created": async ({ session }) => {
|
|
445
|
+
await client.app.log({
|
|
446
|
+
body: {
|
|
447
|
+
service: "qwen-plugin",
|
|
448
|
+
level: "debug",
|
|
449
|
+
message: `New session created: ${session.id}`,
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
export default QwenPlugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qwen-opencode-provider",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "OpenCode plugin for Qwen API - adds provider and 28+ models with custom tools",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": ["opencode", "plugin", "qwen", "ai", "alibaba", "qwen-api", "chatgpt"],
|
|
8
|
+
"author": "",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/tanu1337/qwen-api"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/tanu1337/qwen-api#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/tanu1337/qwen-api/issues"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {}
|
|
19
|
+
}
|