qwen-opencode-provider 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +49 -32
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,16 +1,14 @@
1
1
  /**
2
2
  * OpenCode Qwen API Plugin
3
3
  *
4
- * Uses local proxy approach (like Pollinations plugin) to handle auth.
5
- * No manual config needed!
6
- *
7
- * Usage:
8
- * 1. Add to opencode.json: { "plugin": ["qwen-opencode-provider"] }
9
- * 2. Run /connect -> Select "Other" -> Enter "qwen"
4
+ * Uses local proxy approach to handle auth - reads API key from OpenCode auth storage.
10
5
  */
11
6
 
12
7
  import http from 'http';
8
+ import https from 'https';
13
9
  import fs from 'fs';
10
+ import path from 'path';
11
+ import os from 'os';
14
12
 
15
13
  const LOG_FILE = '/tmp/opencode_qwen_plugin.log';
16
14
  const PROVIDER_ID = 'qwen';
@@ -43,13 +41,32 @@ function log(msg) {
43
41
  } catch (e) { }
44
42
  }
45
43
 
46
- let cachedApiKey = '';
44
+ function getAuthFilePath() {
45
+ const home = os.homedir();
46
+ return path.join(home, '.local', 'share', 'opencode', 'auth.json');
47
+ }
48
+
49
+ function readApiKeyFromAuth() {
50
+ try {
51
+ const authPath = getAuthFilePath();
52
+ if (fs.existsSync(authPath)) {
53
+ const content = fs.readFileSync(authPath, 'utf-8');
54
+ const auth = JSON.parse(content);
55
+
56
+ if (auth[PROVIDER_ID]?.apiKey) {
57
+ log(`[Auth] Found API key for ${PROVIDER_ID}`);
58
+ return auth[PROVIDER_ID].apiKey;
59
+ }
60
+ }
61
+ } catch (e) {
62
+ log(`[Auth] Error reading auth: ${e.message}`);
63
+ }
64
+ return null;
65
+ }
47
66
 
48
67
  function startProxy() {
49
68
  return new Promise((resolve) => {
50
69
  const server = http.createServer(async (req, res) => {
51
- log(`[Proxy] Request: ${req.method} ${req.url}`);
52
-
53
70
  res.setHeader('Access-Control-Allow-Origin', '*');
54
71
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
55
72
  res.setHeader('Access-Control-Allow-Headers', '*');
@@ -61,27 +78,37 @@ function startProxy() {
61
78
  }
62
79
 
63
80
  if (req.method === 'GET' && req.url === '/health') {
81
+ const apiKey = readApiKeyFromAuth();
64
82
  res.writeHead(200, { 'Content-Type': 'application/json' });
65
- res.end(JSON.stringify({ status: 'ok', provider: 'qwen' }));
83
+ res.end(JSON.stringify({
84
+ status: 'ok',
85
+ provider: 'qwen',
86
+ hasKey: !!apiKey
87
+ }));
66
88
  return;
67
89
  }
68
90
 
69
91
  if (req.url.startsWith('/v1/')) {
70
- const targetUrl = API_BASE_URL + req.url;
92
+ const apiKey = readApiKeyFromAuth();
71
93
 
94
+ log(`[Proxy] Request: ${req.method} ${req.url}, hasKey: ${!!apiKey}`);
95
+
72
96
  const options = {
73
- hostname: new URL(API_BASE_URL).hostname,
97
+ hostname: 'qwen.aikit.club',
74
98
  port: 443,
75
99
  path: req.url,
76
100
  method: req.method,
77
101
  headers: {
78
102
  ...req.headers,
79
- 'Host': new URL(API_BASE_URL).host
103
+ 'Host': 'qwen.aikit.club'
80
104
  }
81
105
  };
82
106
 
83
- if (cachedApiKey) {
84
- options.headers['Authorization'] = `Bearer ${cachedApiKey}`;
107
+ if (apiKey) {
108
+ options.headers['Authorization'] = `Bearer ${apiKey}`;
109
+ log(`[Proxy] Added Authorization header`);
110
+ } else {
111
+ log(`[Proxy] No API key found!`);
85
112
  }
86
113
 
87
114
  const proxyReq = https.request(options, (proxyRes) => {
@@ -90,7 +117,7 @@ function startProxy() {
90
117
  });
91
118
 
92
119
  proxyReq.on('error', (e) => {
93
- log(`[Proxy] Error: ${e}`);
120
+ log(`[Proxy] Error: ${e.message}`);
94
121
  res.writeHead(500);
95
122
  res.end(JSON.stringify({ error: String(e) }));
96
123
  });
@@ -110,20 +137,20 @@ function startProxy() {
110
137
  });
111
138
 
112
139
  server.on('error', (e) => {
113
- log(`[Proxy] Fatal Error: ${e}`);
140
+ log(`[Proxy] Fatal Error: ${e.message}`);
114
141
  resolve(0);
115
142
  });
116
143
  });
117
144
  }
118
145
 
119
- import https from 'https';
120
-
121
146
  export const QwenPlugin = async (ctx) => {
122
147
  log('[Plugin] Initializing Qwen Plugin...');
123
148
 
124
149
  const port = await startProxy();
125
150
  const localBaseUrl = `http://127.0.0.1:${port}/v1`;
126
151
 
152
+ log(`[Plugin] Local proxy URL: ${localBaseUrl}`);
153
+
127
154
  return {
128
155
  async config(config) {
129
156
  log('[Hook] config() called');
@@ -140,22 +167,12 @@ export const QwenPlugin = async (ctx) => {
140
167
  log(`[Hook] Registered provider with ${Object.keys(QWEN_MODELS).length} models`);
141
168
  },
142
169
 
143
- async 'installation.updated'({ installation }) {
144
- log('[Hook] installation.updated() called');
145
-
146
- if (installation?.auth?.[PROVIDER_ID]?.apiKey) {
147
- cachedApiKey = installation.auth[PROVIDER_ID].apiKey;
148
- log('[Hook] API Key updated');
149
- }
150
- },
151
-
152
170
  auth: {
153
171
  provider: PROVIDER_ID,
154
172
  loader: async (auth) => {
155
- if (auth?.apiKey) {
156
- cachedApiKey = auth.apiKey;
157
- }
158
- return { apiKey: cachedApiKey };
173
+ const apiKey = auth?.apiKey || '';
174
+ log(`[Auth] loader called, hasKey: ${!!apiKey}`);
175
+ return { apiKey };
159
176
  },
160
177
  methods: [
161
178
  { type: 'api', label: 'API Key' }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwen-opencode-provider",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "OpenCode plugin for Qwen API - auto adds provider with 28+ models",
5
5
  "main": "index.js",
6
6
  "type": "module",