opencode-qwen-cli-auth 2.3.4 → 2.3.5

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 CHANGED
@@ -50,10 +50,10 @@ The plugin stores each successful login in the multi-account store and can auto-
50
50
 
51
51
  ## Supported Models
52
52
 
53
- | Model | ID | Context | Max Output | Cost |
54
- |-------|-----|---------|------------|---------|
55
- | Qwen Coder (Qwen 3.5 Plus) | `coder-model` | 1M tokens | 65,536 tokens | Free |
56
- | Qwen VL Plus (Vision) | `vision-model` | 128K tokens | 8,192 tokens | Free |
53
+ | Model | ID | Input | Output | Context | Max Output | Cost |
54
+ |-------|-----|-------|--------|---------|------------|---------|
55
+ | Qwen Coder (Qwen 3.5 Plus) | `coder-model` | text | text | 1M tokens | 65,536 tokens | Free |
56
+ | Qwen VL Plus (Vision) | `vision-model` | text, image | text | 128K tokens | 8,192 tokens | Free |
57
57
 
58
58
  ## Configuration
59
59
 
@@ -124,7 +124,8 @@ When hitting a `429 insufficient_quota` error, the plugin automatically:
124
124
  1. **Marks current account exhausted** for cooldown window
125
125
  2. **Switches to next healthy account** and retries with same payload
126
126
  3. **Degrades payload** if no healthy account can be switched
127
- 4. **CLI fallback** (optional) - invokes `qwen` CLI if `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1` is set
127
+ 4. **CLI fallback** (optional) - invokes `qwen` CLI only for text-only payloads when `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1` is set
128
+ 5. **Multimodal safety guard** - skips CLI fallback for non-text parts (image/audio/file/video) to avoid semantic loss
128
129
 
129
130
  ### Token Expiration
130
131
 
package/README.vi.md CHANGED
@@ -50,10 +50,10 @@ Plugin sẽ lưu từng lần đăng nhập thành công vào kho đa tài kho
50
50
 
51
51
  ## Models hỗ trợ
52
52
 
53
- | Model | ID | Context | Max Output | Chi phí |
54
- |-------|-----|---------|------------|---------|
55
- | Qwen Coder (Qwen 3.5 Plus) | `coder-model` | 1M tokens | 65,536 tokens | Miễn phí |
56
- | Qwen VL Plus (Vision) | `vision-model` | 128K tokens | 8,192 tokens | Miễn phí |
53
+ | Model | ID | Input | Output | Context | Max Output | Chi phí |
54
+ |-------|-----|-------|--------|---------|------------|---------|
55
+ | Qwen Coder (Qwen 3.5 Plus) | `coder-model` | text | text | 1M tokens | 65,536 tokens | Miễn phí |
56
+ | Qwen VL Plus (Vision) | `vision-model` | text, image | text | 128K tokens | 8,192 tokens | Miễn phí |
57
57
 
58
58
  ## Cấu hình
59
59
 
@@ -124,7 +124,8 @@ Khi gặp lỗi `429 insufficient_quota`, plugin sẽ tự động:
124
124
  1. **Đánh dấu tài khoản hiện tại đã hết quota** trong cửa sổ cooldown
125
125
  2. **Đổi sang tài khoản khỏe tiếp theo** và retry với payload ban đầu
126
126
  3. **Degrade payload** nếu không còn tài khoản khỏe để đổi
127
- 4. **CLI fallback** (tùy chọn) - gọi `qwen` CLI nếu biến `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1` được bật
127
+ 4. **CLI fallback** (tùy chọn) - chỉ gọi `qwen` CLI cho payload chỉ có text khi bật `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1`
128
+ 5. **Guard multimodal an toàn** - bỏ qua CLI fallback khi payload có phần non-text (image/audio/file/video) để tránh mất ngữ nghĩa
128
129
 
129
130
  ### Token Hết Hạn
130
131
 
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * @license MIT with Usage Disclaimer (see LICENSE file)
13
13
  * @repository https://github.com/TVD-00/opencode-qwen-cli-auth
14
- * @version 2.2.9
14
+ * @version 2.3.5
15
15
  */
16
16
 
17
17
  import { randomUUID } from "node:crypto";
@@ -40,7 +40,7 @@ const CLI_FALLBACK_MAX_BUFFER_CHARS = 1024 * 1024;
40
40
  /** Enable CLI fallback feature via environment variable */
41
41
  const ENABLE_CLI_FALLBACK = process.env.OPENCODE_QWEN_ENABLE_CLI_FALLBACK === "1";
42
42
  /** User agent string for plugin identification */
