opencode-qwen-cli-auth 2.2.5 → 2.2.6

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/dist/index.js +88 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const CHAT_REQUEST_TIMEOUT_MS = 30000;
17
17
  const CHAT_MAX_RETRIES = 0;
18
18
  const MAX_CONSECUTIVE_POLL_FAILURES = 3;
19
19
  const QUOTA_DEGRADE_MAX_TOKENS = 1024;
20
- const CLI_FALLBACK_TIMEOUT_MS = 45000;
20
+ const CLI_FALLBACK_TIMEOUT_MS = 8000;
21
21
  const CLI_FALLBACK_MAX_BUFFER_CHARS = 1024 * 1024;
22
22
  const PLUGIN_USER_AGENT = "opencode-qwen-cli-auth/2.2.1";
23
23
  const CLIENT_ONLY_BODY_FIELDS = new Set([
@@ -309,7 +309,10 @@ function extractQwenCliText(events) {
309
309
  }
310
310
  return null;
311
311
  }
312
- function makeQwenCliCompletionResponse(model, content, context) {
312
+ function createSseResponseChunk(data) {
313
+ return `data: ${JSON.stringify(data)}\n\n`;
314
+ }
315
+ function makeQwenCliCompletionResponse(model, content, context, streamMode) {
313
316
  if (LOGGING_ENABLED) {
314
317
  logInfo("Qwen CLI fallback returned completion", {
315
318
  request_id: context.requestId,
@@ -317,6 +320,51 @@ function makeQwenCliCompletionResponse(model, content, context) {
317
320
  modelID: model,
318
321
  });
319
322
  }
323
+ if (streamMode) {
324
+ const completionId = `chatcmpl-${randomUUID()}`;
325
+ const created = Math.floor(Date.now() / 1000);
326
+ const encoder = new TextEncoder();
327
+ const stream = new ReadableStream({
328
+ start(controller) {
329
+ controller.enqueue(encoder.encode(createSseResponseChunk({
330
+ id: completionId,
331
+ object: "chat.completion.chunk",
332
+ created,
333
+ model,
334
+ choices: [
335
+ {
336
+ index: 0,
337
+ delta: { role: "assistant", content },
338
+ finish_reason: null,
339
+ },
340
+ ],
341
+ })));
342
+ controller.enqueue(encoder.encode(createSseResponseChunk({
343
+ id: completionId,
344
+ object: "chat.completion.chunk",
345
+ created,
346
+ model,
347
+ choices: [
348
+ {
349
+ index: 0,
350
+ delta: {},
351
+ finish_reason: "stop",
352
+ },
353
+ ],
354
+ })));
355
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
356
+ controller.close();
357
+ },
358
+ });
359
+ return new Response(stream, {
360
+ status: 200,
361
+ headers: {
362
+ "content-type": "text/event-stream; charset=utf-8",
363
+ "cache-control": "no-cache",
364
+ "x-qwen-cli-fallback": "1",
365
+ },
366
+ });
367
+ }
320
368
  const body = {
321
369
  id: `chatcmpl-${randomUUID()}`,
322
370
  object: "chat.completion",
@@ -346,8 +394,9 @@ function makeQwenCliCompletionResponse(model, content, context) {
346
394
  },
347
395
  });
348
396
  }
349
- async function runQwenCliFallback(payload, context) {
397
+ async function runQwenCliFallback(payload, context, abortSignal) {
350
398
  const model = typeof payload?.model === "string" && payload.model.length > 0 ? payload.model : "coder-model";
399
+ const streamMode = payload?.stream === true;
351
400
  const prompt = buildQwenCliPrompt(payload);
352
401
  const args = [prompt, "-o", "json", "--max-session-turns", "1", "--model", model];
353
402
  if (LOGGING_ENABLED) {
@@ -363,6 +412,8 @@ async function runQwenCliFallback(payload, context) {
363
412
  let stdout = "";
364
413
  let stderr = "";
365
414
  let timer = null;
415
+ let child = undefined;
416
+ let abortHandler = undefined;
366
417
  const useShell = shouldUseShell(QWEN_CLI_COMMAND);
367
418
  const finalize = (result) => {
368
419
  if (settled) {
@@ -372,9 +423,18 @@ async function runQwenCliFallback(payload, context) {
372
423
  if (timer) {
373
424
  clearTimeout(timer);
374
425
  }
426
+ if (abortSignal && abortHandler) {
427
+ abortSignal.removeEventListener("abort", abortHandler);
428
+ }
375
429
  resolve(result);
376
430
  };
377
- let child;
431
+ if (abortSignal?.aborted) {
432
+ finalize({
433
+ ok: false,
434
+ reason: "cli_aborted",
435
+ });
436
+ return;
437
+ }
378
438
  try {
379
439
  child = spawn(QWEN_CLI_COMMAND, args, {
380
440
  shell: useShell,
@@ -389,6 +449,20 @@ async function runQwenCliFallback(payload, context) {
389
449
  });
390
450
  return;
391
451
  }
452
+ if (abortSignal) {
453
+ abortHandler = () => {
454
+ try {
455
+ child?.kill();
456
+ }
457
+ catch (_killError) {
458
+ }
459
+ finalize({
460
+ ok: false,
461
+ reason: "cli_aborted",
462
+ });
463
+ };
464
+ abortSignal.addEventListener("abort", abortHandler, { once: true });
465
+ }
392
466
  timer = setTimeout(() => {
393
467
  try {
394
468
  child.kill();
@@ -418,7 +492,7 @@ async function runQwenCliFallback(payload, context) {
418
492
  if (content) {
419
493
  finalize({
420
494
  ok: true,
421
- response: makeQwenCliCompletionResponse(model, content, context),
495
+ response: makeQwenCliCompletionResponse(model, content, context, streamMode),
422
496
  });
423
497
  return;
424
498
  }
@@ -470,6 +544,7 @@ async function sendWithTimeout(input, requestInit) {
470
544
  }
471
545
  async function failFastFetch(input, init) {
472
546
  const requestInit = init ? { ...init } : {};
547
+ const sourceSignal = requestInit.signal;
473
548
  const rawPayload = parseJsonRequestBody(requestInit);
474
549
  const sessionID = typeof rawPayload?.sessionID === "string" ? rawPayload.sessionID : undefined;
475
550
  let payload = rawPayload;
@@ -532,10 +607,13 @@ async function failFastFetch(input, init) {
532
607
  return response;
533
608
  }
534
609
  const fallbackBody = await response.text().catch(() => "");
535
- const cliFallback = await runQwenCliFallback(payload, context);
610
+ const cliFallback = await runQwenCliFallback(payload, context, sourceSignal);
536
611
  if (cliFallback.ok) {
537
612
  return cliFallback.response;
538
613
  }
614
+ if (cliFallback.reason === "cli_aborted") {
615
+ return makeFailFastErrorResponse(400, "request_aborted", "Qwen request was aborted");
616
+ }
539
617
  if (LOGGING_ENABLED) {
540
618
  logWarn("Qwen CLI fallback failed", {
541
619
  request_id: context.requestId,
@@ -547,10 +625,13 @@ async function failFastFetch(input, init) {
547
625
  }
548
626
  return makeQuotaFailFastResponse(fallbackBody, response.headers, context);
549
627
  }
550
- const cliFallback = await runQwenCliFallback(payload, context);
628
+ const cliFallback = await runQwenCliFallback(payload, context, sourceSignal);
551
629
  if (cliFallback.ok) {
552
630
  return cliFallback.response;
553
631
  }
632
+ if (cliFallback.reason === "cli_aborted") {
633
+ return makeFailFastErrorResponse(400, "request_aborted", "Qwen request was aborted");
634
+ }
554
635
  if (LOGGING_ENABLED) {
555
636
  logWarn("Qwen CLI fallback failed", {
556
637
  request_id: context.requestId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-qwen-cli-auth",
3
- "version": "2.2.5",
3
+ "version": "2.2.6",
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",