@oevortex/opencode-qwen-auth 0.1.0 → 0.1.2
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/index.cjs +1600 -0
- package/dist/index.d.cts +445 -0
- package/dist/index.d.ts +445 -0
- package/dist/index.js +1534 -0
- package/package.json +14 -6
- package/index.ts +0 -82
- package/src/constants.ts +0 -43
- package/src/global.d.ts +0 -257
- package/src/models.ts +0 -148
- package/src/plugin/auth.ts +0 -151
- package/src/plugin/browser.ts +0 -126
- package/src/plugin/fetch-wrapper.ts +0 -460
- package/src/plugin/logger.ts +0 -111
- package/src/plugin/server.ts +0 -364
- package/src/plugin/token.ts +0 -225
- package/src/plugin.ts +0 -444
- package/src/qwen/oauth.ts +0 -271
- package/src/qwen/thinking-parser.ts +0 -190
- package/src/types.ts +0 -292
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen OAuth and plugin types
|
|
3
|
+
* Based on revibe/core/llm/backend/qwen implementation
|
|
4
|
+
*/
|
|
5
|
+
interface QwenOAuthCredentials {
|
|
6
|
+
access_token: string;
|
|
7
|
+
refresh_token: string;
|
|
8
|
+
token_type: string;
|
|
9
|
+
expiry_date: number;
|
|
10
|
+
resource_url?: string;
|
|
11
|
+
}
|
|
12
|
+
interface QwenTokenResponse {
|
|
13
|
+
access_token: string;
|
|
14
|
+
refresh_token?: string;
|
|
15
|
+
token_type: string;
|
|
16
|
+
expires_in: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
error_description?: string;
|
|
19
|
+
}
|
|
20
|
+
interface QwenModelInfo {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
context_window?: number;
|
|
24
|
+
max_output?: number;
|
|
25
|
+
input_price?: number;
|
|
26
|
+
output_price?: number;
|
|
27
|
+
supports_native_tools?: boolean;
|
|
28
|
+
supports_thinking?: boolean;
|
|
29
|
+
}
|
|
30
|
+
interface ChatMessage {
|
|
31
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
32
|
+
content?: string;
|
|
33
|
+
reasoning_content?: string;
|
|
34
|
+
tool_calls?: ToolCall[];
|
|
35
|
+
tool_call_id?: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
}
|
|
38
|
+
interface ToolCall {
|
|
39
|
+
id?: string;
|
|
40
|
+
index?: number;
|
|
41
|
+
type?: "function";
|
|
42
|
+
function: FunctionCall;
|
|
43
|
+
}
|
|
44
|
+
interface FunctionCall {
|
|
45
|
+
name?: string;
|
|
46
|
+
arguments?: string;
|
|
47
|
+
}
|
|
48
|
+
interface Tool {
|
|
49
|
+
type: "function";
|
|
50
|
+
function: {
|
|
51
|
+
name: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
parameters?: Record<string, unknown>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
type ToolChoice = "none" | "auto" | "required" | {
|
|
57
|
+
type: "function";
|
|
58
|
+
function: {
|
|
59
|
+
name: string;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
interface ChatCompletionRequest {
|
|
63
|
+
model: string;
|
|
64
|
+
messages: ChatMessage[];
|
|
65
|
+
temperature?: number;
|
|
66
|
+
max_tokens?: number;
|
|
67
|
+
tools?: Tool[];
|
|
68
|
+
tool_choice?: ToolChoice;
|
|
69
|
+
stream?: boolean;
|
|
70
|
+
stream_options?: {
|
|
71
|
+
include_usage?: boolean;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
interface ChatCompletionChoice {
|
|
75
|
+
index: number;
|
|
76
|
+
message?: ChatMessage;
|
|
77
|
+
delta?: Partial<ChatMessage>;
|
|
78
|
+
finish_reason?: string | null;
|
|
79
|
+
}
|
|
80
|
+
interface ChatCompletionUsage {
|
|
81
|
+
prompt_tokens: number;
|
|
82
|
+
completion_tokens: number;
|
|
83
|
+
total_tokens?: number;
|
|
84
|
+
}
|
|
85
|
+
interface ChatCompletionResponse {
|
|
86
|
+
id: string;
|
|
87
|
+
object: string;
|
|
88
|
+
created: number;
|
|
89
|
+
model: string;
|
|
90
|
+
choices: ChatCompletionChoice[];
|
|
91
|
+
usage?: ChatCompletionUsage;
|
|
92
|
+
error?: {
|
|
93
|
+
message: string;
|
|
94
|
+
type?: string;
|
|
95
|
+
code?: string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
interface AuthDetails {
|
|
99
|
+
type: "oauth" | "api";
|
|
100
|
+
}
|
|
101
|
+
interface OAuthAuthDetails extends AuthDetails {
|
|
102
|
+
type: "oauth";
|
|
103
|
+
access?: string;
|
|
104
|
+
refresh: string;
|
|
105
|
+
expires?: number;
|
|
106
|
+
}
|
|
107
|
+
interface RefreshParts {
|
|
108
|
+
refreshToken: string;
|
|
109
|
+
resourceUrl?: string;
|
|
110
|
+
}
|
|
111
|
+
interface LoaderResult {
|
|
112
|
+
apiKey?: string;
|
|
113
|
+
baseUrl?: string;
|
|
114
|
+
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
115
|
+
}
|
|
116
|
+
interface Provider {
|
|
117
|
+
models?: Record<string, any>;
|
|
118
|
+
}
|
|
119
|
+
type GetAuth = () => Promise<AuthDetails>;
|
|
120
|
+
interface PluginContext {
|
|
121
|
+
client: {
|
|
122
|
+
auth: {
|
|
123
|
+
set: (options: {
|
|
124
|
+
path: {
|
|
125
|
+
id: string;
|
|
126
|
+
};
|
|
127
|
+
body: Record<string, unknown>;
|
|
128
|
+
}) => Promise<void>;
|
|
129
|
+
};
|
|
130
|
+
tui: {
|
|
131
|
+
showToast: (options: {
|
|
132
|
+
body: {
|
|
133
|
+
message: string;
|
|
134
|
+
variant: "info" | "success" | "warning" | "error";
|
|
135
|
+
};
|
|
136
|
+
}) => Promise<void>;
|
|
137
|
+
};
|
|
138
|
+
log?: {
|
|
139
|
+
debug: (message: string, meta?: Record<string, unknown>) => void;
|
|
140
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
141
|
+
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
142
|
+
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
interface AuthorizeResult {
|
|
147
|
+
url: string;
|
|
148
|
+
instructions?: string;
|
|
149
|
+
method: "auto" | "manual";
|
|
150
|
+
callback: () => Promise<TokenExchangeResult>;
|
|
151
|
+
}
|
|
152
|
+
interface TokenExchangeResult {
|
|
153
|
+
type: "success" | "failed";
|
|
154
|
+
refresh?: string;
|
|
155
|
+
access?: string;
|
|
156
|
+
expires?: number;
|
|
157
|
+
error?: string;
|
|
158
|
+
resourceUrl?: string;
|
|
159
|
+
}
|
|
160
|
+
interface AuthMethod {
|
|
161
|
+
label: string;
|
|
162
|
+
type: "oauth" | "api";
|
|
163
|
+
authorize?: () => Promise<AuthorizeResult>;
|
|
164
|
+
prompts?: Array<{
|
|
165
|
+
type: "text" | "password";
|
|
166
|
+
message: string;
|
|
167
|
+
key: string;
|
|
168
|
+
}>;
|
|
169
|
+
}
|
|
170
|
+
interface PluginResult {
|
|
171
|
+
auth?: {
|
|
172
|
+
provider: string;
|
|
173
|
+
loader: (getAuth: GetAuth, provider: Provider) => Promise<LoaderResult | Record<string, unknown>>;
|
|
174
|
+
methods: AuthMethod[];
|
|
175
|
+
};
|
|
176
|
+
tool?: Record<string, unknown>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Main Qwen OAuth Plugin for OpenCode
|
|
181
|
+
* Provides authentication with Qwen/Alibaba Cloud DashScope API
|
|
182
|
+
*/
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Main Qwen OAuth Plugin export.
|
|
186
|
+
*
|
|
187
|
+
* This plugin provides:
|
|
188
|
+
* - OAuth authentication with Qwen/Alibaba Cloud
|
|
189
|
+
* - Automatic token refresh
|
|
190
|
+
* - Request handling with proper authentication headers
|
|
191
|
+
*/
|
|
192
|
+
declare function QwenOAuthPlugin({ client, }: PluginContext): Promise<PluginResult>;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Qwen OAuth Manager
|
|
196
|
+
* Handles OAuth token management for Qwen Code API
|
|
197
|
+
* Based on revibe/core/llm/backend/qwen/oauth.py
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get the path to the cached OAuth credentials file.
|
|
202
|
+
*/
|
|
203
|
+
declare function getQwenCachedCredentialPath(customPath?: string): string;
|
|
204
|
+
/**
|
|
205
|
+
* Manages OAuth authentication for Qwen Code API.
|
|
206
|
+
*/
|
|
207
|
+
declare class QwenOAuthManager {
|
|
208
|
+
private oauthPath?;
|
|
209
|
+
private credentials;
|
|
210
|
+
private refreshLock;
|
|
211
|
+
constructor(oauthPath?: string);
|
|
212
|
+
/**
|
|
213
|
+
* Load OAuth credentials from the cached file.
|
|
214
|
+
*/
|
|
215
|
+
private loadCachedCredentials;
|
|
216
|
+
/**
|
|
217
|
+
* Refresh the OAuth access token.
|
|
218
|
+
*/
|
|
219
|
+
refreshAccessToken(credentials: QwenOAuthCredentials): Promise<QwenOAuthCredentials>;
|
|
220
|
+
/**
|
|
221
|
+
* Save credentials to the cache file.
|
|
222
|
+
*/
|
|
223
|
+
private saveCredentials;
|
|
224
|
+
/**
|
|
225
|
+
* Check if the access token is still valid.
|
|
226
|
+
*/
|
|
227
|
+
private isTokenValid;
|
|
228
|
+
/**
|
|
229
|
+
* Invalidate cached credentials to force a refresh on next request.
|
|
230
|
+
*/
|
|
231
|
+
invalidateCredentials(): void;
|
|
232
|
+
/**
|
|
233
|
+
* Ensure we have valid authentication credentials.
|
|
234
|
+
* @returns Tuple of [access_token, base_url]
|
|
235
|
+
*/
|
|
236
|
+
ensureAuthenticated(forceRefresh?: boolean): Promise<[string, string]>;
|
|
237
|
+
/**
|
|
238
|
+
* Get the API base URL from credentials.
|
|
239
|
+
*/
|
|
240
|
+
private getBaseUrl;
|
|
241
|
+
/**
|
|
242
|
+
* Get the current credentials, refreshing if needed.
|
|
243
|
+
*/
|
|
244
|
+
getCredentials(): Promise<QwenOAuthCredentials>;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Check if OAuth credentials exist.
|
|
248
|
+
*/
|
|
249
|
+
declare function hasQwenCredentials(customPath?: string): boolean;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Thinking Block Parser for Qwen
|
|
253
|
+
* Handles <think>...</think> tags in the stream to separate
|
|
254
|
+
* reasoning content from regular text content.
|
|
255
|
+
*
|
|
256
|
+
* Based on revibe/core/llm/backend/qwen/handler.py
|
|
257
|
+
*/
|
|
258
|
+
interface ParseResult {
|
|
259
|
+
/** Regular content (outside thinking blocks) */
|
|
260
|
+
regularContent: string;
|
|
261
|
+
/** Reasoning/thinking content (inside thinking blocks) */
|
|
262
|
+
reasoningContent: string;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Parser for Qwen's thinking/reasoning blocks.
|
|
266
|
+
*
|
|
267
|
+
* Handles `<think>...</think>` tags in the stream to separate
|
|
268
|
+
* reasoning content from regular text content.
|
|
269
|
+
*
|
|
270
|
+
* This parser is stateful and designed for streaming scenarios
|
|
271
|
+
* where tags might be split across multiple chunks.
|
|
272
|
+
*/
|
|
273
|
+
declare class ThinkingBlockParser {
|
|
274
|
+
private inThinkingBlock;
|
|
275
|
+
private buffer;
|
|
276
|
+
/**
|
|
277
|
+
* Reset the parser state.
|
|
278
|
+
* Call this when starting a new message/response.
|
|
279
|
+
*/
|
|
280
|
+
reset(): void;
|
|
281
|
+
/**
|
|
282
|
+
* Check if currently inside a thinking block.
|
|
283
|
+
*/
|
|
284
|
+
isInThinkingBlock(): boolean;
|
|
285
|
+
/**
|
|
286
|
+
* Parse text and separate thinking content from regular content.
|
|
287
|
+
*
|
|
288
|
+
* This method is designed for streaming - call it with each chunk
|
|
289
|
+
* of text as it arrives, and it will return the parsed content.
|
|
290
|
+
*
|
|
291
|
+
* @param text - The text chunk to parse
|
|
292
|
+
* @returns Object containing regular and reasoning content
|
|
293
|
+
*/
|
|
294
|
+
parse(text: string): ParseResult;
|
|
295
|
+
/**
|
|
296
|
+
* Find if there's a partial opening tag at the end of the buffer.
|
|
297
|
+
* Returns the length of the partial tag, or 0 if none found.
|
|
298
|
+
*/
|
|
299
|
+
private findPartialOpenTag;
|
|
300
|
+
/**
|
|
301
|
+
* Flush any remaining content in the buffer.
|
|
302
|
+
* Call this when the stream is complete.
|
|
303
|
+
*
|
|
304
|
+
* @returns Any remaining content based on current state
|
|
305
|
+
*/
|
|
306
|
+
flush(): ParseResult;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Parse complete text for thinking blocks (non-streaming).
|
|
310
|
+
*
|
|
311
|
+
* Use this for non-streaming responses where the entire text is available.
|
|
312
|
+
*
|
|
313
|
+
* @param text - The complete text to parse
|
|
314
|
+
* @returns Object containing regular and reasoning content
|
|
315
|
+
*/
|
|
316
|
+
declare function parseThinkingBlocks(text: string): ParseResult;
|
|
317
|
+
/**
|
|
318
|
+
* Remove thinking blocks from text, returning only regular content.
|
|
319
|
+
*
|
|
320
|
+
* @param text - The text to process
|
|
321
|
+
* @returns Text with thinking blocks removed
|
|
322
|
+
*/
|
|
323
|
+
declare function stripThinkingBlocks(text: string): string;
|
|
324
|
+
/**
|
|
325
|
+
* Extract only thinking/reasoning content from text.
|
|
326
|
+
*
|
|
327
|
+
* @param text - The text to process
|
|
328
|
+
* @returns Only the content inside thinking blocks
|
|
329
|
+
*/
|
|
330
|
+
declare function extractThinkingContent(text: string): string;
|
|
331
|
+
/**
|
|
332
|
+
* Check if text contains any thinking blocks.
|
|
333
|
+
*
|
|
334
|
+
* @param text - The text to check
|
|
335
|
+
* @returns True if text contains <think> tags
|
|
336
|
+
*/
|
|
337
|
+
declare function hasThinkingBlocks(text: string): boolean;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Auth utilities for the Qwen OpenCode plugin
|
|
341
|
+
* Based on opencode-google-antigravity-auth/src/plugin/auth.ts
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Type guard to check if auth details are OAuth type.
|
|
346
|
+
*/
|
|
347
|
+
declare function isOAuthAuth(auth: AuthDetails): auth is OAuthAuthDetails;
|
|
348
|
+
/**
|
|
349
|
+
* Parse refresh token string into its constituent parts.
|
|
350
|
+
* Format: "refreshToken|resourceUrl"
|
|
351
|
+
*/
|
|
352
|
+
declare function parseRefreshParts(refresh: string): RefreshParts;
|
|
353
|
+
/**
|
|
354
|
+
* Serialize refresh parts back into stored string format.
|
|
355
|
+
*/
|
|
356
|
+
declare function formatRefreshParts(parts: RefreshParts): string;
|
|
357
|
+
/**
|
|
358
|
+
* Check if an access token is expired or missing.
|
|
359
|
+
* Includes a buffer for clock skew to trigger refresh slightly early.
|
|
360
|
+
*/
|
|
361
|
+
declare function accessTokenExpired(auth: OAuthAuthDetails): boolean;
|
|
362
|
+
/**
|
|
363
|
+
* Create auth details object for storage.
|
|
364
|
+
*/
|
|
365
|
+
declare function createAuthDetails(accessToken: string, refreshToken: string, expiresAt: number, resourceUrl?: string): OAuthAuthDetails;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Token refresh utilities for the Qwen OpenCode plugin
|
|
369
|
+
* Handles refreshing OAuth access tokens when they expire
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Refresh an OAuth access token using the refresh token.
|
|
374
|
+
*
|
|
375
|
+
* @param auth - Current OAuth auth details with refresh token
|
|
376
|
+
* @param client - Plugin client for showing toasts
|
|
377
|
+
* @returns Updated auth details with new access token, or null on failure
|
|
378
|
+
*/
|
|
379
|
+
declare function refreshAccessToken(auth: OAuthAuthDetails, client: PluginContext["client"]): Promise<OAuthAuthDetails | null>;
|
|
380
|
+
/**
|
|
381
|
+
* Check if an access token needs refresh (expired or about to expire).
|
|
382
|
+
*/
|
|
383
|
+
declare function needsTokenRefresh(auth: OAuthAuthDetails): boolean;
|
|
384
|
+
/**
|
|
385
|
+
* Ensure we have a valid access token, refreshing if necessary.
|
|
386
|
+
*
|
|
387
|
+
* @param auth - Current auth details
|
|
388
|
+
* @param client - Plugin client for showing toasts
|
|
389
|
+
* @returns Updated auth details, or null if refresh failed
|
|
390
|
+
*/
|
|
391
|
+
declare function ensureValidToken(auth: OAuthAuthDetails, client: PluginContext["client"]): Promise<OAuthAuthDetails | null>;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Qwen Code Models Configuration
|
|
395
|
+
* Defines available models and their specifications for the OpenCode plugin
|
|
396
|
+
*/
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Available Qwen Code models accessible through OAuth authentication.
|
|
400
|
+
* All models are free when using OAuth.
|
|
401
|
+
*/
|
|
402
|
+
declare const QWEN_CODE_MODELS: Record<string, QwenModelInfo>;
|
|
403
|
+
/**
|
|
404
|
+
* Model aliases mapping friendly names to actual model IDs.
|
|
405
|
+
*/
|
|
406
|
+
declare const MODEL_ALIASES: Record<string, string>;
|
|
407
|
+
/**
|
|
408
|
+
* Default model to use when none specified.
|
|
409
|
+
*/
|
|
410
|
+
declare const DEFAULT_MODEL = "qwen3-coder-plus";
|
|
411
|
+
/**
|
|
412
|
+
* Get model info by ID or alias.
|
|
413
|
+
*
|
|
414
|
+
* @param modelIdOrAlias - Model ID or alias name
|
|
415
|
+
* @returns Model info or undefined if not found
|
|
416
|
+
*/
|
|
417
|
+
declare function getModelInfo(modelIdOrAlias: string): QwenModelInfo | undefined;
|
|
418
|
+
/**
|
|
419
|
+
* Resolve model alias to actual model ID.
|
|
420
|
+
*
|
|
421
|
+
* @param modelIdOrAlias - Model ID or alias name
|
|
422
|
+
* @returns Resolved model ID or the original string if not found
|
|
423
|
+
*/
|
|
424
|
+
declare function resolveModelId(modelIdOrAlias: string): string;
|
|
425
|
+
/**
|
|
426
|
+
* Get all available model IDs (including aliases).
|
|
427
|
+
*/
|
|
428
|
+
declare function getAllModelIds(): string[];
|
|
429
|
+
/**
|
|
430
|
+
* Check if a model ID or alias is valid.
|
|
431
|
+
*/
|
|
432
|
+
declare function isValidModel(modelIdOrAlias: string): boolean;
|
|
433
|
+
/**
|
|
434
|
+
* Generate OpenCode provider configuration for Qwen Code models.
|
|
435
|
+
* Use this to configure models in opencode.json.
|
|
436
|
+
*/
|
|
437
|
+
declare function generateProviderConfig(): Record<string, unknown>;
|
|
438
|
+
|
|
439
|
+
declare const QWEN_OAUTH_BASE_URL = "https://chat.qwen.ai";
|
|
440
|
+
declare const QWEN_DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
441
|
+
declare const QWEN_INTL_BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1";
|
|
442
|
+
declare const QWEN_PORTAL_BASE_URL = "https://portal.qwen.ai/v1";
|
|
443
|
+
declare const QWEN_PROVIDER_ID = "qwencode";
|
|
444
|
+
|
|
445
|
+
export { type AuthDetails, type ChatCompletionRequest, type ChatCompletionResponse, type ChatMessage, DEFAULT_MODEL, MODEL_ALIASES, type OAuthAuthDetails, type PluginContext, type PluginResult, QWEN_CODE_MODELS, QWEN_DEFAULT_BASE_URL, QWEN_INTL_BASE_URL, QWEN_OAUTH_BASE_URL, QWEN_PORTAL_BASE_URL, QWEN_PROVIDER_ID, type QwenModelInfo, type QwenOAuthCredentials, QwenOAuthManager, QwenOAuthPlugin, type QwenTokenResponse, ThinkingBlockParser, type TokenExchangeResult, type Tool, type ToolCall, type ToolChoice, accessTokenExpired, createAuthDetails, QwenOAuthPlugin as default, ensureValidToken, extractThinkingContent, formatRefreshParts, generateProviderConfig, getAllModelIds, getModelInfo, getQwenCachedCredentialPath, hasQwenCredentials, hasThinkingBlocks, isOAuthAuth, isValidModel, needsTokenRefresh, parseRefreshParts, parseThinkingBlocks, refreshAccessToken, resolveModelId, stripThinkingBlocks };
|