@hotmanxp/opencode-qwen-login-plugin 1.0.0 → 1.1.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/dist/cli.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Qwen Login Plugin - CLI 工具
4
4
  *
5
- * 从 qwen-code OAuth 配置 opencode
5
+ * 从 qwen-code OAuth 配置 opencode 和 .gemini/settings.json
6
6
  *
7
7
  * 使用方法:
8
8
  * node dist/cli.js
package/dist/cli.js CHANGED
@@ -2,16 +2,23 @@
2
2
  /**
3
3
  * Qwen Login Plugin - CLI 工具
4
4
  *
5
- * 从 qwen-code OAuth 配置 opencode
5
+ * 从 qwen-code OAuth 配置 opencode 和 .gemini/settings.json
6
6
  *
7
7
  * 使用方法:
8
8
  * node dist/cli.js
9
9
  */
10
10
  import { configureOpencodeFromQwenOAuth } from "./qwen-oauth.js";
11
+ console.log("🔐 Qwen Login Plugin - 配置工具\n");
12
+ console.log("正在从 qwen-code 导入认证信息...\n");
11
13
  configureOpencodeFromQwenOAuth()
12
14
  .then((success) => {
13
15
  if (success) {
14
- console.log("✅ 配置成功!现在可以在 opencode 中使用 Qwen 模型");
16
+ console.log("✅ 配置成功!");
17
+ console.log("\n已写入以下配置文件:");
18
+ console.log(" - opencode.json");
19
+ console.log(" - .gemini/settings.json");
20
+ console.log("\n现在可以在 opencode 和 Gemini CLI 中使用 Qwen 模型:");
21
+ console.log(" opencode --model qwen/qwen-plus");
15
22
  }
16
23
  else {
17
24
  console.log("❌ 配置失败,请检查是否已使用 qwen-code 认证");
package/dist/cli.js.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAEH,OAAO,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAA;AAEhE,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;AAC5C,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AAExC,8BAA8B,EAAE;KAC7B,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;IAChB,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACtB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAC3B,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;QAChC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;QACzD,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"}
@@ -92,7 +92,13 @@ export declare function getQwenConfigFromOAuth(): Promise<QwenApiConfig | null>;
92
92
  */
93
93
  export declare function saveToOpencodeConfig(config: QwenApiConfig): Promise<void>;
94
94
  /**
95
- * 主函数:从 qwen-code OAuth 配置 opencode
95
+ * 保存配置到 .gemini/settings.json
96
+ *
97
+ * @param config API 配置
98
+ */
99
+ export declare function saveToGeminiSettings(config: QwenApiConfig): Promise<void>;
100
+ /**
101
+ * 主函数:从 qwen-code OAuth 配置 opencode 和 .gemini/settings.json
96
102
  *
97
103
  * @returns 是否成功配置
98
104
  */
@@ -1 +1 @@
1
- {"version":3,"file":"qwen-oauth.d.ts","sourceRoot":"","sources":["../src/qwen-oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,sDAAsD,CAAA;AAEnF;;GAEG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAA;AAE1C;;GAEG;AACH,wBAAgB,eAAe;;;;EAM9B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBzE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAOxD;AASD;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAS7E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAG7D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,aAAa,CAS5E;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAY5E;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E/E;AAED;;;;GAIG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,OAAO,CAAC,CAwBvE"}
1
+ {"version":3,"file":"qwen-oauth.d.ts","sourceRoot":"","sources":["../src/qwen-oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,sDAAsD,CAAA;AAEnF;;GAEG;AACH,eAAO,MAAM,iBAAiB,YAAY,CAAA;AAE1C;;GAEG;AACH,wBAAgB,eAAe;;;;EAM9B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAGvC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBzE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAOxD;AASD;;;;GAIG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAS7E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAG7D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,aAAa,CAS5E;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAY5E;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAa/E;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/E;AAiFD;;;;GAIG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,OAAO,CAAC,CA2BvE"}
@@ -154,13 +154,32 @@ export async function saveToOpencodeConfig(config) {
154
154
  // XDG 路径不存在,使用 macOS 路径
155
155
  opencodeConfigPath = join(homedir(), "Library", "Application Support", "opencode", "opencode.json");
156
156
  }
157
+ await saveConfigToFile(opencodeConfigPath, config, "opencode");
158
+ }
159
+ /**
160
+ * 保存配置到 .gemini/settings.json
161
+ *
162
+ * @param config API 配置
163
+ */
164
+ export async function saveToGeminiSettings(config) {
165
+ const geminiSettingsPath = join(homedir(), ".gemini", "settings.json");
166
+ await saveConfigToFile(geminiSettingsPath, config, "gemini");
167
+ }
168
+ /**
169
+ * 通用函数:保存配置到文件
170
+ *
171
+ * @param configPath 配置文件路径
172
+ * @param config API 配置
173
+ * @param targetType 目标类型 ("opencode" | "gemini")
174
+ */
175
+ async function saveConfigToFile(configPath, config, targetType) {
157
176
  try {
158
177
  // 确保目录存在
159
- await mkdir(join(opencodeConfigPath, ".."), { recursive: true });
178
+ await mkdir(join(configPath, ".."), { recursive: true });
160
179
  // 读取现有配置
161
180
  let existingConfig = {};
162
181
  try {
163
- const content = await readFile(opencodeConfigPath, "utf-8");
182
+ const content = await readFile(configPath, "utf-8");
164
183
  existingConfig = JSON.parse(content);
165
184
  }
166
185
  catch {
@@ -201,23 +220,24 @@ export async function saveToOpencodeConfig(config) {
201
220
  }
202
221
  },
203
222
  options: {
204
- apiKey: config.apiKey,
223
+ // Gemini 只需要 token,不需要 "Bearer " 前缀
224
+ apiKey: targetType === "gemini" ? config.apiKey.replace("Bearer ", "") : config.apiKey,
205
225
  baseURL: config.baseURL,
206
226
  // 保存自定义请求头
207
227
  ...(config.headers ? { headers: config.headers } : {})
208
228
  }
209
229
  };
210
- // 不设置默认 model,让用户自己在 opencode.json 中配置
230
+ // 不设置默认 model,让用户自己在配置文件中配置
211
231
  // 用户可以选择:qwen/coder-model, qwen/qwen-plus 等
212
232
  // 保存配置
213
- await writeFile(opencodeConfigPath, JSON.stringify(existingConfig, null, 2), "utf-8");
233
+ await writeFile(configPath, JSON.stringify(existingConfig, null, 2), "utf-8");
214
234
  }
215
235
  catch (error) {
216
236
  throw error;
217
237
  }
218
238
  }
219
239
  /**
220
- * 主函数:从 qwen-code OAuth 配置 opencode
240
+ * 主函数:从 qwen-code OAuth 配置 opencode 和 .gemini/settings.json
221
241
  *
222
242
  * @returns 是否成功配置
223
243
  */
@@ -236,6 +256,8 @@ export async function configureOpencodeFromQwenOAuth() {
236
256
  const apiConfig = getApiConfigFromOAuth(creds);
237
257
  // 4. 保存到 opencode 配置
238
258
  await saveToOpencodeConfig(apiConfig);
259
+ // 5. 保存到 .gemini/settings.json
260
+ await saveToGeminiSettings(apiConfig);
239
261
  return true;
240
262
  }
241
263
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"qwen-oauth.js","sourceRoot":"","sources":["../src/qwen-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"qwen-oauth.js","sourceRoot":"","sources":["../src/qwen-oauth.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmanxp/opencode-qwen-login-plugin",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "通义千问认证插件 - 从 qwen-code OAuth 自动配置 opencode",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,7 @@
29
29
  "license": "MIT",
30
30
  "repository": {
31
31
  "type": "git",
32
- "url": "https://github.com/your-username/qwen-login-plugin"
32
+ "url": "https://github.com/hotmanxp/planning-with-files.git"
33
33
  },
34
34
  "files": [
35
35
  "dist",
@@ -1,188 +0,0 @@
1
- /**
2
- * 配置合并测试 - 集成测试
3
- *
4
- * 测试 saveToOpencodeConfig 函数的合并行为
5
- */
6
- import { describe, it, beforeEach, afterEach } from 'node:test';
7
- import { writeFile, mkdir, rm, readFile } from 'node:fs/promises';
8
- import { join } from 'node:path';
9
- import assert from 'node:assert';
10
- import { saveToOpencodeConfig } from './qwen-oauth.js';
11
- describe('saveToOpencodeConfig - 配置合并', () => {
12
- let testConfigDir;
13
- let testConfigPath;
14
- let originalHOME;
15
- beforeEach(async () => {
16
- originalHOME = process.env.HOME;
17
- testConfigDir = await mkdtemp('opencode-config-test-');
18
- testConfigPath = join(testConfigDir, '.config', 'opencode');
19
- await mkdir(testConfigPath, { recursive: true });
20
- process.env.HOME = testConfigDir;
21
- });
22
- afterEach(async () => {
23
- process.env.HOME = originalHOME;
24
- await rm(testConfigDir, { recursive: true, force: true }).catch(() => { });
25
- });
26
- it('应该保留现有的 plugin 数组', async () => {
27
- // 创建初始配置
28
- const initialConfig = {
29
- plugin: ['oh-my-opencode', 'another-plugin'],
30
- agent: {
31
- build: {
32
- max_steps: 10
33
- }
34
- }
35
- };
36
- const configPath = join(testConfigPath, 'opencode.json');
37
- await writeFile(configPath, JSON.stringify(initialConfig, null, 2));
38
- // 调用 saveToOpencodeConfig
39
- await saveToOpencodeConfig({
40
- apiKey: 'Bearer test-token',
41
- baseURL: 'https://portal.qwen.ai/v1',
42
- headers: {
43
- 'user-agent': 'QwenCode/test'
44
- }
45
- });
46
- // 读取更新后的配置
47
- const updatedConfig = JSON.parse(await readFile(configPath, 'utf-8'));
48
- // 验证 plugin 数组被保留
49
- assert.ok(updatedConfig.plugin.includes('oh-my-opencode'), 'Should contain oh-my-opencode');
50
- assert.ok(updatedConfig.plugin.includes('another-plugin'), 'Should contain another-plugin');
51
- // 验证 agent 配置被保留
52
- assert.ok(updatedConfig.agent, 'Should have agent config');
53
- assert.ok(updatedConfig.agent.build, 'Should have build agent');
54
- assert.strictEqual(updatedConfig.agent.build.max_steps, 10);
55
- // 验证 qwen provider 被添加
56
- assert.ok(updatedConfig.provider, 'Should have provider config');
57
- assert.ok(updatedConfig.provider.qwen, 'Should have qwen provider');
58
- });
59
- it('应该保留现有的其他 provider', async () => {
60
- // 创建初始配置,包含其他 provider
61
- const initialConfig = {
62
- provider: {
63
- anthropic: {
64
- options: {
65
- apiKey: 'sk-ant-xxx'
66
- }
67
- },
68
- openai: {
69
- options: {
70
- apiKey: 'sk-openai-xxx'
71
- }
72
- }
73
- },
74
- model: 'anthropic/claude-sonnet-4-20250514'
75
- };
76
- const configPath = join(testConfigPath, 'opencode.json');
77
- await writeFile(configPath, JSON.stringify(initialConfig, null, 2));
78
- // 调用 saveToOpencodeConfig
79
- await saveToOpencodeConfig({
80
- apiKey: 'Bearer test-token',
81
- baseURL: 'https://portal.qwen.ai/v1',
82
- headers: {}
83
- });
84
- // 读取更新后的配置
85
- const updatedConfig = JSON.parse(await readFile(configPath, 'utf-8'));
86
- // 验证其他 provider 被保留
87
- assert.ok(updatedConfig.provider.anthropic, "should be defined");
88
- assert.strictEqual(updatedConfig.provider.anthropic.options.apiKey, 'sk-ant-xxx');
89
- assert.ok(updatedConfig.provider.openai, "should be defined");
90
- assert.strictEqual(updatedConfig.provider.openai.options.apiKey, 'sk-openai-xxx');
91
- // 验证 qwen provider 被添加
92
- assert.ok(updatedConfig.provider.qwen, "should be defined");
93
- assert.strictEqual(updatedConfig.provider.qwen.options.apiKey, 'Bearer test-token');
94
- });
95
- it('不应该覆盖现有的 model 配置', async () => {
96
- // 创建初始配置,包含自定义 model
97
- const initialConfig = {
98
- provider: {
99
- anthropic: {
100
- options: {
101
- apiKey: 'sk-ant-xxx'
102
- }
103
- }
104
- },
105
- model: 'anthropic/claude-sonnet-4-20250514'
106
- };
107
- const configPath = join(testConfigPath, 'opencode.json');
108
- await writeFile(configPath, JSON.stringify(initialConfig, null, 2));
109
- // 调用 saveToOpencodeConfig
110
- await saveToOpencodeConfig({
111
- apiKey: 'Bearer test-token',
112
- baseURL: 'https://portal.qwen.ai/v1',
113
- headers: {}
114
- });
115
- // 读取更新后的配置
116
- const updatedConfig = JSON.parse(await readFile(configPath, 'utf-8'));
117
- // 验证原有 model 被保留(不覆盖)
118
- assert.strictEqual(updatedConfig.model, 'anthropic/claude-sonnet-4-20250514');
119
- });
120
- it('不应该设置默认 model', async () => {
121
- // 创建初始配置,没有 model
122
- const initialConfig = {
123
- plugin: ['test-plugin']
124
- };
125
- const configPath = join(testConfigPath, 'opencode.json');
126
- await writeFile(configPath, JSON.stringify(initialConfig, null, 2));
127
- // 调用 saveToOpencodeConfig
128
- await saveToOpencodeConfig({
129
- apiKey: 'Bearer test-token',
130
- baseURL: 'https://portal.qwen.ai/v1',
131
- headers: {}
132
- });
133
- // 读取更新后的配置
134
- const updatedConfig = JSON.parse(await readFile(configPath, 'utf-8'));
135
- // 验证不设置默认 model(让用户自己配置)
136
- assert.strictEqual(updatedConfig.model, undefined, 'Should not set default model');
137
- // 验证 plugin 被保留
138
- assert.ok(updatedConfig.plugin.includes('test-plugin'), 'Should include test-plugin');
139
- });
140
- it('应该保留其他顶层配置', async () => {
141
- // 创建初始配置,包含各种配置
142
- const initialConfig = {
143
- $schema: 'https://opencode.ai/config.json',
144
- agent: {
145
- build: { max_steps: 10 },
146
- plan: { read_only: true }
147
- },
148
- mode: {
149
- default: 'build'
150
- },
151
- instructions: ['./instructions.md'],
152
- plugin: ['plugin-a', 'plugin-b'],
153
- provider: {
154
- anthropic: { options: { apiKey: 'sk-ant' } }
155
- }
156
- };
157
- const configPath = join(testConfigPath, 'opencode.json');
158
- await writeFile(configPath, JSON.stringify(initialConfig, null, 2));
159
- // 调用 saveToOpencodeConfig
160
- await saveToOpencodeConfig({
161
- apiKey: 'Bearer test-token',
162
- baseURL: 'https://portal.qwen.ai/v1',
163
- headers: {}
164
- });
165
- // 读取更新后的配置
166
- const updatedConfig = JSON.parse(await readFile(configPath, 'utf-8'));
167
- // 验证所有配置都被保留
168
- assert.strictEqual(updatedConfig.$schema, 'https://opencode.ai/config.json');
169
- assert.strictEqual(updatedConfig.agent.build.max_steps, 10);
170
- assert.strictEqual(updatedConfig.agent.plan.read_only, true);
171
- assert.strictEqual(updatedConfig.mode.default, 'build');
172
- assert.ok(updatedConfig.instructions.includes('./instructions.md'), "should include");
173
- assert.ok(updatedConfig.plugin.includes('plugin-a'), "should include");
174
- assert.ok(updatedConfig.plugin.includes('plugin-b'), "should include");
175
- assert.strictEqual(updatedConfig.provider.anthropic.options.apiKey, 'sk-ant');
176
- // 验证 qwen 被添加
177
- assert.ok(updatedConfig.provider.qwen, "should be defined");
178
- });
179
- });
180
- // 辅助函数
181
- async function mkdtemp(template) {
182
- const fs = await import('node:fs/promises');
183
- const path = await import('node:path');
184
- const os = await import('node:os');
185
- const dir = path.join(os.tmpdir(), `${template}${Date.now()}-${Math.random().toString(36).slice(2)}`);
186
- await fs.mkdir(dir, { recursive: true });
187
- return dir;
188
- }
package/dist/plugin.d.ts DELETED
@@ -1,14 +0,0 @@
1
- /**
2
- * Qwen Login Plugin - 通义千问认证插件
3
- *
4
- * 为 opencode 提供 Qwen 模型认证支持
5
- * 自动从 qwen-code OAuth 配置导入认证信息
6
- */
7
- /**
8
- * Qwen 认证插件主函数
9
- */
10
- declare function plugin(pluginInput: any): Promise<{
11
- config: (config: any) => Promise<void>;
12
- }>;
13
- export default plugin;
14
- //# sourceMappingURL=plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,iBAAe,MAAM,CAAC,WAAW,EAAE,GAAG;qBAUX,GAAG;GAgE7B;AAGD,eAAe,MAAM,CAAA"}
package/dist/plugin.js DELETED
@@ -1,83 +0,0 @@
1
- /**
2
- * Qwen Login Plugin - 通义千问认证插件
3
- *
4
- * 为 opencode 提供 Qwen 模型认证支持
5
- * 自动从 qwen-code OAuth 配置导入认证信息
6
- */
7
- import { configureOpencodeFromQwenOAuth, readOAuthCredentials, isTokenValid, getApiConfigFromOAuth } from "./qwen-oauth.js";
8
- /**
9
- * Qwen 认证插件主函数
10
- */
11
- async function plugin(pluginInput) {
12
- // 插件加载时立即写入配置文件
13
- try {
14
- await configureOpencodeFromQwenOAuth();
15
- }
16
- catch (error) {
17
- // 静默失败,使用其他认证方式
18
- }
19
- return {
20
- // 配置钩子 - 修改运行时配置
21
- config: async (config) => {
22
- try {
23
- // 1. 读取 OAuth 凭证
24
- const creds = await readOAuthCredentials();
25
- if (!creds) {
26
- return;
27
- }
28
- // 2. 检查 token 是否有效
29
- if (!isTokenValid(creds)) {
30
- return;
31
- }
32
- // 3. 生成 API 配置
33
- const apiConfig = getApiConfigFromOAuth(creds);
34
- // 4. 按照 opencode provider schema 配置
35
- if (!config.provider) {
36
- config.provider = {};
37
- }
38
- config.provider.qwen = {
39
- name: "Qwen (通义千问)",
40
- npm: "@ai-sdk/openai-compatible",
41
- env: [],
42
- models: {
43
- "coder-model": {
44
- name: "Qwen Coder Model",
45
- tool_call: true,
46
- reasoning: false,
47
- attachment: true,
48
- temperature: true,
49
- interleaved: true,
50
- limit: {
51
- context: 256000,
52
- output: 8192
53
- }
54
- },
55
- "qwen-plus": {
56
- name: "Qwen Plus",
57
- tool_call: true,
58
- reasoning: false,
59
- attachment: true,
60
- temperature: true,
61
- interleaved: true,
62
- limit: {
63
- context: 131072,
64
- output: 8192
65
- }
66
- }
67
- },
68
- options: {
69
- apiKey: apiConfig.apiKey,
70
- baseURL: apiConfig.baseURL,
71
- headers: apiConfig.headers
72
- }
73
- };
74
- }
75
- catch (error) {
76
- // 静默失败
77
- }
78
- }
79
- };
80
- }
81
- // 只导出默认函数
82
- export default plugin;
83
- //# sourceMappingURL=plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,8BAA8B,EAAE,oBAAoB,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAE3H;;GAEG;AACH,KAAK,UAAU,MAAM,CAAC,WAAgB;IACpC,gBAAgB;IAChB,IAAI,CAAC;QACH,MAAM,8BAA8B,EAAE,CAAA;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,gBAAgB;IAClB,CAAC;IAED,OAAO;QACL,iBAAiB;QACjB,MAAM,EAAE,KAAK,EAAE,MAAW,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACH,iBAAiB;gBACjB,MAAM,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAA;gBAE1C,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAM;gBACR,CAAC;gBAED,mBAAmB;gBACnB,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAM;gBACR,CAAC;gBAED,eAAe;gBACf,MAAM,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAA;gBAE9C,oCAAoC;gBACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACrB,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAA;gBACtB,CAAC;gBAED,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG;oBACrB,IAAI,EAAE,aAAa;oBACnB,GAAG,EAAE,2BAA2B;oBAChC,GAAG,EAAE,EAAE;oBACP,MAAM,EAAE;wBACN,aAAa,EAAE;4BACb,IAAI,EAAE,kBAAkB;4BACxB,SAAS,EAAE,IAAI;4BACf,SAAS,EAAE,KAAK;4BAChB,UAAU,EAAE,IAAI;4BAChB,WAAW,EAAE,IAAI;4BACjB,WAAW,EAAE,IAAI;4BACjB,KAAK,EAAE;gCACL,OAAO,EAAE,MAAM;gCACf,MAAM,EAAE,IAAI;6BACb;yBACF;wBACD,WAAW,EAAE;4BACX,IAAI,EAAE,WAAW;4BACjB,SAAS,EAAE,IAAI;4BACf,SAAS,EAAE,KAAK;4BAChB,UAAU,EAAE,IAAI;4BAChB,WAAW,EAAE,IAAI;4BACjB,WAAW,EAAE,IAAI;4BACjB,KAAK,EAAE;gCACL,OAAO,EAAE,MAAM;gCACf,MAAM,EAAE,IAAI;6BACb;yBACF;qBACF;oBACD,OAAO,EAAE;wBACP,MAAM,EAAE,SAAS,CAAC,MAAM;wBACxB,OAAO,EAAE,SAAS,CAAC,OAAO;wBAC1B,OAAO,EAAE,SAAS,CAAC,OAAO;qBAC3B;iBACF,CAAA;YAEH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO;YACT,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,UAAU;AACV,eAAe,MAAM,CAAA"}
@@ -1,123 +0,0 @@
1
- /**
2
- * Qwen Login Plugin - 单元测试
3
- *
4
- * 测试配置合并、OAuth 读取等功能
5
- */
6
- import { describe, it, beforeEach, afterEach } from 'node:test';
7
- import { writeFile, mkdir, rm } from 'node:fs/promises';
8
- import { join } from 'node:path';
9
- import { tmpdir } from 'node:os';
10
- import assert from 'node:assert';
11
- import { readOAuthCredentials, isTokenValid, getApiConfigFromOAuth, buildBaseUrl, buildDefaultHeaders } from './qwen-oauth.js';
12
- // 测试用的临时目录
13
- let testDir;
14
- let testQwenDir;
15
- describe('Qwen Login Plugin', () => {
16
- beforeEach(async () => {
17
- // 创建临时测试目录
18
- testDir = await mkdtemp('qwen-login-plugin-test-');
19
- testQwenDir = join(testDir, '.qwen');
20
- await mkdir(testQwenDir, { recursive: true });
21
- });
22
- afterEach(async () => {
23
- // 清理临时目录
24
- await rm(testDir, { recursive: true, force: true }).catch(() => { });
25
- });
26
- describe('buildBaseUrl', () => {
27
- it('应该从 resource_url 构建正确的 endpoint', () => {
28
- assert.strictEqual(buildBaseUrl('portal.qwen.ai'), 'https://portal.qwen.ai/v1');
29
- });
30
- it('应该在没有 resource_url 时使用默认值', () => {
31
- assert.strictEqual(buildBaseUrl(''), 'https://dashscope.aliyuncs.com/compatible-mode/v1');
32
- assert.strictEqual(buildBaseUrl(undefined), 'https://dashscope.aliyuncs.com/compatible-mode/v1');
33
- });
34
- });
35
- describe('buildDefaultHeaders', () => {
36
- it('应该生成完整的请求头', () => {
37
- const headers = buildDefaultHeaders('test-token-123');
38
- assert.ok(headers.authorization, 'Should have authorization header');
39
- assert.strictEqual(headers.authorization, 'Bearer test-token-123');
40
- assert.ok(headers['user-agent'], 'Should have user-agent header');
41
- assert.match(headers['user-agent'], /QwenCode\/.*\(.*;.*\)/);
42
- assert.ok(headers['x-dashscope-authtype'], 'Should have x-dashscope-authtype header');
43
- assert.strictEqual(headers['x-dashscope-authtype'], 'qwen-oauth');
44
- assert.ok(headers['content-type'], 'Should have content-type header');
45
- assert.strictEqual(headers['content-type'], 'application/json');
46
- assert.ok(headers['accept'], 'Should have accept header');
47
- assert.strictEqual(headers['accept'], 'application/json');
48
- });
49
- it('应该包含所有 stainless 相关 headers', () => {
50
- const headers = buildDefaultHeaders('test-token');
51
- assert.strictEqual(headers['x-stainless-lang'], 'js');
52
- assert.strictEqual(headers['x-stainless-package-version'], '4.104.0');
53
- assert.strictEqual(headers['x-stainless-os'], 'MacOS');
54
- assert.strictEqual(headers['x-stainless-runtime'], 'node');
55
- });
56
- });
57
- describe('isTokenValid', () => {
58
- it('应该返回 true 当 token 未过期', () => {
59
- const futureDate = Date.now() + 1000 * 60 * 60 * 24; // 1 天后
60
- assert.strictEqual(isTokenValid({ expiry_date: futureDate }), true);
61
- });
62
- it('应该返回 false 当 token 已过期', () => {
63
- const pastDate = Date.now() - 1000 * 60 * 60; // 1 小时前
64
- assert.strictEqual(isTokenValid({ expiry_date: pastDate }), false);
65
- });
66
- });
67
- describe('readOAuthCredentials', () => {
68
- it('应该成功读取 OAuth 凭证', async () => {
69
- // 模拟 OAuth 凭证文件
70
- const mockCreds = {
71
- access_token: 'test-access-token',
72
- token_type: 'Bearer',
73
- refresh_token: 'test-refresh-token',
74
- resource_url: 'portal.qwen.ai',
75
- expiry_date: Date.now() + 1000000
76
- };
77
- await writeFile(join(testQwenDir, 'oauth_creds.json'), JSON.stringify(mockCreds));
78
- // 临时修改 home 目录
79
- const originalHome = process.env.HOME;
80
- process.env.HOME = testDir;
81
- try {
82
- const creds = await readOAuthCredentials();
83
- assert.deepStrictEqual(creds, mockCreds);
84
- }
85
- finally {
86
- process.env.HOME = originalHome;
87
- }
88
- });
89
- it('应该在文件不存在时返回 null', async () => {
90
- const originalHome = process.env.HOME;
91
- process.env.HOME = testDir;
92
- try {
93
- const creds = await readOAuthCredentials();
94
- assert.strictEqual(creds, null);
95
- }
96
- finally {
97
- process.env.HOME = originalHome;
98
- }
99
- });
100
- });
101
- describe('getApiConfigFromOAuth', () => {
102
- it('应该从 OAuth 凭证生成正确的 API 配置', () => {
103
- const mockCreds = {
104
- access_token: 'test-token',
105
- token_type: 'Bearer',
106
- refresh_token: 'test-refresh',
107
- resource_url: 'portal.qwen.ai',
108
- expiry_date: Date.now() + 1000000
109
- };
110
- const config = getApiConfigFromOAuth(mockCreds);
111
- assert.strictEqual(config.apiKey, 'Bearer test-token');
112
- assert.strictEqual(config.baseURL, 'https://portal.qwen.ai/v1');
113
- assert.ok(config.headers);
114
- assert.strictEqual(config.headers?.authorization, 'Bearer test-token');
115
- });
116
- });
117
- });
118
- // 辅助函数
119
- async function mkdtemp(template) {
120
- const dir = join(tmpdir(), `${template}${Date.now()}-${Math.random().toString(36).slice(2)}`);
121
- await mkdir(dir, { recursive: true });
122
- return dir;
123
- }