43
- const PLUGIN_USER_AGENT = "opencode-qwen-cli-auth/2.2.1";
43
+ const PLUGIN_USER_AGENT = "opencode-qwen-cli-auth/2.3.4";
44
44
  /** Output token limits per model for DashScope OAuth */
45
45
  const DASH_SCOPE_OUTPUT_LIMITS = {
46
46
  "coder-model": 65536,
@@ -490,6 +490,54 @@ function extractMessageText(content) {
490
490
  return "";
491
491
  }).filter(Boolean).join("\n").trim();
492
492
  }
493
+
494
+ /**
495
+ * Checks whether content contains non-text parts
496
+ * @param {*} content - Message content
497
+ * @returns {boolean} True if any non-text part is present
498
+ */
499
+ function hasNonTextContentPart(content) {
500
+ if (typeof content === "string") {
501
+ return false;
502
+ }
503
+ if (Array.isArray(content)) {
504
+ return content.some((part) => {
505
+ if (typeof part === "string") {
506
+ return false;
507
+ }
508
+ if (!part || typeof part !== "object") {
509
+ return true;
510
+ }
511
+ if (typeof part.text === "string") {
512
+ return false;
513
+ }
514
+ const partType = typeof part.type === "string" ? part.type.toLowerCase() : "";
515
+ if (partType === "text" && typeof part.text === "string") {
516
+ return false;
517
+ }
518
+ return true;
519
+ });
520
+ }
521
+ if (content && typeof content === "object") {
522
+ return typeof content.text !== "string";
523
+ }
524
+ return false;
525
+ }
526
+
527
+ /**
528
+ * Checks whether payload contains any multimodal message content
529
+ * @param {Object} payload - Request payload
530
+ * @returns {boolean} True if payload contains non-text message parts
531
+ */
532
+ function payloadContainsNonTextMessages(payload) {
533
+ const messages = Array.isArray(payload?.messages) ? payload.messages : [];
534
+ for (const message of messages) {
535
+ if (hasNonTextContentPart(message?.content)) {
536
+ return true;
537
+ }
538
+ }
539
+ return false;
540
+ }
493
541
  /**
494
542
  * Builds prompt text from chat messages for CLI fallback
495
543
  * @param {Object} payload - Request payload with messages
@@ -691,6 +739,20 @@ function makeQwenCliCompletionResponse(model, content, context, streamMode) {
691
739
  async function runQwenCliFallback(payload, context, abortSignal) {
692
740
  const model = typeof payload?.model === "string" && payload.model.length > 0 ? payload.model : "coder-model";
693
741
  const streamMode = payload?.stream === true;
742
+ if (payloadContainsNonTextMessages(payload)) {
743
+ if (LOGGING_ENABLED) {
744
+ logWarn("Skipping qwen CLI fallback for multimodal payload", {
745
+ request_id: context.requestId,
746
+ sessionID: context.sessionID,
747
+ modelID: model,
748
+ accountID: context.accountID,
749
+ });
750
+ }
751
+ return {
752
+ ok: false,
753
+ reason: "cli_fallback_unsupported_multimodal_payload",
754
+ };
755
+ }
694
756
  const prompt = buildQwenCliPrompt(payload);
695
757
  const args = [prompt, "-o", "json", "--max-session-turns", "1", "--model", model];
696
758
  if (LOGGING_ENABLED) {
@@ -1341,6 +1403,7 @@ export const QwenAuthPlugin = async (_input) => {
1341
1403
  name: "Qwen Coder (Qwen 3.5 Plus)",
1342
1404
  // Qwen does not support reasoning_effort from OpenCode UI
1343
1405
  // Thinking is always enabled by default on server side (qwen3.5-plus)
1406
+ attachment: false,
1344
1407
  reasoning: false,
1345
1408
  limit: { context: 1048576, output: CHAT_MAX_TOKENS_CAP },
1346
1409
  cost: { input: 0, output: 0 },
@@ -1349,10 +1412,11 @@ export const QwenAuthPlugin = async (_input) => {
1349
1412
  "vision-model": {
1350
1413
  id: "vision-model",
1351
1414
  name: "Qwen VL Plus (vision)",
1415
+ attachment: true,
1352
1416
  reasoning: false,
1353
1417
  limit: { context: 131072, output: DASH_SCOPE_OUTPUT_LIMITS["vision-model"] },
1354
1418
  cost: { input: 0, output: 0 },
1355
- modalities: { input: ["text"], output: ["text"] },
1419
+ modalities: { input: ["text", "image"], output: ["text"] },
1356
1420
  },
1357
1421
  },
1358
1422
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-qwen-cli-auth",
3
- "version": "2.3.4",
3
+ "version": "2.3.5",
4
4
  "description": "Qwen OAuth authentication plugin for opencode - use your Qwen account instead of API keys",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",