@iamtoricool/opencool-qwen-auth 0.0.1-alpha
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 +125 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +238 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# OpenCool Qwen Auth Plugin
|
|
2
|
+
|
|
3
|
+
Qwen AI OAuth Authentication Plugin for [opencode](https://opencode.ai) - Access Qwen models via your chat.qwen.ai subscription.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin enables opencode to use Qwen AI's models (Qwen3 Max, Qwen3 Coder, etc.) via OAuth authentication with Qwen's chat platform. Instead of using API keys, you can authenticate using your existing Qwen Chat subscription.
|
|
8
|
+
|
|
9
|
+
## Supported Models
|
|
10
|
+
|
|
11
|
+
- **qwen3-max** - Most capable Qwen3 model for complex tasks
|
|
12
|
+
- **qwen3-coder-plus** - Optimized for code generation and programming
|
|
13
|
+
- **qwen3-vl-plus** - Vision language model for image understanding
|
|
14
|
+
- **qwen3-235b-a22b** - Large mixture-of-experts model
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @iamtoricool/opencool-qwen-auth
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
Add to your `~/.opencode/opencode.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"plugin": ["@iamtoricool/opencool-qwen-auth"],
|
|
29
|
+
"provider": {
|
|
30
|
+
"qwen": {
|
|
31
|
+
"options": {}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"model": "qwen/qwen3-max",
|
|
35
|
+
"models": {
|
|
36
|
+
"qwen3-max": {
|
|
37
|
+
"id": "qwen3-max",
|
|
38
|
+
"name": "Qwen3 Max",
|
|
39
|
+
"variants": {
|
|
40
|
+
"latest": "qwen3-max"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Authentication
|
|
48
|
+
|
|
49
|
+
Run the login command:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
opencode providers login qwen
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This will:
|
|
56
|
+
1. Open your browser to Qwen's OAuth authorization page
|
|
57
|
+
2. Start a local callback server on port 1455
|
|
58
|
+
3. Exchange the authorization code for access/refresh tokens
|
|
59
|
+
4. Store tokens securely for future use
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **OAuth 2.0 with PKCE** - Secure authentication flow
|
|
64
|
+
- **Token Refresh** - Automatic token refresh when expired
|
|
65
|
+
- **Model Configuration** - Per-model options support
|
|
66
|
+
- **Streaming Support** - Full SSE streaming for real-time responses
|
|
67
|
+
- **Fallback Options** - Manual URL paste if browser auto-open fails
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
The plugin follows the opencode plugin architecture:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
index.ts # Main plugin entry point
|
|
75
|
+
lib/
|
|
76
|
+
types.ts # TypeScript type definitions
|
|
77
|
+
constants.ts # OAuth endpoints, client IDs, models
|
|
78
|
+
config.ts # Configuration loading
|
|
79
|
+
logger.ts # Request logging
|
|
80
|
+
auth/
|
|
81
|
+
auth.ts # PKCE, token exchange, JWT decode
|
|
82
|
+
server.ts # Local OAuth callback server
|
|
83
|
+
browser.ts # Platform-specific browser opening
|
|
84
|
+
request/
|
|
85
|
+
fetch-helpers.ts # 7-step fetch orchestration
|
|
86
|
+
request-transformer.ts # Model normalization, body transforms
|
|
87
|
+
response-handler.ts # SSE parsing
|
|
88
|
+
config/
|
|
89
|
+
opencode-modern.json # Model definitions
|
|
90
|
+
test/ # Comprehensive test suite
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Development
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Install dependencies
|
|
97
|
+
npm install
|
|
98
|
+
|
|
99
|
+
# Run type check
|
|
100
|
+
npm run typecheck
|
|
101
|
+
|
|
102
|
+
# Run tests
|
|
103
|
+
npm test
|
|
104
|
+
|
|
105
|
+
# Run tests with watch mode
|
|
106
|
+
npm run test:watch
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## OAuth Configuration
|
|
110
|
+
|
|
111
|
+
The plugin uses Qwen's OAuth endpoints:
|
|
112
|
+
|
|
113
|
+
- **Authorization URL**: `https://auth.qwen.ai/oauth/authorize`
|
|
114
|
+
- **Token URL**: `https://auth.qwen.ai/oauth/token`
|
|
115
|
+
- **API Base**: `https://chat.qwen.ai/backend-api`
|
|
116
|
+
- **Redirect URI**: `http://localhost:1455/auth/callback`
|
|
117
|
+
- **Scopes**: `openid profile email offline_access`
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
Contributions are welcome! Please ensure tests pass before submitting PRs.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen AI OAuth Authentication Plugin for opencode
|
|
3
|
+
*
|
|
4
|
+
* This plugin enables opencode to use Qwen's AI models via OAuth authentication
|
|
5
|
+
* with chat.qwen.ai, allowing users to leverage their Qwen subscription.
|
|
6
|
+
*
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
10
|
+
/**
|
|
11
|
+
* Qwen AI OAuth authentication plugin for opencode
|
|
12
|
+
*
|
|
13
|
+
* This plugin enables opencode to use Qwen AI models via OAuth authentication,
|
|
14
|
+
* allowing users to leverage their Qwen Chat subscription.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```json
|
|
18
|
+
* {
|
|
19
|
+
* "plugin": ["opencool-qwen-auth"],
|
|
20
|
+
* "model": "qwen/qwen3-max"
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const QwenAuthPlugin: Plugin;
|
|
25
|
+
export default QwenAuthPlugin;
|
|
26
|
+
export type { UserConfig, PluginConfig, RequestBody, TokenResult, AuthorizationFlow, } from "./lib/types.js";
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAoC/D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,EAAE,MA+P5B,CAAC;AAEF,eAAe,cAAc,CAAC;AAG9B,YAAY,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,iBAAiB,GAClB,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen AI OAuth Authentication Plugin for opencode
|
|
3
|
+
*
|
|
4
|
+
* This plugin enables opencode to use Qwen's AI models via OAuth authentication
|
|
5
|
+
* with chat.qwen.ai, allowing users to leverage their Qwen subscription.
|
|
6
|
+
*
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
import { createAuthorizationFlow, decodeJWT, exchangeAuthorizationCode, parseAuthorizationInput, extractBrowserSession, isBrowserSessionAvailable, } from "./lib/auth/auth.js";
|
|
10
|
+
import { REDIRECT_URI } from "./lib/constants.js";
|
|
11
|
+
import { openBrowserUrl } from "./lib/auth/browser.js";
|
|
12
|
+
import { startLocalOAuthServer } from "./lib/auth/server.js";
|
|
13
|
+
import { loadPluginConfig, parseUserConfig, initLogger } from "./lib/config.js";
|
|
14
|
+
import { AUTH_LABELS, QWEN_BASE_URL, DUMMY_API_KEY, LOG_STAGES, PLUGIN_NAME, PROVIDER_ID, } from "./lib/constants.js";
|
|
15
|
+
import { logRequest, logDebug } from "./lib/logger.js";
|
|
16
|
+
import { createQwenHeaders, extractRequestUrl, handleErrorResponse, handleSuccessResponse, refreshAndUpdateToken, rewriteUrlForQwen, shouldRefreshToken, transformRequestForQwen, } from "./lib/request/fetch-helpers.js";
|
|
17
|
+
/**
|
|
18
|
+
* Qwen AI OAuth authentication plugin for opencode
|
|
19
|
+
*
|
|
20
|
+
* This plugin enables opencode to use Qwen AI models via OAuth authentication,
|
|
21
|
+
* allowing users to leverage their Qwen Chat subscription.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```json
|
|
25
|
+
* {
|
|
26
|
+
* "plugin": ["opencool-qwen-auth"],
|
|
27
|
+
* "model": "qwen/qwen3-max"
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const QwenAuthPlugin = async ({ client }) => {
|
|
32
|
+
// Initialize logger with plugin config
|
|
33
|
+
const pluginConfig = loadPluginConfig();
|
|
34
|
+
initLogger(pluginConfig);
|
|
35
|
+
/**
|
|
36
|
+
* Build manual OAuth flow for fallback when auto browser open fails
|
|
37
|
+
*/
|
|
38
|
+
const buildManualOAuthFlow = (pkce, url) => ({
|
|
39
|
+
url,
|
|
40
|
+
method: "code",
|
|
41
|
+
instructions: AUTH_LABELS.INSTRUCTIONS_MANUAL,
|
|
42
|
+
callback: async (input) => {
|
|
43
|
+
const parsed = parseAuthorizationInput(input);
|
|
44
|
+
if (!parsed.code) {
|
|
45
|
+
return { type: "failed" };
|
|
46
|
+
}
|
|
47
|
+
const tokens = await exchangeAuthorizationCode(parsed.code, pkce.verifier, REDIRECT_URI);
|
|
48
|
+
return tokens?.type === "success" ? tokens : { type: "failed" };
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
auth: {
|
|
53
|
+
provider: PROVIDER_ID,
|
|
54
|
+
/**
|
|
55
|
+
* Loader function that configures OAuth authentication and request handling
|
|
56
|
+
*
|
|
57
|
+
* This function:
|
|
58
|
+
* 1. Validates OAuth authentication
|
|
59
|
+
* 2. Extracts user info from access token
|
|
60
|
+
* 3. Loads user configuration from opencode.json
|
|
61
|
+
* 4. Returns SDK configuration with custom fetch implementation
|
|
62
|
+
*
|
|
63
|
+
* @param getAuth - Function to retrieve current auth state
|
|
64
|
+
* @param provider - Provider configuration from opencode.json
|
|
65
|
+
* @returns SDK configuration object or empty object for non-OAuth auth
|
|
66
|
+
*/
|
|
67
|
+
async loader(getAuth, provider) {
|
|
68
|
+
const auth = await getAuth();
|
|
69
|
+
// Only handle OAuth auth type, skip API key auth
|
|
70
|
+
if (auth.type !== "oauth") {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
// Decode JWT to verify it's valid
|
|
74
|
+
const decoded = decodeJWT(auth.access);
|
|
75
|
+
if (!decoded) {
|
|
76
|
+
logDebug(`[${PLUGIN_NAME}] Failed to decode access token (skipping plugin)`);
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
logDebug(`[${PLUGIN_NAME}] Authenticated as:`, decoded.email || decoded.sub);
|
|
80
|
+
// Extract user configuration (global + per-model options)
|
|
81
|
+
const userConfig = parseUserConfig(provider);
|
|
82
|
+
// Return SDK configuration
|
|
83
|
+
return {
|
|
84
|
+
apiKey: DUMMY_API_KEY,
|
|
85
|
+
baseURL: QWEN_BASE_URL,
|
|
86
|
+
/**
|
|
87
|
+
* Custom fetch implementation for Qwen API
|
|
88
|
+
*
|
|
89
|
+
* Handles:
|
|
90
|
+
* - Token refresh when expired
|
|
91
|
+
* - URL rewriting for Qwen backend
|
|
92
|
+
* - Request body transformation
|
|
93
|
+
* - OAuth header injection
|
|
94
|
+
* - SSE to JSON conversion for non-streaming requests
|
|
95
|
+
* - Error handling and logging
|
|
96
|
+
*
|
|
97
|
+
* @param input - Request URL or Request object
|
|
98
|
+
* @param init - Request options
|
|
99
|
+
* @returns Response from Qwen API
|
|
100
|
+
*/
|
|
101
|
+
async fetch(input, init) {
|
|
102
|
+
// Step 1: Check and refresh token if needed
|
|
103
|
+
let currentAuth = await getAuth();
|
|
104
|
+
if (shouldRefreshToken(currentAuth)) {
|
|
105
|
+
currentAuth = await refreshAndUpdateToken(currentAuth, client);
|
|
106
|
+
}
|
|
107
|
+
// Step 2: Extract and rewrite URL for Qwen backend
|
|
108
|
+
const originalUrl = extractRequestUrl(input);
|
|
109
|
+
const url = rewriteUrlForQwen(originalUrl);
|
|
110
|
+
// Step 3: Transform request body with user configuration
|
|
111
|
+
// Capture original stream value before transformation
|
|
112
|
+
const originalBody = init?.body
|
|
113
|
+
? JSON.parse(init.body)
|
|
114
|
+
: {};
|
|
115
|
+
const isStreaming = originalBody.stream === true;
|
|
116
|
+
const transformation = await transformRequestForQwen(init, url, userConfig);
|
|
117
|
+
const requestInit = transformation?.updatedInit ?? init;
|
|
118
|
+
// Step 4: Create headers with OAuth token
|
|
119
|
+
const accessToken = currentAuth.type === "oauth" ? currentAuth.access : "";
|
|
120
|
+
const headers = createQwenHeaders(requestInit, accessToken);
|
|
121
|
+
// Step 5: Make request to Qwen API
|
|
122
|
+
logDebug(`[${PLUGIN_NAME}] Making request to:`, url);
|
|
123
|
+
const response = await fetch(url, {
|
|
124
|
+
...requestInit,
|
|
125
|
+
headers,
|
|
126
|
+
});
|
|
127
|
+
// Step 6: Log response
|
|
128
|
+
logRequest(LOG_STAGES.RESPONSE, {
|
|
129
|
+
status: response.status,
|
|
130
|
+
ok: response.ok,
|
|
131
|
+
statusText: response.statusText,
|
|
132
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
133
|
+
});
|
|
134
|
+
// Step 7: Handle error or success response
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
return await handleErrorResponse(response);
|
|
137
|
+
}
|
|
138
|
+
return await handleSuccessResponse(response, isStreaming);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
methods: [
|
|
143
|
+
{
|
|
144
|
+
label: AUTH_LABELS.OAUTH,
|
|
145
|
+
type: "oauth",
|
|
146
|
+
/**
|
|
147
|
+
* OAuth authorization flow
|
|
148
|
+
*
|
|
149
|
+
* Steps:
|
|
150
|
+
* 1. Generate PKCE challenge and state for security
|
|
151
|
+
* 2. Start local OAuth callback server on port 1455
|
|
152
|
+
* 3. Open browser to Qwen authorization page
|
|
153
|
+
* 4. Wait for user to complete login
|
|
154
|
+
* 5. Exchange authorization code for tokens
|
|
155
|
+
*
|
|
156
|
+
* @returns Authorization flow configuration
|
|
157
|
+
*/
|
|
158
|
+
authorize: async () => {
|
|
159
|
+
const { pkce, state, url } = await createAuthorizationFlow();
|
|
160
|
+
const serverInfo = await startLocalOAuthServer({ state });
|
|
161
|
+
// Attempt to open browser automatically
|
|
162
|
+
openBrowserUrl(url);
|
|
163
|
+
if (!serverInfo.ready) {
|
|
164
|
+
serverInfo.close();
|
|
165
|
+
return buildManualOAuthFlow(pkce, url);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
url,
|
|
169
|
+
method: "auto",
|
|
170
|
+
instructions: AUTH_LABELS.INSTRUCTIONS,
|
|
171
|
+
callback: async () => {
|
|
172
|
+
const result = await serverInfo.waitForCode(state);
|
|
173
|
+
serverInfo.close();
|
|
174
|
+
if (!result) {
|
|
175
|
+
return { type: "failed" };
|
|
176
|
+
}
|
|
177
|
+
const tokens = await exchangeAuthorizationCode(result.code, pkce.verifier, REDIRECT_URI);
|
|
178
|
+
return tokens?.type === "success"
|
|
179
|
+
? tokens
|
|
180
|
+
: { type: "failed" };
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
label: AUTH_LABELS.OAUTH_MANUAL,
|
|
187
|
+
type: "oauth",
|
|
188
|
+
authorize: async () => {
|
|
189
|
+
const { pkce, url } = await createAuthorizationFlow();
|
|
190
|
+
return buildManualOAuthFlow(pkce, url);
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
label: "Use Browser Session (Firefox/Chrome)",
|
|
195
|
+
type: "oauth",
|
|
196
|
+
authorize: async () => {
|
|
197
|
+
// Try to extract session from browser
|
|
198
|
+
const token = await extractBrowserSession();
|
|
199
|
+
if (token) {
|
|
200
|
+
// Validate and exchange the token
|
|
201
|
+
const tokens = await exchangeAuthorizationCode(token);
|
|
202
|
+
if (tokens.type === "success") {
|
|
203
|
+
return {
|
|
204
|
+
url: "https://chat.qwen.ai",
|
|
205
|
+
method: "code",
|
|
206
|
+
instructions: "Browser session authenticated successfully!",
|
|
207
|
+
callback: async () => tokens,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// If browser session failed, provide manual instructions
|
|
212
|
+
const available = await isBrowserSessionAvailable();
|
|
213
|
+
if (!available) {
|
|
214
|
+
return {
|
|
215
|
+
url: "https://chat.qwen.ai",
|
|
216
|
+
method: "code",
|
|
217
|
+
instructions: "Browser session extraction requires better-sqlite3. Please install it with: npm install better-sqlite3\n\nOr use API key authentication instead.",
|
|
218
|
+
callback: async () => ({ type: "failed" }),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
url: "https://chat.qwen.ai",
|
|
223
|
+
method: "code",
|
|
224
|
+
instructions: "No Qwen session found in browser cookies. Please:\n1. Open Firefox or Chrome\n2. Go to https://chat.qwen.ai\n3. Sign in with your account\n4. Try again\n\nOr use API key authentication.",
|
|
225
|
+
callback: async () => ({ type: "failed" }),
|
|
226
|
+
};
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
label: AUTH_LABELS.API_KEY,
|
|
231
|
+
type: "api",
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
export default QwenAuthPlugin;
|
|
238
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iamtoricool/opencool-qwen-auth",
|
|
3
|
+
"version": "0.0.1-alpha",
|
|
4
|
+
"description": "Qwen AI OAuth Authentication Plugin for opencode - Access Qwen models via chat.qwen.ai subscription",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc && cp lib/oauth-success.html dist/lib/",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:e2e": "vitest run test/e2e/",
|
|
13
|
+
"test:unit": "vitest run test/*.test.ts",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"test:coverage": "vitest run --coverage"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@opencode-ai/plugin": "^1.0.150"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@opencode-ai/plugin": "^1.0.150",
|
|
22
|
+
"@opencode-ai/sdk": "^1.0.150",
|
|
23
|
+
"@types/node": "^24.6.2",
|
|
24
|
+
"@vitest/ui": "^3.2.4",
|
|
25
|
+
"msw": "^2.6.0",
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"vitest": "^3.2.4"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@openauthjs/openauth": "^0.4.3",
|
|
31
|
+
"hono": "^4.10.4"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20.0.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"opencode",
|
|
38
|
+
"plugin",
|
|
39
|
+
"qwen",
|
|
40
|
+
"oauth",
|
|
41
|
+
"ai",
|
|
42
|
+
"qwen3"
|
|
43
|
+
],
|
|
44
|
+
"author": "",
|
|
45
|
+
"license": "MIT"
|
|
46
|
+
}
|