opencode-qwen-cli-auth 2.3.1 → 2.3.3
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 +1 -1
- package/README.vi.md +1 -1
- package/dist/index.js +62 -43
- package/dist/lib/auth/auth.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ The plugin stores each successful login in the multi-account store and can auto-
|
|
|
67
67
|
| `ENABLE_PLUGIN_REQUEST_LOGGING=1` | Enable request logging to file | Optional |
|
|
68
68
|
| `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1` | Enable CLI fallback on quota error | Optional |
|
|
69
69
|
| `OPENCODE_QWEN_ACCOUNTS_PATH` | Override multi-account store path (must be inside `~/.qwen`) | Optional |
|
|
70
|
-
| `OPENCODE_QWEN_QUOTA_COOLDOWN_MS` | Cooldown for exhausted accounts | Default: `
|
|
70
|
+
| `OPENCODE_QWEN_QUOTA_COOLDOWN_MS` | Cooldown for exhausted accounts | Default: `86400000` (24h) |
|
|
71
71
|
|
|
72
72
|
### Debug & Logging
|
|
73
73
|
|
package/README.vi.md
CHANGED
|
@@ -67,7 +67,7 @@ Plugin sẽ lưu từng lần đăng nhập thành công vào kho đa tài kho
|
|
|
67
67
|
| `ENABLE_PLUGIN_REQUEST_LOGGING=1` | Bật ghi log request ra file | Tùy chọn |
|
|
68
68
|
| `OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1` | Bật tính năng gọi CLI khi hết quota | Tùy chọn |
|
|
69
69
|
| `OPENCODE_QWEN_ACCOUNTS_PATH` | Ghi đè đường dẫn kho đa tài khoản (phải nằm trong `~/.qwen`) | Tùy chọn |
|
|
70
|
-
| `OPENCODE_QWEN_QUOTA_COOLDOWN_MS` | Thời gian cooldown cho tài khoản đã hết quota | Mặc định: `
|
|
70
|
+
| `OPENCODE_QWEN_QUOTA_COOLDOWN_MS` | Thời gian cooldown cho tài khoản đã hết quota | Mặc định: `86400000` (24 giờ) |
|
|
71
71
|
|
|
72
72
|
### Debug & Logging
|
|
73
73
|
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { PROVIDER_ID, AUTH_LABELS, DEVICE_FLOW, PORTAL_HEADERS } from "./lib/con
|
|
|
22
22
|
import { logError, logInfo, logWarn, LOGGING_ENABLED } from "./lib/logger.js";
|
|
23
23
|
|
|
24
24
|
/** Request timeout for chat completions in milliseconds */
|
|
25
|
-
const CHAT_REQUEST_TIMEOUT_MS =
|
|
25
|
+
const CHAT_REQUEST_TIMEOUT_MS = 120000;
|
|
26
26
|
/** Maximum number of retry attempts for failed requests */
|
|
27
27
|
const CHAT_MAX_RETRIES = 3;
|
|
28
28
|
/** Output token cap for coder-model (64K tokens) */
|
|
@@ -415,19 +415,19 @@ function sanitizeOutgoingPayload(payload) {
|
|
|
415
415
|
function createQuotaDegradedPayload(payload) {
|
|
416
416
|
const degraded = { ...payload };
|
|
417
417
|
let changed = false;
|
|
418
|
-
// Remove tool-related fields
|
|
419
|
-
if ("tools" in degraded) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
if ("tool_choice" in degraded) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if ("parallel_tool_calls" in degraded) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
418
|
+
// Remove tool-related fields (skip removing tools so Agents don't break)
|
|
419
|
+
// if ("tools" in degraded) {
|
|
420
|
+
// delete degraded.tools;
|
|
421
|
+
// changed = true;
|
|
422
|
+
// }
|
|
423
|
+
// if ("tool_choice" in degraded) {
|
|
424
|
+
// delete degraded.tool_choice;
|
|
425
|
+
// changed = true;
|
|
426
|
+
// }
|
|
427
|
+
// if ("parallel_tool_calls" in degraded) {
|
|
428
|
+
// delete degraded.parallel_tool_calls;
|
|
429
|
+
// changed = true;
|
|
430
|
+
// }
|
|
431
431
|
// Disable streaming
|
|
432
432
|
if (degraded.stream !== false) {
|
|
433
433
|
degraded.stream = false;
|
|
@@ -595,7 +595,7 @@ function createSseResponseChunk(data) {
|
|
|
595
595
|
* @param {boolean} streamMode - Whether to return streaming response
|
|
596
596
|
* @returns {Response} Formatted completion response
|
|
597
597
|
*/
|
|
598
|
-
function makeQwenCliCompletionResponse(model, content, context, streamMode) {
|
|
598
|
+
function makeQwenCliCompletionResponse(model, content, context, streamMode, abortSignal) {
|
|
599
599
|
if (LOGGING_ENABLED) {
|
|
600
600
|
logInfo("Qwen CLI fallback returned completion", {
|
|
601
601
|
request_id: context.requestId,
|
|
@@ -609,7 +609,7 @@ function makeQwenCliCompletionResponse(model, content, context, streamMode) {
|
|
|
609
609
|
const encoder = new TextEncoder();
|
|
610
610
|
const stream = new ReadableStream({
|
|
611
611
|
start(controller) {
|
|
612
|
-
// Send first chunk with content
|
|
612
|
+
// Send first chunk with empty content
|
|
613
613
|
controller.enqueue(encoder.encode(createSseResponseChunk({
|
|
614
614
|
id: completionId,
|
|
615
615
|
object: "chat.completion.chunk",
|
|
@@ -618,28 +618,51 @@ function makeQwenCliCompletionResponse(model, content, context, streamMode) {
|
|
|
618
618
|
choices: [
|
|
619
619
|
{
|
|
620
620
|
index: 0,
|
|
621
|
-
delta: { role: "assistant", content },
|
|
621
|
+
delta: { role: "assistant", content: "" },
|
|
622
622
|
finish_reason: null,
|
|
623
623
|
},
|
|
624
624
|
],
|
|
625
625
|
})));
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
{
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
626
|
+
|
|
627
|
+
const CHUNK_SIZE = 15;
|
|
628
|
+
const DELAY_MS = 20;
|
|
629
|
+
let position = 0;
|
|
630
|
+
|
|
631
|
+
function pushNextChunk() {
|
|
632
|
+
if (abortSignal?.aborted) {
|
|
633
|
+
try { controller.close(); } catch (e) { }
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (position >= content.length) {
|
|
638
|
+
// Send stop chunk
|
|
639
|
+
controller.enqueue(encoder.encode(createSseResponseChunk({
|
|
640
|
+
id: completionId,
|
|
641
|
+
object: "chat.completion.chunk",
|
|
642
|
+
created,
|
|
643
|
+
model,
|
|
644
|
+
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
|
645
|
+
})));
|
|
646
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
647
|
+
try { controller.close(); } catch (e) { }
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const nextSlice = content.slice(position, position + CHUNK_SIZE);
|
|
652
|
+
position += CHUNK_SIZE;
|
|
653
|
+
|
|
654
|
+
controller.enqueue(encoder.encode(createSseResponseChunk({
|
|
655
|
+
id: completionId,
|
|
656
|
+
object: "chat.completion.chunk",
|
|
657
|
+
created,
|
|
658
|
+
model,
|
|
659
|
+
choices: [{ index: 0, delta: { content: nextSlice }, finish_reason: null }],
|
|
660
|
+
})));
|
|
661
|
+
|
|
662
|
+
setTimeout(pushNextChunk, DELAY_MS);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
pushNextChunk();
|
|
643
666
|
},
|
|
644
667
|
});
|
|
645
668
|
return new Response(stream, {
|
|
@@ -701,12 +724,8 @@ async function runQwenCliFallback(payload, context, abortSignal) {
|
|
|
701
724
|
command: QWEN_CLI_COMMAND,
|
|
702
725
|
});
|
|
703
726
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
ok: false,
|
|
707
|
-
reason: "cli_shell_execution_blocked_for_security",
|
|
708
|
-
};
|
|
709
|
-
}
|
|
727
|
+
// Use secure spawn logic across ALL OS, allowing .cmd locally on Windows by injecting shell correctly.
|
|
728
|
+
const isShellRequired = requiresShellExecution(QWEN_CLI_COMMAND);
|
|
710
729
|
return await new Promise((resolve) => {
|
|
711
730
|
let settled = false;
|
|
712
731
|
let stdout = "";
|
|
@@ -736,7 +755,7 @@ async function runQwenCliFallback(payload, context, abortSignal) {
|
|
|
736
755
|
}
|
|
737
756
|
try {
|
|
738
757
|
child = spawn(QWEN_CLI_COMMAND, args, {
|
|
739
|
-
shell:
|
|
758
|
+
shell: isShellRequired,
|
|
740
759
|
windowsHide: true,
|
|
741
760
|
stdio: ["ignore", "pipe", "pipe"],
|
|
742
761
|
});
|
|
@@ -791,7 +810,7 @@ async function runQwenCliFallback(payload, context, abortSignal) {
|
|
|
791
810
|
if (content) {
|
|
792
811
|
finalize({
|
|
793
812
|
ok: true,
|
|
794
|
-
response: makeQwenCliCompletionResponse(model, content, context, streamMode),
|
|
813
|
+
response: makeQwenCliCompletionResponse(model, content, context, streamMode, abortSignal),
|
|
795
814
|
});
|
|
796
815
|
return;
|
|
797
816
|
}
|
|
@@ -964,7 +983,7 @@ async function failFastFetch(input, init) {
|
|
|
964
983
|
attempt: retryAttempt + 1,
|
|
965
984
|
});
|
|
966
985
|
}
|
|
967
|
-
const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];
|
|
986
|
+
const RETRYABLE_STATUS_CODES = [429, 500, 502, 503, 504];
|
|
968
987
|
if (RETRYABLE_STATUS_CODES.includes(response.status)) {
|
|
969
988
|
if (response.status === 429) {
|
|
970
989
|
const firstBody = await response.text().catch(() => "");
|
package/dist/lib/auth/auth.js
CHANGED
|
@@ -30,7 +30,7 @@ const LOCK_MAX_ATTEMPTS = 20;
|
|
|
30
30
|
/** Account schema version for ~/.qwen/oauth_accounts.json */
|
|
31
31
|
const ACCOUNT_STORE_VERSION = 1;
|
|
32
32
|
/** Default cooldown when account hits insufficient_quota */
|
|
33
|
-
const DEFAULT_QUOTA_COOLDOWN_MS =
|
|
33
|
+
const DEFAULT_QUOTA_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Checks if an error is an AbortError (from AbortController)
|
package/package.json
CHANGED