opencode-qwen-oauth 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 +213 -0
- package/bin/install.js +236 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +250 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# opencode-qwen-oauth
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-qwen-oauth)
|
|
4
|
+
[](https://www.npmjs.com/package/opencode-qwen-oauth)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Qwen OAuth authentication plugin for [OpenCode](https://opencode.ai) - authenticate with Qwen.ai using OAuth device flow (PKCE).
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- š **OAuth Device Flow** - PKCE-secured authentication, works in headless/CI environments
|
|
12
|
+
- š **Automatic Token Refresh** - Tokens are refreshed before expiry
|
|
13
|
+
- š **Auto Browser Open** - Automatically opens browser for authentication
|
|
14
|
+
- š **File Logging** - All OAuth activity logged to `~/.config/opencode/logs/qwen-oauth.log`
|
|
15
|
+
- š **Debug Mode** - Enable verbose output with `QWEN_OAUTH_DEBUG=true`
|
|
16
|
+
- š **Easy Install** - One-command installation with CLI tool
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Using npx (recommended)
|
|
24
|
+
npx opencode-qwen-oauth install
|
|
25
|
+
|
|
26
|
+
# Or using bunx
|
|
27
|
+
bunx opencode-qwen-oauth install
|
|
28
|
+
|
|
29
|
+
# Or install manually
|
|
30
|
+
npm install opencode-qwen-oauth
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The installer will:
|
|
34
|
+
- Add `opencode-qwen-oauth` to your `.opencode/opencode.json` plugins
|
|
35
|
+
- Configure the Qwen provider with models
|
|
36
|
+
|
|
37
|
+
### Authenticate
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Start OpenCode
|
|
41
|
+
opencode
|
|
42
|
+
|
|
43
|
+
# Connect to Qwen
|
|
44
|
+
/connect
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Select **"Qwen Code (qwen.ai OAuth)"** and follow the device flow instructions.
|
|
48
|
+
|
|
49
|
+
### Use Qwen Models
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
/model qwen/qwen3-coder-plus
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Models
|
|
56
|
+
|
|
57
|
+
| Model | Context | Features |
|
|
58
|
+
|-------|---------|----------|
|
|
59
|
+
| `qwen3-coder-plus` | 1M tokens | Optimized for coding |
|
|
60
|
+
| `qwen3-vl-plus` | 256K tokens | Vision + language |
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
### Debug Mode
|
|
65
|
+
|
|
66
|
+
Enable verbose logging to console:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
QWEN_OAUTH_DEBUG=true opencode
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Log Files
|
|
73
|
+
|
|
74
|
+
All OAuth activity is logged to:
|
|
75
|
+
```
|
|
76
|
+
~/.config/opencode/logs/qwen-oauth.log
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
View logs in real-time:
|
|
80
|
+
```bash
|
|
81
|
+
tail -f ~/.config/opencode/logs/qwen-oauth.log
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Manual Configuration
|
|
85
|
+
|
|
86
|
+
If you prefer manual setup, add to `.opencode/opencode.json`:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"$schema": "https://opencode.ai/config.json",
|
|
91
|
+
"plugin": ["opencode-qwen-oauth"],
|
|
92
|
+
"provider": {
|
|
93
|
+
"qwen": {
|
|
94
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
95
|
+
"name": "Qwen Code",
|
|
96
|
+
"options": {
|
|
97
|
+
"baseURL": "https://portal.qwen.ai/v1"
|
|
98
|
+
},
|
|
99
|
+
"models": {
|
|
100
|
+
"qwen3-coder-plus": {
|
|
101
|
+
"id": "qwen3-coder-plus",
|
|
102
|
+
"name": "Qwen3 Coder Plus"
|
|
103
|
+
},
|
|
104
|
+
"qwen3-vl-plus": {
|
|
105
|
+
"id": "qwen3-vl-plus",
|
|
106
|
+
"name": "Qwen3 VL Plus",
|
|
107
|
+
"attachment": true
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## CLI Commands
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Install (default)
|
|
119
|
+
npx opencode-qwen-oauth install
|
|
120
|
+
|
|
121
|
+
# Uninstall
|
|
122
|
+
npx opencode-qwen-oauth uninstall
|
|
123
|
+
|
|
124
|
+
# Help
|
|
125
|
+
npx opencode-qwen-oauth --help
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Troubleshooting
|
|
129
|
+
|
|
130
|
+
### "Device code expired"
|
|
131
|
+
Complete the browser login within 5 minutes of starting `/connect`.
|
|
132
|
+
|
|
133
|
+
### "invalid_grant" error
|
|
134
|
+
Your refresh token has expired. Run `/connect` to re-authenticate.
|
|
135
|
+
|
|
136
|
+
### Provider not showing in /connect
|
|
137
|
+
Use the CLI directly:
|
|
138
|
+
```bash
|
|
139
|
+
opencode auth login qwen
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Check logs
|
|
143
|
+
```bash
|
|
144
|
+
cat ~/.config/opencode/logs/qwen-oauth.log
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## How It Works
|
|
148
|
+
|
|
149
|
+
This plugin implements OAuth 2.0 Device Flow (RFC 8628) with PKCE:
|
|
150
|
+
|
|
151
|
+
1. **Device Code Request** - Plugin requests a device code from Qwen OAuth server
|
|
152
|
+
2. **User Authorization** - User visits the verification URL and enters the user code
|
|
153
|
+
3. **Token Polling** - Plugin polls for the access token until user authorizes
|
|
154
|
+
4. **Token Storage** - Tokens are stored in OpenCode's auth system
|
|
155
|
+
5. **Auto Refresh** - Access tokens are refreshed before expiry
|
|
156
|
+
|
|
157
|
+
## Security
|
|
158
|
+
|
|
159
|
+
- Uses PKCE (RFC 7636) for enhanced security
|
|
160
|
+
- No client secret required
|
|
161
|
+
- Tokens stored in OpenCode's secure auth storage
|
|
162
|
+
- All OAuth activity logged for auditing
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Clone and install
|
|
168
|
+
git clone https://github.com/yourusername/opencode-qwen-oauth.git
|
|
169
|
+
cd opencode-qwen-oauth
|
|
170
|
+
npm install
|
|
171
|
+
|
|
172
|
+
# Build
|
|
173
|
+
npm run build
|
|
174
|
+
|
|
175
|
+
# Watch mode
|
|
176
|
+
npm run dev
|
|
177
|
+
|
|
178
|
+
# Test locally
|
|
179
|
+
npm link
|
|
180
|
+
cd /path/to/project
|
|
181
|
+
opencode-qwen-oauth install
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Project Structure
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
opencode-qwen-oauth/
|
|
188
|
+
āāā src/ # TypeScript source files
|
|
189
|
+
ā āāā index.ts # Main plugin implementation
|
|
190
|
+
āāā bin/ # CLI scripts
|
|
191
|
+
ā āāā install.js # Installer script
|
|
192
|
+
āāā dist/ # Compiled JavaScript (generated)
|
|
193
|
+
āāā package.json # Package manifest
|
|
194
|
+
āāā tsconfig.json # TypeScript config
|
|
195
|
+
āāā LICENSE # MIT license
|
|
196
|
+
āāā README.md # This file
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Note:** `.opencode/` directory is for local testing only and is not included in the npm package.
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT
|
|
204
|
+
|
|
205
|
+
## Contributing
|
|
206
|
+
|
|
207
|
+
Contributions welcome! Please open an issue or submit a PR.
|
|
208
|
+
|
|
209
|
+
## Related
|
|
210
|
+
|
|
211
|
+
- [OpenCode Documentation](https://opencode.ai/docs)
|
|
212
|
+
- [OpenCode Plugin API](https://opencode.ai/docs/plugins)
|
|
213
|
+
- [Qwen.ai](https://chat.qwen.ai)
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Qwen OAuth Plugin Installer
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx opencode-qwen-oauth install
|
|
8
|
+
* bunx opencode-qwen-oauth install
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
|
|
15
|
+
// ============================================
|
|
16
|
+
// Helpers
|
|
17
|
+
// ============================================
|
|
18
|
+
|
|
19
|
+
function getProjectRoot() {
|
|
20
|
+
let current = process.cwd();
|
|
21
|
+
while (current !== "/") {
|
|
22
|
+
if (existsSync(join(current, ".opencode"))) {
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
const parent = join(current, "..");
|
|
26
|
+
if (parent === current) break;
|
|
27
|
+
current = parent;
|
|
28
|
+
}
|
|
29
|
+
return process.cwd();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getOpencodeDir() {
|
|
33
|
+
return join(getProjectRoot(), ".opencode");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getOpencodeConfigPath() {
|
|
37
|
+
return join(getOpencodeDir(), "opencode.json");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ensureDir(dir) {
|
|
41
|
+
if (!existsSync(dir)) {
|
|
42
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function log(message) {
|
|
47
|
+
console.log(`[opencode-qwen-oauth] ${message}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function error(message) {
|
|
51
|
+
console.error(`[opencode-qwen-oauth] ERROR: ${message}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================
|
|
55
|
+
// Install
|
|
56
|
+
// ============================================
|
|
57
|
+
|
|
58
|
+
function install() {
|
|
59
|
+
log("Installing Qwen OAuth plugin...");
|
|
60
|
+
|
|
61
|
+
const opencodeDir = getOpencodeDir();
|
|
62
|
+
const configPath = getOpencodeConfigPath();
|
|
63
|
+
|
|
64
|
+
ensureDir(opencodeDir);
|
|
65
|
+
|
|
66
|
+
// Read or create config
|
|
67
|
+
let config = { "$schema": "https://opencode.ai/config.json", plugin: [], provider: {} };
|
|
68
|
+
|
|
69
|
+
if (existsSync(configPath)) {
|
|
70
|
+
try {
|
|
71
|
+
const content = readFileSync(configPath, "utf-8");
|
|
72
|
+
config = JSON.parse(content);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
error(`Failed to parse ${configPath}: ${e.message}`);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add plugin
|
|
80
|
+
config.plugin = config.plugin || [];
|
|
81
|
+
if (!config.plugin.includes("opencode-qwen-oauth")) {
|
|
82
|
+
config.plugin.push("opencode-qwen-oauth");
|
|
83
|
+
log("Added 'opencode-qwen-oauth' to plugins");
|
|
84
|
+
} else {
|
|
85
|
+
log("Plugin 'opencode-qwen-oauth' already in config");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add provider config
|
|
89
|
+
config.provider = config.provider || {};
|
|
90
|
+
if (!config.provider.qwen) {
|
|
91
|
+
config.provider.qwen = {
|
|
92
|
+
npm: "@ai-sdk/openai-compatible",
|
|
93
|
+
name: "Qwen Code",
|
|
94
|
+
options: {
|
|
95
|
+
baseURL: "https://portal.qwen.ai/v1",
|
|
96
|
+
},
|
|
97
|
+
models: {
|
|
98
|
+
"qwen3-coder-plus": {
|
|
99
|
+
id: "qwen3-coder-plus",
|
|
100
|
+
name: "Qwen3 Coder Plus",
|
|
101
|
+
},
|
|
102
|
+
"qwen3-vl-plus": {
|
|
103
|
+
id: "qwen3-vl-plus",
|
|
104
|
+
name: "Qwen3 VL Plus",
|
|
105
|
+
attachment: true,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
log("Added 'qwen' provider configuration");
|
|
110
|
+
} else {
|
|
111
|
+
log("Provider 'qwen' already configured");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Write config
|
|
115
|
+
try {
|
|
116
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
117
|
+
log(`Configuration written to ${configPath}`);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
error(`Failed to write config: ${e.message}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Install npm package in .opencode
|
|
124
|
+
const opencodePackagePath = join(opencodeDir, "package.json");
|
|
125
|
+
let opencodePackage = { dependencies: {} };
|
|
126
|
+
|
|
127
|
+
if (existsSync(opencodePackagePath)) {
|
|
128
|
+
try {
|
|
129
|
+
opencodePackage = JSON.parse(readFileSync(opencodePackagePath, "utf-8"));
|
|
130
|
+
} catch {
|
|
131
|
+
// Ignore
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
opencodePackage.dependencies = opencodePackage.dependencies || {};
|
|
136
|
+
if (!opencodePackage.dependencies["opencode-qwen-oauth"]) {
|
|
137
|
+
opencodePackage.dependencies["opencode-qwen-oauth"] = "^1.0.0";
|
|
138
|
+
log("Added 'opencode-qwen-oauth' to .opencode/package.json dependencies");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
writeFileSync(opencodePackagePath, JSON.stringify(opencodePackage, null, 2) + "\n", "utf-8");
|
|
143
|
+
} catch (e) {
|
|
144
|
+
error(`Failed to write package.json: ${e.message}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
log("\nā
Installation complete!");
|
|
149
|
+
log("\nNext steps:");
|
|
150
|
+
log(" 1. Run: opencode");
|
|
151
|
+
log(" 2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')");
|
|
152
|
+
log(" 3. Use model: /model qwen/qwen3-coder-plus");
|
|
153
|
+
log("\nDebug mode: QWEN_OAUTH_DEBUG=true opencode");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ============================================
|
|
157
|
+
// Uninstall
|
|
158
|
+
// ============================================
|
|
159
|
+
|
|
160
|
+
function uninstall() {
|
|
161
|
+
log("Uninstalling Qwen OAuth plugin...");
|
|
162
|
+
|
|
163
|
+
const configPath = getOpencodeConfigPath();
|
|
164
|
+
|
|
165
|
+
if (!existsSync(configPath)) {
|
|
166
|
+
log("No config file found, nothing to uninstall");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let config;
|
|
171
|
+
try {
|
|
172
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
173
|
+
} catch (e) {
|
|
174
|
+
error(`Failed to parse config: ${e.message}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Remove plugin
|
|
179
|
+
if (config.plugin) {
|
|
180
|
+
const index = config.plugin.indexOf("opencode-qwen-oauth");
|
|
181
|
+
if (index !== -1) {
|
|
182
|
+
config.plugin.splice(index, 1);
|
|
183
|
+
log("Removed 'opencode-qwen-oauth' from plugins");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Remove provider
|
|
188
|
+
if (config.provider && config.provider.qwen) {
|
|
189
|
+
delete config.provider.qwen;
|
|
190
|
+
log("Removed 'qwen' provider configuration");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
195
|
+
log(`Configuration updated: ${configPath}`);
|
|
196
|
+
} catch (e) {
|
|
197
|
+
error(`Failed to write config: ${e.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
log("\nā
Uninstallation complete!");
|
|
202
|
+
log("Note: You may want to manually remove the npm package:");
|
|
203
|
+
log(" npm uninstall opencode-qwen-auth");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ============================================
|
|
207
|
+
// Main
|
|
208
|
+
// ============================================
|
|
209
|
+
|
|
210
|
+
const command = process.argv[2];
|
|
211
|
+
|
|
212
|
+
if (command === "install" || !command) {
|
|
213
|
+
install();
|
|
214
|
+
} else if (command === "uninstall") {
|
|
215
|
+
uninstall();
|
|
216
|
+
} else if (command === "--help" || command === "-h") {
|
|
217
|
+
console.log(`
|
|
218
|
+
opencode-qwen-oauth - Qwen OAuth Plugin for OpenCode
|
|
219
|
+
|
|
220
|
+
Usage:
|
|
221
|
+
npx opencode-qwen-oauth install Install the plugin (default)
|
|
222
|
+
npx opencode-qwen-oauth uninstall Remove the plugin
|
|
223
|
+
npx opencode-qwen-oauth --help Show this help
|
|
224
|
+
|
|
225
|
+
After installation:
|
|
226
|
+
1. Run: opencode
|
|
227
|
+
2. Connect: /connect (select 'Qwen Code (qwen.ai OAuth)')
|
|
228
|
+
3. Use model: /model qwen/qwen3-coder-plus
|
|
229
|
+
|
|
230
|
+
Debug mode: QWEN_OAUTH_DEBUG=true opencode
|
|
231
|
+
`);
|
|
232
|
+
} else {
|
|
233
|
+
error(`Unknown command: ${command}`);
|
|
234
|
+
console.log("Run 'npx opencode-qwen-auth --help' for usage");
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen OAuth Plugin for OpenCode
|
|
3
|
+
* Provides OAuth device flow authentication for Qwen.ai
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
8
|
+
export declare const QwenOAuthPlugin: Plugin;
|
|
9
|
+
export default QwenOAuthPlugin;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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;AAkP/D,eAAO,MAAM,eAAe,EAAE,MAuF7B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen OAuth Plugin for OpenCode
|
|
3
|
+
* Provides OAuth device flow authentication for Qwen.ai
|
|
4
|
+
*
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*/
|
|
7
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { appendFileSync, mkdirSync, existsSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
// ============================================
|
|
13
|
+
// Constants
|
|
14
|
+
// ============================================
|
|
15
|
+
const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
|
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",
|
|
102
|
+
});
|
|
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
|
+
return {
|
|
177
|
+
auth: {
|
|
178
|
+
provider: "qwen",
|
|
179
|
+
methods: [
|
|
180
|
+
{
|
|
181
|
+
type: "oauth",
|
|
182
|
+
label: "Qwen Code (qwen.ai OAuth)",
|
|
183
|
+
authorize: async () => {
|
|
184
|
+
debugLog("Starting Qwen OAuth device flow...");
|
|
185
|
+
const device = await authorizeDevice();
|
|
186
|
+
const url = device.verification_uri_complete || device.verification_uri;
|
|
187
|
+
// Try to open browser automatically
|
|
188
|
+
openBrowser(url);
|
|
189
|
+
debugLog("Device authorization received", {
|
|
190
|
+
user_code: device.user_code,
|
|
191
|
+
verification_uri: device.verification_uri,
|
|
192
|
+
expires_in: device.expires_in,
|
|
193
|
+
interval: device.interval,
|
|
194
|
+
});
|
|
195
|
+
// Show essential info to user
|
|
196
|
+
console.log(`\nš± Qwen OAuth: Visit ${url}`);
|
|
197
|
+
console.log(`š Enter code: ${device.user_code}\n`);
|
|
198
|
+
return {
|
|
199
|
+
url,
|
|
200
|
+
instructions: `Enter code: ${device.user_code}`,
|
|
201
|
+
method: "auto",
|
|
202
|
+
callback: async () => {
|
|
203
|
+
debugLog("Polling for OAuth token...");
|
|
204
|
+
const result = await pollForToken(device.device_code, device.verifier, device.interval, device.expires_in);
|
|
205
|
+
if (result.success) {
|
|
206
|
+
debugLog("Qwen authentication successful!", {
|
|
207
|
+
expires_in: result.expires_in,
|
|
208
|
+
has_refresh: !!result.refresh_token,
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
type: "success",
|
|
212
|
+
access: result.access_token,
|
|
213
|
+
refresh: result.refresh_token,
|
|
214
|
+
expires: Date.now() + result.expires_in * 1000,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
debugLog(`Authentication failed: ${result.error}`);
|
|
218
|
+
return { type: "failed", error: result.error };
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
config: async (config) => {
|
|
226
|
+
const providers = config.provider || {};
|
|
227
|
+
config.provider = providers;
|
|
228
|
+
providers["qwen"] = {
|
|
229
|
+
npm: "@ai-sdk/openai-compatible",
|
|
230
|
+
name: "Qwen Code",
|
|
231
|
+
options: {
|
|
232
|
+
baseURL: QWEN_API_BASE_URL,
|
|
233
|
+
},
|
|
234
|
+
models: {
|
|
235
|
+
"qwen3-coder-plus": {
|
|
236
|
+
id: "qwen3-coder-plus",
|
|
237
|
+
name: "Qwen3 Coder Plus",
|
|
238
|
+
},
|
|
239
|
+
"qwen3-vl-plus": {
|
|
240
|
+
id: "qwen3-vl-plus",
|
|
241
|
+
name: "Qwen3 VL Plus",
|
|
242
|
+
attachment: true,
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
export default QwenOAuthPlugin;
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-qwen-oauth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Qwen OAuth authentication plugin for OpenCode - authenticate with Qwen.ai using OAuth device flow",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"opencode-qwen-oauth": "./bin/install.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"opencode",
|
|
25
|
+
"opencode-plugin",
|
|
26
|
+
"qwen",
|
|
27
|
+
"oauth",
|
|
28
|
+
"authentication",
|
|
29
|
+
"ai",
|
|
30
|
+
"llm"
|
|
31
|
+
],
|
|
32
|
+
"author": "Your Name <your.email@example.com>",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/yourusername/opencode-qwen-oauth.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/yourusername/opencode-qwen-oauth/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/yourusername/opencode-qwen-oauth#readme",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@opencode-ai/plugin": "^1.2.15"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.10.0",
|
|
47
|
+
"typescript": "^5.3.0"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@ai-sdk/openai-compatible": "^0.0.1"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"@ai-sdk/openai-compatible": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|