opencode-qwen-cli-auth 2.3.8 → 2.3.9

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 CHANGED
@@ -1397,6 +1397,114 @@ export const QwenAuthPlugin = async (_input) => {
1397
1397
  };
1398
1398
  },
1399
1399
  },
1400
+ {
1401
+ label: "Add another Qwen account (multi-account switch)",
1402
+ type: "oauth",
1403
+ /**
1404
+ * Them account Qwen phu de auto-switch khi account chinh het quota.
1405
+ * Luon tao account moi (forceNew), khong ghi de account cu.
1406
+ * Account moi khong duoc dat active ngay (setActive: false).
1407
+ */
1408
+ authorize: async () => {
1409
+ const pkce = await createPKCE();
1410
+ const deviceAuth = await requestDeviceCode(pkce);
1411
+ if (!deviceAuth) {
1412
+ throw new Error("Failed to request device code");
1413
+ }
1414
+ console.log(`\n[Add Account] Please visit: ${deviceAuth.verification_uri}`);
1415
+ console.log(`[Add Account] Enter code: ${deviceAuth.user_code}\n`);
1416
+ const verificationUrl = deviceAuth.verification_uri_complete || deviceAuth.verification_uri;
1417
+ return {
1418
+ url: verificationUrl,
1419
+ method: "auto",
1420
+ instructions: "Login with a DIFFERENT Qwen account to add it as backup for auto-switch.",
1421
+ callback: async () => {
1422
+ let pollInterval = (deviceAuth.interval || 5) * 1000;
1423
+ const POLLING_MARGIN_MS = 3000;
1424
+ const maxInterval = DEVICE_FLOW.MAX_POLL_INTERVAL;
1425
+ const startTime = Date.now();
1426
+ const expiresIn = deviceAuth.expires_in * 1000;
1427
+ let consecutivePollFailures = 0;
1428
+ while (Date.now() - startTime < expiresIn) {
1429
+ await new Promise(resolve => setTimeout(resolve, pollInterval + POLLING_MARGIN_MS));
1430
+ const result = await pollForToken(deviceAuth.device_code, pkce.verifier);
1431
+ if (result.type === "success") {
1432
+ // Luu vao legacy token file de loader co the doc
1433
+ saveToken(result);
1434
+ // forceNew: luon tao account moi, khong match account cu
1435
+ // setActive: false - giu account hien tai, chi them du phong
1436
+ const savedAccount = await upsertOAuthAccount(result, {
1437
+ setActive: false,
1438
+ forceNew: true,
1439
+ });
1440
+ if (LOGGING_ENABLED) {
1441
+ logInfo("Added new backup Qwen account", {
1442
+ accountId: savedAccount?.accountId,
1443
+ totalAccounts: savedAccount?.totalAccountCount,
1444
+ healthyAccounts: savedAccount?.healthyAccountCount,
1445
+ });
1446
+ }
1447
+ console.log(`[Add Account] Success! Account added. Total accounts: ${savedAccount?.totalAccountCount || "?"}`);
1448
+ // Khoi phuc legacy token file (oauth_creds.json) ve active account
1449
+ // de loader doc dung token cua account chinh, khong dung token account moi
1450
+ try {
1451
+ const activeAcct = await getActiveOAuthAccount({ allowExhausted: true });
1452
+ if (activeAcct?.accessToken) {
1453
+ // getActiveOAuthAccount da tu dong sync vao oauth_creds.json
1454
+ // nen khong can goi saveToken thu cong
1455
+ }
1456
+ } catch (_restoreError) {
1457
+ // Khong anh huong chuc nang chinh
1458
+ }
1459
+ return {
1460
+ type: "success",
1461
+ access: result.access,
1462
+ refresh: result.refresh,
1463
+ expires: result.expires,
1464
+ };
1465
+ }
1466
+ if (result.type === "slow_down") {
1467
+ consecutivePollFailures = 0;
1468
+ pollInterval = Math.min(pollInterval + 5000, maxInterval);
1469
+ continue;
1470
+ }
1471
+ if (result.type === "pending") {
1472
+ consecutivePollFailures = 0;
1473
+ continue;
1474
+ }
1475
+ if (result.type === "failed") {
1476
+ if (result.fatal) {
1477
+ logError("OAuth token polling failed with fatal error (add-account)", {
1478
+ status: result.status,
1479
+ error: result.error,
1480
+ description: result.description,
1481
+ });
1482
+ return { type: "failed" };
1483
+ }
1484
+ consecutivePollFailures += 1;
1485
+ logWarn(`OAuth token polling failed (add-account) (${consecutivePollFailures}/${MAX_CONSECUTIVE_POLL_FAILURES})`);
1486
+ if (consecutivePollFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {
1487
+ console.error("[qwen-oauth-plugin] OAuth token polling failed repeatedly (add-account)");
1488
+ return { type: "failed" };
1489
+ }
1490
+ continue;
1491
+ }
1492
+ if (result.type === "denied") {
1493
+ console.error("[qwen-oauth-plugin] Device authorization was denied (add-account)");
1494
+ return { type: "failed" };
1495
+ }
1496
+ if (result.type === "expired") {
1497
+ console.error("[qwen-oauth-plugin] Device authorization code expired (add-account)");
1498
+ return { type: "failed" };
1499
+ }
1500
+ return { type: "failed" };
1501
+ }
1502
+ console.error("[qwen-oauth-plugin] Device authorization timed out (add-account)");
1503
+ return { type: "failed" };
1504
+ },
1505
+ };
1506
+ },
1507
+ },
1400
1508
  ],
