@hotmanxp/opencode-qwen-login-plugin 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 +334 -0
- package/dist/auth.d.ts +41 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +112 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +24 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-merge.test.js +188 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +14 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +83 -0
- package/dist/plugin.js.map +1 -0
- package/dist/qwen-config.d.ts +45 -0
- package/dist/qwen-config.d.ts.map +1 -0
- package/dist/qwen-config.js +50 -0
- package/dist/qwen-config.js.map +1 -0
- package/dist/qwen-oauth.d.ts +100 -0
- package/dist/qwen-oauth.d.ts.map +1 -0
- package/dist/qwen-oauth.js +245 -0
- package/dist/qwen-oauth.js.map +1 -0
- package/dist/qwen-oauth.test.js +123 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qwen Login Plugin
|
|
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,334 @@
|
|
|
1
|
+
# Qwen Login Plugin
|
|
2
|
+
|
|
3
|
+
通义千问认证插件 - **自动从 qwen-code OAuth 配置 opencode**
|
|
4
|
+
|
|
5
|
+
## ✨ 功能特性
|
|
6
|
+
|
|
7
|
+
- 🔐 **自动导入 OAuth 配置**
|
|
8
|
+
- 自动读取 `~/.qwen/oauth_creds.json`
|
|
9
|
+
- 使用 `resource_url` 构建正确的 endpoint (`https://portal.qwen.ai/v1`)
|
|
10
|
+
- 自动配置完整的请求头(User-Agent、x-dashscope-* 等)
|
|
11
|
+
- 将 qwen-code 的 OAuth token 配置到 opencode
|
|
12
|
+
- 无需手动输入 API Key
|
|
13
|
+
|
|
14
|
+
- ⚙️ **一键配置**
|
|
15
|
+
- CLI 工具一键完成配置
|
|
16
|
+
- 插件加载时自动配置
|
|
17
|
+
- 支持手动认证作为备选
|
|
18
|
+
|
|
19
|
+
- 🔒 **安全可靠**
|
|
20
|
+
- 使用 qwen-code 的 OAuth token
|
|
21
|
+
- Token 过期检测
|
|
22
|
+
- 敏感信息不硬编码
|
|
23
|
+
|
|
24
|
+
- 📝 **完整的请求头**
|
|
25
|
+
- `User-Agent`: QwenCode/unknown (darwin; arm64)
|
|
26
|
+
- `x-dashscope-authtype`: qwen-oauth
|
|
27
|
+
- `x-stainless-*`: Stainless SDK 标识
|
|
28
|
+
- `authorization`: Bearer token
|
|
29
|
+
|
|
30
|
+
## 📦 安装
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd /Users/ethan/code/planning-with-files/extensions/qwen-login-plugin
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 🚀 使用方法
|
|
39
|
+
|
|
40
|
+
### 方法 1: CLI 工具(推荐)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 运行配置工具
|
|
44
|
+
npm start
|
|
45
|
+
|
|
46
|
+
# 或直接使用
|
|
47
|
+
node dist/cli.js
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
输出示例:
|
|
51
|
+
```
|
|
52
|
+
🔐 Qwen Login Plugin - 配置工具
|
|
53
|
+
|
|
54
|
+
正在从 qwen-code 导入认证信息...
|
|
55
|
+
|
|
56
|
+
Qwen API configuration saved to opencode.json
|
|
57
|
+
Successfully configured opencode with Qwen OAuth credentials
|
|
58
|
+
|
|
59
|
+
✅ 配置成功!
|
|
60
|
+
|
|
61
|
+
现在可以在 opencode 中使用 Qwen 模型:
|
|
62
|
+
opencode --model qwen/qwen-plus
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 方法 2: 作为 opencode 插件
|
|
66
|
+
|
|
67
|
+
在 `opencode.json` 中添加:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"plugin": [
|
|
72
|
+
"file:///Users/ethan/code/planning-with-files/extensions/qwen-login-plugin/dist/index.js"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
插件加载时会自动从 qwen-code 导入配置。
|
|
78
|
+
|
|
79
|
+
### 方法 3: 直接使用配置
|
|
80
|
+
|
|
81
|
+
运行 CLI 后,配置已写入 `~/.config/opencode/opencode.json`:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"provider": {
|
|
86
|
+
"qwen": {
|
|
87
|
+
"options": {
|
|
88
|
+
"apiKey": "Bearer <token>",
|
|
89
|
+
"baseURL": "https://portal.qwen.ai/v1"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"model": "qwen/qwen-plus"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
直接启动 opencode 即可使用:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
opencode --model qwen/qwen-plus
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 📝 工作原理
|
|
104
|
+
|
|
105
|
+
### 1. 读取 qwen-code OAuth 凭证
|
|
106
|
+
|
|
107
|
+
从 `~/.qwen/oauth_creds.json` 读取:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"access_token": "Ti3s_MI-n-HJSGEgEmgDy2qFHwnwbwK-...",
|
|
112
|
+
"token_type": "Bearer",
|
|
113
|
+
"refresh_token": "6aLiqAwXh-c10x9EetHuD_3cbxyOzA2d...",
|
|
114
|
+
"resource_url": "portal.qwen.ai",
|
|
115
|
+
"expiry_date": 1771852368630
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 2. 生成 API 配置
|
|
120
|
+
|
|
121
|
+
根据 `resource_url` 构建正确的 endpoint,并生成完整的请求头:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
{
|
|
125
|
+
"apiKey": "Bearer <access_token>",
|
|
126
|
+
"baseURL": "https://portal.qwen.ai/v1",
|
|
127
|
+
"headers": {
|
|
128
|
+
"accept": "application/json",
|
|
129
|
+
"content-type": "application/json",
|
|
130
|
+
"user-agent": "QwenCode/unknown (darwin; arm64)",
|
|
131
|
+
"x-stainless-lang": "js",
|
|
132
|
+
"x-stainless-package-version": "4.104.0",
|
|
133
|
+
"x-stainless-os": "MacOS",
|
|
134
|
+
"x-stainless-arch": "arm64",
|
|
135
|
+
"x-stainless-runtime": "node",
|
|
136
|
+
"x-stainless-runtime-version": "24.11.1",
|
|
137
|
+
"authorization": "Bearer <token>",
|
|
138
|
+
"x-dashscope-cachecontrol": "enable",
|
|
139
|
+
"x-dashscope-useragent": "QwenCode/unknown (darwin; arm64)",
|
|
140
|
+
"x-dashscope-authtype": "qwen-oauth",
|
|
141
|
+
"x-stainless-retry-count": "0",
|
|
142
|
+
"x-stainless-timeout": "120"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 3. 保存到 opencode 配置
|
|
148
|
+
|
|
149
|
+
写入 `~/Library/Application Support/opencode/opencode.json`
|
|
150
|
+
|
|
151
|
+
### 4. Token 过期检测
|
|
152
|
+
|
|
153
|
+
自动检查 `expiry_date`,过期则提示重新认证
|
|
154
|
+
|
|
155
|
+
## 🔧 开发模式
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# 监听编译
|
|
159
|
+
npm run dev
|
|
160
|
+
|
|
161
|
+
# 清理构建文件
|
|
162
|
+
npm run clean
|
|
163
|
+
|
|
164
|
+
# 重新构建
|
|
165
|
+
npm run build
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 📁 项目结构
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
qwen-login-plugin/
|
|
172
|
+
├── src/
|
|
173
|
+
│ ├── index.ts # 插件入口
|
|
174
|
+
│ ├── cli.ts # CLI 工具
|
|
175
|
+
│ └── qwen-oauth.ts # OAuth 配置工具
|
|
176
|
+
├── dist/ # 构建输出
|
|
177
|
+
├── package.json
|
|
178
|
+
├── tsconfig.json
|
|
179
|
+
├── README.md
|
|
180
|
+
└── LICENSE
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 🛠️ API
|
|
184
|
+
|
|
185
|
+
### 从代码中调用
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import {
|
|
189
|
+
configureOpencodeFromQwenOAuth,
|
|
190
|
+
readOAuthCredentials,
|
|
191
|
+
getQwenConfigFromOAuth,
|
|
192
|
+
buildBaseUrl
|
|
193
|
+
} from 'qwen-login-plugin'
|
|
194
|
+
|
|
195
|
+
// 一键配置
|
|
196
|
+
const success = await configureOpencodeFromQwenOAuth()
|
|
197
|
+
|
|
198
|
+
// 或分步调用
|
|
199
|
+
const creds = await readOAuthCredentials()
|
|
200
|
+
const config = await getQwenConfigFromOAuth()
|
|
201
|
+
|
|
202
|
+
// 使用 resource_url 构建 endpoint
|
|
203
|
+
const endpoint = buildBaseUrl(creds.resource_url)
|
|
204
|
+
// => "https://portal.qwen.ai/v1"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 可用函数
|
|
208
|
+
|
|
209
|
+
| 函数 | 说明 |
|
|
210
|
+
|------|------|
|
|
211
|
+
| `readOAuthCredentials()` | 读取 OAuth 凭证 |
|
|
212
|
+
| `isTokenValid(creds)` | 检查 token 是否过期 |
|
|
213
|
+
| `getApiConfigFromOAuth(creds)` | 从 OAuth 生成 API 配置 |
|
|
214
|
+
| `getQwenConfigFromOAuth()` | 获取 Qwen API 配置 |
|
|
215
|
+
| `saveToOpencodeConfig(config)` | 保存到 opencode.json |
|
|
216
|
+
| `configureOpencodeFromQwenOAuth()` | 一键配置 |
|
|
217
|
+
| `buildBaseUrl(resourceUrl)` | 从 resource_url 构建 endpoint |
|
|
218
|
+
|
|
219
|
+
## 📋 配置说明
|
|
220
|
+
|
|
221
|
+
### OAuth 凭证字段
|
|
222
|
+
|
|
223
|
+
| 字段 | 说明 | 示例 |
|
|
224
|
+
|------|------|------|
|
|
225
|
+
| `access_token` | OAuth access token | `Ti3s_MI-n-HJSGEgEmgDy2qFHwnwbwK-...` |
|
|
226
|
+
| `token_type` | Token 类型 | `Bearer` |
|
|
227
|
+
| `refresh_token` | 刷新 token | `6aLiqAwXh-c10x9EetHuD_3cbxyOzA2d...` |
|
|
228
|
+
| `resource_url` | API 资源地址 | `portal.qwen.ai` |
|
|
229
|
+
| `expiry_date` | 过期时间戳(毫秒) | `1771852368630` |
|
|
230
|
+
|
|
231
|
+
### 生成的 API 配置
|
|
232
|
+
|
|
233
|
+
| 字段 | 说明 | 值 |
|
|
234
|
+
|------|------|------|
|
|
235
|
+
| `provider.qwen` | Qwen provider 配置 | 包含 apiKey、baseURL、headers |
|
|
236
|
+
| `models` | 可用模型列表 | `coder-model`, `qwen-plus` 等 |
|
|
237
|
+
|
|
238
|
+
**注意**: 插件不会设置默认 model,需要在 `opencode.json` 中手动配置:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"model": "qwen/coder-model"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
可用模型:
|
|
247
|
+
- `qwen/coder-model` - 编程专用模型
|
|
248
|
+
- `qwen/qwen-plus` - 平衡性能和成本
|
|
249
|
+
|
|
250
|
+
#### Headers 详情
|
|
251
|
+
|
|
252
|
+
| Header | 值 | 说明 |
|
|
253
|
+
|--------|-----|------|
|
|
254
|
+
| `user-agent` | `QwenCode/unknown (darwin; arm64)` | 客户端标识 |
|
|
255
|
+
| `x-dashscope-authtype` | `qwen-oauth` | 认证类型 |
|
|
256
|
+
| `x-dashscope-useragent` | `QwenCode/unknown (darwin; arm64)` | DashScope 客户端标识 |
|
|
257
|
+
| `x-dashscope-cachecontrol` | `enable` | 启用缓存控制 |
|
|
258
|
+
| `x-stainless-*` | various | Stainless SDK 标识 |
|
|
259
|
+
| `authorization` | `Bearer <token>` | OAuth token |
|
|
260
|
+
|
|
261
|
+
### 最终 API 调用
|
|
262
|
+
|
|
263
|
+
opencode 会使用以下 endpoint 和请求头调用 Qwen API:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
POST https://portal.qwen.ai/v1/chat/completions
|
|
267
|
+
Headers:
|
|
268
|
+
user-agent: QwenCode/unknown (darwin; arm64)
|
|
269
|
+
x-dashscope-authtype: qwen-oauth
|
|
270
|
+
x-dashscope-useragent: QwenCode/unknown (darwin; arm64)
|
|
271
|
+
x-dashscope-cachecontrol: enable
|
|
272
|
+
authorization: Bearer <access_token>
|
|
273
|
+
x-stainless-lang: js
|
|
274
|
+
x-stainless-package-version: 4.104.0
|
|
275
|
+
x-stainless-os: MacOS
|
|
276
|
+
x-stainless-arch: arm64
|
|
277
|
+
x-stainless-runtime: node
|
|
278
|
+
x-stainless-runtime-version: 24.11.1
|
|
279
|
+
x-stainless-retry-count: 0
|
|
280
|
+
x-stainless-timeout: 120
|
|
281
|
+
accept: application/json
|
|
282
|
+
content-type: application/json
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## ⚠️ 故障排查
|
|
286
|
+
|
|
287
|
+
### 1. "No Qwen OAuth credentials found"
|
|
288
|
+
|
|
289
|
+
**原因**: `~/.qwen/oauth_creds.json` 文件不存在
|
|
290
|
+
|
|
291
|
+
**解决**: 先使用 qwen-code 进行认证:
|
|
292
|
+
```bash
|
|
293
|
+
qwen login
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### 2. "Qwen OAuth token has expired"
|
|
297
|
+
|
|
298
|
+
**原因**: OAuth token 已过期
|
|
299
|
+
|
|
300
|
+
**解决**: 重新认证:
|
|
301
|
+
```bash
|
|
302
|
+
qwen login
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 3. 配置未生效
|
|
306
|
+
|
|
307
|
+
**检查**:
|
|
308
|
+
1. 确认 `~/Library/Application Support/opencode/opencode.json` 存在
|
|
309
|
+
2. 确认配置格式正确
|
|
310
|
+
3. 重启 opencode
|
|
311
|
+
|
|
312
|
+
### 4. API 调用失败
|
|
313
|
+
|
|
314
|
+
**检查**:
|
|
315
|
+
1. 确认 `baseURL` 是 `https://portal.qwen.ai/v1`
|
|
316
|
+
2. 确认 `apiKey` 格式为 `Bearer <token>`
|
|
317
|
+
3. 检查网络连接
|
|
318
|
+
|
|
319
|
+
## 📋 前置要求
|
|
320
|
+
|
|
321
|
+
- Node.js 20+
|
|
322
|
+
- 已安装并认证 qwen-code
|
|
323
|
+
- opencode 已安装
|
|
324
|
+
|
|
325
|
+
## 📄 许可证
|
|
326
|
+
|
|
327
|
+
MIT
|
|
328
|
+
|
|
329
|
+
## 🔗 相关链接
|
|
330
|
+
|
|
331
|
+
- [opencode 文档](https://opencode.ai/docs)
|
|
332
|
+
- [通义千问 API](https://help.aliyun.com/zh/dashscope/)
|
|
333
|
+
- [qwen-code](https://github.com/QwenLM/qwen-code)
|
|
334
|
+
- [Qwen Portal](https://portal.qwen.ai)
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 认证管理工具
|
|
3
|
+
*
|
|
4
|
+
* 使用 opencode 的 auth 系统存储和管理敏感认证信息
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 认证信息接口
|
|
8
|
+
*/
|
|
9
|
+
export interface AuthInfo {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
baseURL: string;
|
|
12
|
+
provider: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 保存认证信息
|
|
16
|
+
*
|
|
17
|
+
* @param auth - 认证信息
|
|
18
|
+
*/
|
|
19
|
+
export declare function saveAuth(auth: AuthInfo): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* 获取认证信息
|
|
22
|
+
*
|
|
23
|
+
* @param key - 认证信息键 (apiKey | baseURL)
|
|
24
|
+
* @returns 认证信息值,如果不存在则返回 undefined
|
|
25
|
+
*/
|
|
26
|
+
export declare function getAuth(key: "apiKey" | "baseURL"): Promise<string | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* 验证 API Key
|
|
29
|
+
*
|
|
30
|
+
* 通过简单的格式检查和可选的 API 调用来验证 API Key 的有效性
|
|
31
|
+
*
|
|
32
|
+
* @param apiKey - API Key
|
|
33
|
+
* @param baseURL - Base URL
|
|
34
|
+
* @returns boolean - 是否有效
|
|
35
|
+
*/
|
|
36
|
+
export declare function validateApiKey(apiKey: string, baseURL: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* 清除认证信息
|
|
39
|
+
*/
|
|
40
|
+
export declare function clearAuth(): Promise<void>;
|
|
41
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB;AAuBD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5D;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAUpF;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAwBlB;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAS/C"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 认证管理工具
|
|
3
|
+
*
|
|
4
|
+
* 使用 opencode 的 auth 系统存储和管理敏感认证信息
|
|
5
|
+
*/
|
|
6
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
/**
|
|
10
|
+
* 获取 opencode 配置目录
|
|
11
|
+
*/
|
|
12
|
+
function getOpencodeConfigDir() {
|
|
13
|
+
switch (process.platform) {
|
|
14
|
+
case "darwin":
|
|
15
|
+
return join(homedir(), "Library", "Application Support", "opencode");
|
|
16
|
+
case "win32":
|
|
17
|
+
return join(process.env.APPDATA || "", "opencode");
|
|
18
|
+
default:
|
|
19
|
+
return join(homedir(), ".config", "opencode");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 获取认证信息存储文件路径
|
|
24
|
+
*/
|
|
25
|
+
function getAuthFilePath() {
|
|
26
|
+
return join(getOpencodeConfigDir(), "qwen-auth.json");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 保存认证信息
|
|
30
|
+
*
|
|
31
|
+
* @param auth - 认证信息
|
|
32
|
+
*/
|
|
33
|
+
export async function saveAuth(auth) {
|
|
34
|
+
try {
|
|
35
|
+
const configDir = getOpencodeConfigDir();
|
|
36
|
+
const authFile = getAuthFilePath();
|
|
37
|
+
// 确保目录存在
|
|
38
|
+
await mkdir(configDir, { recursive: true });
|
|
39
|
+
// 保存认证信息
|
|
40
|
+
await writeFile(authFile, JSON.stringify(auth, null, 2), "utf-8");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error("Failed to save Qwen auth:", error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 获取认证信息
|
|
49
|
+
*
|
|
50
|
+
* @param key - 认证信息键 (apiKey | baseURL)
|
|
51
|
+
* @returns 认证信息值,如果不存在则返回 undefined
|
|
52
|
+
*/
|
|
53
|
+
export async function getAuth(key) {
|
|
54
|
+
try {
|
|
55
|
+
const authFile = getAuthFilePath();
|
|
56
|
+
const content = await readFile(authFile, "utf-8");
|
|
57
|
+
const auth = JSON.parse(content);
|
|
58
|
+
return auth[key];
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
// 文件不存在或读取失败,返回 undefined
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 验证 API Key
|
|
67
|
+
*
|
|
68
|
+
* 通过简单的格式检查和可选的 API 调用来验证 API Key 的有效性
|
|
69
|
+
*
|
|
70
|
+
* @param apiKey - API Key
|
|
71
|
+
* @param baseURL - Base URL
|
|
72
|
+
* @returns boolean - 是否有效
|
|
73
|
+
*/
|
|
74
|
+
export async function validateApiKey(apiKey, baseURL) {
|
|
75
|
+
// 基本格式验证
|
|
76
|
+
if (!apiKey || !apiKey.startsWith("sk-")) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
// 可选:通过 API 调用验证
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(`${baseURL}/models`, {
|
|
82
|
+
method: "GET",
|
|
83
|
+
headers: {
|
|
84
|
+
Authorization: `Bearer ${apiKey}`,
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// 200 表示 API Key 有效
|
|
89
|
+
return response.ok;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// 网络错误或其他问题,不进行严格验证
|
|
93
|
+
// 允许用户继续配置,真正的验证会在实际调用时进行
|
|
94
|
+
console.warn("API Key validation request failed, skipping validation:", error);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 清除认证信息
|
|
100
|
+
*/
|
|
101
|
+
export async function clearAuth() {
|
|
102
|
+
try {
|
|
103
|
+
const { rm } = await import("node:fs/promises");
|
|
104
|
+
const authFile = getAuthFilePath();
|
|
105
|
+
await rm(authFile, { force: true });
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error("Failed to clear Qwen auth:", error);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Qwen Login Plugin - CLI 工具
|
|
4
|
+
*
|
|
5
|
+
* 从 qwen-code OAuth 配置 opencode
|
|
6
|
+
*
|
|
7
|
+
* 使用方法:
|
|
8
|
+
* node dist/cli.js
|
|
9
|
+
*/
|
|
10
|
+
import { configureOpencodeFromQwenOAuth } from "./qwen-oauth.js";
|
|
11
|
+
configureOpencodeFromQwenOAuth()
|
|
12
|
+
.then((success) => {
|
|
13
|
+
if (success) {
|
|
14
|
+
console.log("✅ 配置成功!现在可以在 opencode 中使用 Qwen 模型");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.log("❌ 配置失败,请检查是否已使用 qwen-code 认证");
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
.catch((error) => {
|
|
21
|
+
console.error("❌ 发生错误:", error.message);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAEhE,8BAA8B,EAAE;KAC7B,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;IAChB,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAA;IAClD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACf,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|