opencode-qwen-cli-auth 2.2.5 → 2.2.7
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.js +115 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,10 +15,12 @@ import { PROVIDER_ID, AUTH_LABELS, DEVICE_FLOW, PORTAL_HEADERS } from "./lib/con
|
|
|
15
15
|
import { logError, logInfo, logWarn, LOGGING_ENABLED } from "./lib/logger.js";
|
|
16
16
|
const CHAT_REQUEST_TIMEOUT_MS = 30000;
|
|
17
17
|
const CHAT_MAX_RETRIES = 0;
|
|
18
|
+
const CHAT_MAX_TOKENS_CAP = 2048;
|
|
18
19
|
const MAX_CONSECUTIVE_POLL_FAILURES = 3;
|
|
19
20
|
const QUOTA_DEGRADE_MAX_TOKENS = 1024;
|
|
20
|
-
const CLI_FALLBACK_TIMEOUT_MS =
|
|
21
|
+
const CLI_FALLBACK_TIMEOUT_MS = 8000;
|
|
21
22
|
const CLI_FALLBACK_MAX_BUFFER_CHARS = 1024 * 1024;
|
|
23
|
+
const ENABLE_CLI_FALLBACK = process.env.OPENCODE_QWEN_ENABLE_CLI_FALLBACK === "1";
|
|
22
24
|
const PLUGIN_USER_AGENT = "opencode-qwen-cli-auth/2.2.1";
|
|
23
25
|
const CLIENT_ONLY_BODY_FIELDS = new Set([
|
|
24
26
|
"providerID",
|
|
@@ -178,6 +180,14 @@ function sanitizeOutgoingPayload(payload) {
|
|
|
178
180
|
delete sanitized.stream_options;
|
|
179
181
|
changed = true;
|
|
180
182
|
}
|
|
183
|
+
if (typeof sanitized.max_tokens === "number" && sanitized.max_tokens > CHAT_MAX_TOKENS_CAP) {
|
|
184
|
+
sanitized.max_tokens = CHAT_MAX_TOKENS_CAP;
|
|
185
|
+
changed = true;
|
|
186
|
+
}
|
|
187
|
+
if (typeof sanitized.max_completion_tokens === "number" && sanitized.max_completion_tokens > CHAT_MAX_TOKENS_CAP) {
|
|
188
|
+
sanitized.max_completion_tokens = CHAT_MAX_TOKENS_CAP;
|
|
189
|
+
changed = true;
|
|
190
|
+
}
|
|
181
191
|
return changed ? sanitized : payload;
|
|
182
192
|
}
|
|
183
193
|
function createQuotaDegradedPayload(payload) {
|
|
@@ -309,7 +319,10 @@ function extractQwenCliText(events) {
|
|
|
309
319
|
}
|
|
310
320
|
return null;
|
|
311
321
|
}
|
|
312
|
-
function
|
|
322
|
+
function createSseResponseChunk(data) {
|
|
323
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
324
|
+
}
|
|
325
|
+
function makeQwenCliCompletionResponse(model, content, context, streamMode) {
|
|
313
326
|
if (LOGGING_ENABLED) {
|
|
314
327
|
logInfo("Qwen CLI fallback returned completion", {
|
|
315
328
|
request_id: context.requestId,
|
|
@@ -317,6 +330,51 @@ function makeQwenCliCompletionResponse(model, content, context) {
|
|
|
317
330
|
modelID: model,
|
|
318
331
|
});
|
|
319
332
|
}
|
|
333
|
+
if (streamMode) {
|
|
334
|
+
const completionId = `chatcmpl-${randomUUID()}`;
|
|
335
|
+
const created = Math.floor(Date.now() / 1000);
|
|
336
|
+
const encoder = new TextEncoder();
|
|
337
|
+
const stream = new ReadableStream({
|
|
338
|
+
start(controller) {
|
|
339
|
+
controller.enqueue(encoder.encode(createSseResponseChunk({
|
|
340
|
+
id: completionId,
|
|
341
|
+
object: "chat.completion.chunk",
|
|
342
|
+
created,
|
|
343
|
+
model,
|
|
344
|
+
choices: [
|
|
345
|
+
{
|
|
346
|
+
index: 0,
|
|
347
|
+
delta: { role: "assistant", content },
|
|
348
|
+
finish_reason: null,
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
})));
|
|
352
|
+
controller.enqueue(encoder.encode(createSseResponseChunk({
|
|
353
|
+
id: completionId,
|
|
354
|
+
object: "chat.completion.chunk",
|
|
355
|
+
created,
|
|
356
|
+
model,
|
|
357
|
+
choices: [
|
|
358
|
+
{
|
|
359
|
+
index: 0,
|
|
360
|
+
delta: {},
|
|
361
|
+
finish_reason: "stop",
|
|
362
|
+
},
|
|
363
|
+
],
|
|
364
|
+
})));
|
|
365
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
366
|
+
controller.close();
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
return new Response(stream, {
|
|
370
|
+
status: 200,
|
|
371
|
+
headers: {
|
|
372
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
373
|
+
"cache-control": "no-cache",
|
|
374
|
+
"x-qwen-cli-fallback": "1",
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
}
|
|
320
378
|
const body = {
|
|
321
379
|
id: `chatcmpl-${randomUUID()}`,
|
|
322
380
|
object: "chat.completion",
|
|
@@ -346,8 +404,9 @@ function makeQwenCliCompletionResponse(model, content, context) {
|
|
|
346
404
|
},
|
|
347
405
|
});
|
|
348
406
|
}
|
|
349
|
-
async function runQwenCliFallback(payload, context) {
|
|
407
|
+
async function runQwenCliFallback(payload, context, abortSignal) {
|
|
350
408
|
const model = typeof payload?.model === "string" && payload.model.length > 0 ? payload.model : "coder-model";
|
|
409
|
+
const streamMode = payload?.stream === true;
|
|
351
410
|
const prompt = buildQwenCliPrompt(payload);
|
|
352
411
|
const args = [prompt, "-o", "json", "--max-session-turns", "1", "--model", model];
|
|
353
412
|
if (LOGGING_ENABLED) {
|
|
@@ -363,6 +422,8 @@ async function runQwenCliFallback(payload, context) {
|
|
|
363
422
|
let stdout = "";
|
|
364
423
|
let stderr = "";
|
|
365
424
|
let timer = null;
|
|
425
|
+
let child = undefined;
|
|
426
|
+
let abortHandler = undefined;
|
|
366
427
|
const useShell = shouldUseShell(QWEN_CLI_COMMAND);
|
|
367
428
|
const finalize = (result) => {
|
|
368
429
|
if (settled) {
|
|
@@ -372,9 +433,18 @@ async function runQwenCliFallback(payload, context) {
|
|
|
372
433
|
if (timer) {
|
|
373
434
|
clearTimeout(timer);
|
|
374
435
|
}
|
|
436
|
+
if (abortSignal && abortHandler) {
|
|
437
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
438
|
+
}
|
|
375
439
|
resolve(result);
|
|
376
440
|
};
|
|
377
|
-
|
|
441
|
+
if (abortSignal?.aborted) {
|
|
442
|
+
finalize({
|
|
443
|
+
ok: false,
|
|
444
|
+
reason: "cli_aborted",
|
|
445
|
+
});
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
378
448
|
try {
|
|
379
449
|
child = spawn(QWEN_CLI_COMMAND, args, {
|
|
380
450
|
shell: useShell,
|
|
@@ -389,6 +459,20 @@ async function runQwenCliFallback(payload, context) {
|
|
|
389
459
|
});
|
|
390
460
|
return;
|
|
391
461
|
}
|
|
462
|
+
if (abortSignal) {
|
|
463
|
+
abortHandler = () => {
|
|
464
|
+
try {
|
|
465
|
+
child?.kill();
|
|
466
|
+
}
|
|
467
|
+
catch (_killError) {
|
|
468
|
+
}
|
|
469
|
+
finalize({
|
|
470
|
+
ok: false,
|
|
471
|
+
reason: "cli_aborted",
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
475
|
+
}
|
|
392
476
|
timer = setTimeout(() => {
|
|
393
477
|
try {
|
|
394
478
|
child.kill();
|
|
@@ -418,7 +502,7 @@ async function runQwenCliFallback(payload, context) {
|
|
|
418
502
|
if (content) {
|
|
419
503
|
finalize({
|
|
420
504
|
ok: true,
|
|
421
|
-
response: makeQwenCliCompletionResponse(model, content, context),
|
|
505
|
+
response: makeQwenCliCompletionResponse(model, content, context, streamMode),
|
|
422
506
|
});
|
|
423
507
|
return;
|
|
424
508
|
}
|
|
@@ -470,6 +554,7 @@ async function sendWithTimeout(input, requestInit) {
|
|
|
470
554
|
}
|
|
471
555
|
async function failFastFetch(input, init) {
|
|
472
556
|
const requestInit = init ? { ...init } : {};
|
|
557
|
+
const sourceSignal = requestInit.signal;
|
|
473
558
|
const rawPayload = parseJsonRequestBody(requestInit);
|
|
474
559
|
const sessionID = typeof rawPayload?.sessionID === "string" ? rawPayload.sessionID : undefined;
|
|
475
560
|
let payload = rawPayload;
|
|
@@ -532,10 +617,34 @@ async function failFastFetch(input, init) {
|
|
|
532
617
|
return response;
|
|
533
618
|
}
|
|
534
619
|
const fallbackBody = await response.text().catch(() => "");
|
|
535
|
-
|
|
620
|
+
if (ENABLE_CLI_FALLBACK) {
|
|
621
|
+
const cliFallback = await runQwenCliFallback(payload, context, sourceSignal);
|
|
622
|
+
if (cliFallback.ok) {
|
|
623
|
+
return cliFallback.response;
|
|
624
|
+
}
|
|
625
|
+
if (cliFallback.reason === "cli_aborted") {
|
|
626
|
+
return makeFailFastErrorResponse(400, "request_aborted", "Qwen request was aborted");
|
|
627
|
+
}
|
|
628
|
+
if (LOGGING_ENABLED) {
|
|
629
|
+
logWarn("Qwen CLI fallback failed", {
|
|
630
|
+
request_id: context.requestId,
|
|
631
|
+
sessionID: context.sessionID,
|
|
632
|
+
modelID: context.modelID,
|
|
633
|
+
reason: cliFallback.reason,
|
|
634
|
+
stderr: cliFallback.stderr,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return makeQuotaFailFastResponse(fallbackBody, response.headers, context);
|
|
639
|
+
}
|
|
640
|
+
if (ENABLE_CLI_FALLBACK) {
|
|
641
|
+
const cliFallback = await runQwenCliFallback(payload, context, sourceSignal);
|
|
536
642
|
if (cliFallback.ok) {
|
|
537
643
|
return cliFallback.response;
|
|
538
644
|
}
|
|
645
|
+
if (cliFallback.reason === "cli_aborted") {
|
|
646
|
+
return makeFailFastErrorResponse(400, "request_aborted", "Qwen request was aborted");
|
|
647
|
+
}
|
|
539
648
|
if (LOGGING_ENABLED) {
|
|
540
649
|
logWarn("Qwen CLI fallback failed", {
|
|
541
650
|
request_id: context.requestId,
|
|
@@ -545,20 +654,6 @@ async function failFastFetch(input, init) {
|
|
|
545
654
|
stderr: cliFallback.stderr,
|
|
546
655
|
});
|
|
547
656
|
}
|
|
548
|
-
return makeQuotaFailFastResponse(fallbackBody, response.headers, context);
|
|
549
|
-
}
|
|
550
|
-
const cliFallback = await runQwenCliFallback(payload, context);
|
|
551
|
-
if (cliFallback.ok) {
|
|
552
|
-
return cliFallback.response;
|
|
553
|
-
}
|
|
554
|
-
if (LOGGING_ENABLED) {
|
|
555
|
-
logWarn("Qwen CLI fallback failed", {
|
|
556
|
-
request_id: context.requestId,
|
|
557
|
-
sessionID: context.sessionID,
|
|
558
|
-
modelID: context.modelID,
|
|
559
|
-
reason: cliFallback.reason,
|
|
560
|
-
stderr: cliFallback.stderr,
|
|
561
|
-
});
|
|
562
657
|
}
|
|
563
658
|
}
|
|
564
659
|
return makeQuotaFailFastResponse(firstBody, response.headers, context);
|
package/package.json
CHANGED