1401
1509
  },
1402
1510
  /**
@@ -45,6 +45,7 @@ export declare function upsertOAuthAccount(tokenResult: TokenResult, options?: {
45
45
  accountId?: string;
46
46
  accountKey?: string;
47
47
  setActive?: boolean;
48
+ forceNew?: boolean;
48
49
  }): Promise<{
49
50
  accountId: string;
50
51
  accessToken: string;
@@ -1056,14 +1056,17 @@ export async function upsertOAuthAccount(tokenResult, options = {}) {
1056
1056
  await withAccountsStoreLock((store) => {
1057
1057
  const now = Date.now();
1058
1058
  let index = -1;
1059
- if (typeof options.accountId === "string" && options.accountId.length > 0) {
1060
- index = store.accounts.findIndex(account => account.id === options.accountId);
1061
- }
1062
- if (index < 0 && accountKey) {
1063
- index = store.accounts.findIndex(account => account.accountKey === accountKey);
1064
- }
1065
- if (index < 0) {
1066
- index = store.accounts.findIndex(account => account.token?.refresh_token === tokenData.refresh_token);
1059
+ // forceNew: bo qua match, luon tao account moi (dung cho "Add another account")
1060
+ if (!options.forceNew) {
1061
+ if (typeof options.accountId === "string" && options.accountId.length > 0) {
1062
+ index = store.accounts.findIndex(account => account.id === options.accountId);
1063
+ }
1064
+ if (index < 0 && accountKey) {
1065
+ index = store.accounts.findIndex(account => account.accountKey === accountKey);
1066
+ }
1067
+ if (index < 0) {
1068
+ index = store.accounts.findIndex(account => account.token?.refresh_token === tokenData.refresh_token);
1069
+ }
1067
1070
  }
1068
1071
  if (index < 0) {
1069
1072
  const newId = typeof options.accountId === "string" && options.accountId.length > 0
@@ -1105,7 +1108,7 @@ export async function getActiveOAuthAccount(options = {}) {
1105
1108
  ? options.preferredAccountId
1106
1109
  : null;
1107
1110
  const attemptedAuthRejected = new Set();
1108
- for (;;) {
1111
+ for (; ;) {
1109
1112
  const lockPath = await acquireAccountsLock();
1110
1113
  let selected = null;
1111
1114
  let dirty = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-qwen-cli-auth",
3
- "version": "2.3.8",
3
+ "version": "2.3.9",
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",