qwenproxy-cli 1.6.1 → 1.6.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/dist/login.js CHANGED
@@ -826,7 +826,7 @@ function addAccountsBatch(entries) {
826
826
  }
827
827
  return { added, skipped, invalid };
828
828
  }
829
- function addAccount(email, password, id) {
829
+ function addAccount(email, password = "", id) {
830
830
  if (!email || typeof email !== "string" || email.trim().length === 0) {
831
831
  throw new Error("Email is required");
832
832
  }
@@ -3182,6 +3182,7 @@ var playwright_exports = {};
3182
3182
  __export(playwright_exports, {
3183
3183
  assertAntiBotHeaders: () => assertAntiBotHeaders,
3184
3184
  buildChromiumLaunchArgs: () => buildChromiumLaunchArgs,
3185
+ cancelEmailOtpLogin: () => cancelEmailOtpLogin,
3185
3186
  captureQwenHeaders: () => captureQwenHeaders,
3186
3187
  classifyQwenAuthError: () => classifyQwenAuthError,
3187
3188
  cleanupOrphanProfiles: () => cleanupOrphanProfiles,
@@ -3222,8 +3223,10 @@ __export(playwright_exports, {
3222
3223
  removePlaywrightProfile: () => removePlaywrightProfile,
3223
3224
  saveStorageState: () => saveStorageState,
3224
3225
  schedulePlaywrightProfileReset: () => schedulePlaywrightProfileReset,
3226
+ startEmailOtpLogin: () => startEmailOtpLogin,
3225
3227
  unregisterPlaywrightAccountForTests: () => unregisterPlaywrightAccountForTests,
3226
3228
  validateAccountLogin: () => validateAccountLogin,
3229
+ verifyEmailOtpLogin: () => verifyEmailOtpLogin,
3227
3230
  withAccountPage: () => withAccountPage
3228
3231
  });
3229
3232
  import { chromium as chromium2 } from "patchright";
@@ -3565,6 +3568,10 @@ async function clearVisibleChallenge(page) {
3565
3568
  "button[aria-label*='Close' i]",
3566
3569
  "button[aria-label*='Fechar' i]",
3567
3570
  ".index-module__whats-new-modal-close",
3571
+ ".age-confirmation-modal button",
3572
+ ".age-confirmation-modal .qwen-chat-v2-btn-black",
3573
+ "button:has-text('Continuar')",
3574
+ "button:has-text('Continue')",
3568
3575
  "button:has-text('Cancel')",
3569
3576
  "button:has-text('Cancelar')",
3570
3577
  "button:has-text('I understand')",
@@ -4759,6 +4766,292 @@ async function validateAccountLogin(rawAccount, headless = true, browserType = "
4759
4766
  release();
4760
4767
  }
4761
4768
  }
4769
+ async function startEmailOtpLogin(email, options = {}) {
4770
+ const normalizedEmail = email.trim().toLowerCase();
4771
+ if (!normalizedEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
4772
+ return { success: false, error: "Formato de e-mail inv\xE1lido" };
4773
+ }
4774
+ const { listAccounts: listAccounts2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4775
+ if (listAccounts2().some((a) => a.email.toLowerCase() === normalizedEmail)) {
4776
+ return {
4777
+ success: false,
4778
+ error: `Uma conta com o e-mail "${normalizedEmail}" j\xE1 est\xE1 cadastrada.`
4779
+ };
4780
+ }
4781
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4782
+ const mockSessionId = "mock-otp-" + Date.now();
4783
+ mockOtpSessions.set(mockSessionId, normalizedEmail);
4784
+ return { success: true, sessionId: mockSessionId };
4785
+ }
4786
+ const accountId = crypto3.randomUUID();
4787
+ const sessionId = accountId;
4788
+ const profilePath = getAccountProfilePath(accountId);
4789
+ const fingerprint = getFingerprintProfile(accountId);
4790
+ const browserType = options.browserType ?? config.playwright.browser;
4791
+ const { engine, channel } = resolveBrowserEngine(browserType);
4792
+ const headless = options.headless ?? config.playwright.headless;
4793
+ const launchOptions = {
4794
+ headless,
4795
+ channel,
4796
+ userAgent: fingerprint.userAgent,
4797
+ locale: fingerprint.locale,
4798
+ timezoneId: fingerprint.timezoneId,
4799
+ viewport: fingerprint.viewport,
4800
+ screen: fingerprint.viewport,
4801
+ deviceScaleFactor: 1,
4802
+ isMobile: false,
4803
+ hasTouch: false,
4804
+ colorScheme: "light",
4805
+ extraHTTPHeaders: {
4806
+ "sec-ch-ua": fingerprint.secChUa,
4807
+ "sec-ch-ua-mobile": "?0",
4808
+ "sec-ch-ua-platform": '"Windows"'
4809
+ },
4810
+ ignoreDefaultArgs: ["--enable-automation", "--enable-blink-features"],
4811
+ args: buildChromiumLaunchArgs(fingerprint.viewport)
4812
+ };
4813
+ let acctContext;
4814
+ try {
4815
+ acctContext = await withTimeout(
4816
+ engine.launchPersistentContext(profilePath, launchOptions),
4817
+ 3e4,
4818
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4819
+ );
4820
+ } catch (launchErr) {
4821
+ if (launchErr?.message?.includes("Executable doesn't exist")) {
4822
+ autoInstallPlaywrightChromium();
4823
+ acctContext = await withTimeout(
4824
+ engine.launchPersistentContext(profilePath, launchOptions),
4825
+ 3e4,
4826
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4827
+ );
4828
+ } else {
4829
+ return { success: false, error: launchErr?.message || String(launchErr) };
4830
+ }
4831
+ }
4832
+ try {
4833
+ await acctContext.addInitScript(getStealthScript(fingerprint));
4834
+ const existingPages = acctContext.pages().filter((p) => !p.isClosed());
4835
+ const acctPage = existingPages.find((p) => p.url().startsWith(qwenOrigin())) ?? existingPages[0] ?? await acctContext.newPage();
4836
+ await acctPage.goto(qwenUrl("/auth?mode=register"), {
4837
+ waitUntil: "domcontentloaded",
4838
+ timeout: Math.min(config.timeouts.navigation, 2e4)
4839
+ }).catch(() => {
4840
+ });
4841
+ await sleep2(1e3);
4842
+ await clearVisibleChallenge(acctPage);
4843
+ const requestId = crypto3.randomUUID();
4844
+ const evalRes = await acctPage.evaluate(
4845
+ async ({ email: email2, requestId: requestId2, version, timezone }) => {
4846
+ try {
4847
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/request", {
4848
+ method: "POST",
4849
+ credentials: "include",
4850
+ headers: {
4851
+ accept: "application/json, text/plain, */*",
4852
+ "content-type": "application/json",
4853
+ source: "web",
4854
+ version,
4855
+ "x-request-origin": "https://chat.qwen.ai",
4856
+ timezone,
4857
+ "x-request-id": requestId2
4858
+ },
4859
+ body: JSON.stringify({
4860
+ identifier: email2,
4861
+ accept_terms: true,
4862
+ language: "pt-BR"
4863
+ })
4864
+ });
4865
+ const json = await response.json().catch(() => null);
4866
+ return { ok: response.ok, status: response.status, data: json };
4867
+ } catch (e) {
4868
+ return { ok: false, error: e.message };
4869
+ }
4870
+ },
4871
+ {
4872
+ email: normalizedEmail,
4873
+ requestId,
4874
+ version: getQwenWebVersion() || "0.3.12",
4875
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4876
+ }
4877
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4878
+ if (!evalRes.ok || evalRes.data?.success !== true) {
4879
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "Falha ao solicitar c\xF3digo de verifica\xE7\xE3o";
4880
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4881
+ });
4882
+ cleanupPlaywrightAccountState(accountId);
4883
+ return { success: false, error: errorMsg };
4884
+ }
4885
+ const timer = setTimeout(async () => {
4886
+ activeOtpSessions.delete(sessionId);
4887
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4888
+ });
4889
+ cleanupPlaywrightAccountState(accountId);
4890
+ }, 5 * 60 * 1e3);
4891
+ timer.unref?.();
4892
+ activeOtpSessions.set(sessionId, {
4893
+ sessionId,
4894
+ accountId,
4895
+ email: normalizedEmail,
4896
+ context: acctContext,
4897
+ page: acctPage,
4898
+ fingerprint,
4899
+ createdAt: Date.now(),
4900
+ timer
4901
+ });
4902
+ return { success: true, sessionId };
4903
+ } catch (err) {
4904
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4905
+ });
4906
+ cleanupPlaywrightAccountState(accountId);
4907
+ return { success: false, error: err?.message || String(err) };
4908
+ }
4909
+ }
4910
+ async function verifyEmailOtpLogin(sessionId, code) {
4911
+ const cleanCode = code.trim();
4912
+ if (!cleanCode) {
4913
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o \xE9 obrigat\xF3rio" };
4914
+ }
4915
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4916
+ const mockEmail = mockOtpSessions.get(sessionId);
4917
+ if (!mockEmail) {
4918
+ return { success: false, error: "Sess\xE3o OTP inv\xE1lida ou expirada" };
4919
+ }
4920
+ if (cleanCode === "000000") {
4921
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado" };
4922
+ }
4923
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4924
+ const mockAccId = "mock-otp-acc-" + Date.now();
4925
+ const newAccount = addAccount2(mockEmail, "", mockAccId);
4926
+ mockOtpSessions.delete(sessionId);
4927
+ return { success: true, account: newAccount };
4928
+ }
4929
+ const session = activeOtpSessions.get(sessionId);
4930
+ if (!session) {
4931
+ return { success: false, error: "Sess\xE3o OTP expirada ou n\xE3o encontrada. Solicite um novo c\xF3digo." };
4932
+ }
4933
+ if (session.page.isClosed()) {
4934
+ activeOtpSessions.delete(sessionId);
4935
+ return { success: false, error: "Navegador foi fechado. Solicite um novo c\xF3digo." };
4936
+ }
4937
+ const requestId = crypto3.randomUUID();
4938
+ const evalRes = await session.page.evaluate(
4939
+ async ({ email, code: code2, requestId: requestId2, version, timezone }) => {
4940
+ try {
4941
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/verify", {
4942
+ method: "POST",
4943
+ credentials: "include",
4944
+ headers: {
4945
+ accept: "application/json, text/plain, */*",
4946
+ "content-type": "application/json",
4947
+ source: "web",
4948
+ version,
4949
+ "x-request-origin": "https://chat.qwen.ai",
4950
+ timezone,
4951
+ "x-request-id": requestId2
4952
+ },
4953
+ body: JSON.stringify({
4954
+ identifier: email,
4955
+ code: code2,
4956
+ name: null
4957
+ })
4958
+ });
4959
+ const json = await response.json().catch(() => null);
4960
+ return { ok: response.ok, status: response.status, data: json };
4961
+ } catch (e) {
4962
+ return { ok: false, error: e.message };
4963
+ }
4964
+ },
4965
+ {
4966
+ email: session.email,
4967
+ code: cleanCode,
4968
+ requestId,
4969
+ version: getQwenWebVersion() || "0.3.12",
4970
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4971
+ }
4972
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4973
+ if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
4974
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado";
4975
+ return { success: false, error: errorMsg };
4976
+ }
4977
+ const resData = evalRes.data.data;
4978
+ const accessToken = resData.access_token || resData.token;
4979
+ const refreshToken = resData.refresh_token;
4980
+ const userId = resData.id;
4981
+ try {
4982
+ await session.page.evaluate(({ token, rTok }) => {
4983
+ try {
4984
+ localStorage.setItem("token", token);
4985
+ localStorage.setItem("access_token", token);
4986
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4987
+ document.cookie = `token=${encodeURIComponent(token)}; path=/; domain=.qwen.ai; max-age=31536000`;
4988
+ } catch {
4989
+ }
4990
+ }, { token: accessToken, rTok: refreshToken });
4991
+ await session.context.addCookies([
4992
+ {
4993
+ name: "token",
4994
+ value: accessToken,
4995
+ domain: ".qwen.ai",
4996
+ path: "/",
4997
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
4998
+ httpOnly: false,
4999
+ secure: true,
5000
+ sameSite: "Lax"
5001
+ }
5002
+ ]);
5003
+ await session.page.goto(qwenUrl("/"), {
5004
+ waitUntil: "domcontentloaded",
5005
+ timeout: Math.min(config.timeouts.navigation, 15e3)
5006
+ }).catch(() => {
5007
+ });
5008
+ await sleep2(1e3);
5009
+ await saveStorageState(session.context, session.accountId);
5010
+ const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5011
+ const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
5012
+ const exp = parseJwtExpiry2(accessToken);
5013
+ saveAuthSession2(session.accountId, {
5014
+ cookie: `token=${accessToken}`,
5015
+ userAgent: session.fingerprint.userAgent,
5016
+ bxV: "2.5.37",
5017
+ version: getQwenWebVersion() || "0.3.12",
5018
+ userId: userId || void 0,
5019
+ tokenExpiresAt: exp || void 0,
5020
+ refreshToken: refreshToken || void 0,
5021
+ capturedAt: Date.now()
5022
+ });
5023
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5024
+ const newAccount = addAccount2(session.email, "", session.accountId);
5025
+ clearTimeout(session.timer);
5026
+ activeOtpSessions.delete(sessionId);
5027
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5028
+ });
5029
+ cleanupPlaywrightAccountState(session.accountId);
5030
+ return { success: true, account: newAccount };
5031
+ } catch (err) {
5032
+ clearTimeout(session.timer);
5033
+ activeOtpSessions.delete(sessionId);
5034
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5035
+ });
5036
+ cleanupPlaywrightAccountState(session.accountId);
5037
+ return { success: false, error: err?.message || String(err) };
5038
+ }
5039
+ }
5040
+ async function cancelEmailOtpLogin(sessionId) {
5041
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
5042
+ mockOtpSessions.delete(sessionId);
5043
+ return;
5044
+ }
5045
+ const session = activeOtpSessions.get(sessionId);
5046
+ if (!session) return;
5047
+ clearTimeout(session.timer);
5048
+ activeOtpSessions.delete(sessionId);
5049
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5050
+ });
5051
+ cleanupPlaywrightAccountState(session.accountId);
5052
+ const { wipeAccountSessionFiles: wipeAccountSessionFiles2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5053
+ wipeAccountSessionFiles2(session.accountId);
5054
+ }
4762
5055
  function classifyQwenAuthError(code, details) {
4763
5056
  const c = (code || "").trim().toLowerCase();
4764
5057
  const d = (details || "").trim().toLowerCase();
@@ -5456,7 +5749,24 @@ async function refreshAccountToken(accountId) {
5456
5749
  return { success: false, error: "Could not refresh or re-authenticate token" };
5457
5750
  }
5458
5751
  async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
5459
- const page = pageOverride ?? accountPages.get(accountId);
5752
+ let page = pageOverride ?? accountPages.get(accountId);
5753
+ if (!page || page.isClosed()) {
5754
+ const { isRunningUnderNodeTest: isRunningUnderNodeTest3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
5755
+ if (!isRunningUnderNodeTest3()) {
5756
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5757
+ const creds = getAccountCredentials2(accountId);
5758
+ if (creds) {
5759
+ await initPlaywrightForAccount(
5760
+ creds,
5761
+ config.playwright.headless,
5762
+ config.playwright.browser,
5763
+ { skipHeaderCapture: true }
5764
+ ).catch(() => {
5765
+ });
5766
+ page = accountPages.get(accountId);
5767
+ }
5768
+ }
5769
+ }
5460
5770
  if (!page || page.isClosed()) {
5461
5771
  throw new Error(`Playwright page unavailable for header capture: ${accountId}`);
5462
5772
  }
@@ -6099,16 +6409,38 @@ async function refreshHeaders(accountId, timeoutMs = config.timeouts.headers, fo
6099
6409
  release();
6100
6410
  }
6101
6411
  }
6102
- async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, mutexTimeoutMs = PLAYWRIGHT_MUTEX_WAIT_MS, recoverOnTimeout = true) {
6412
+ async function ensureAccountPageAvailable(accountId) {
6103
6413
  const inFlightInit = inFlightAccountInits.get(accountId);
6104
6414
  if (inFlightInit) {
6105
6415
  await inFlightInit.catch(() => {
6106
6416
  });
6107
6417
  }
6108
- const page = accountPages.get(accountId);
6418
+ let page = accountPages.get(accountId);
6419
+ if (page && !page.isClosed()) {
6420
+ return page;
6421
+ }
6422
+ const { isRunningUnderNodeTest: isRunningUnderNodeTest3 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
6423
+ if (isRunningUnderNodeTest3()) {
6424
+ throw new Error(`Playwright page unavailable for account: ${accountId}`);
6425
+ }
6426
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
6427
+ const creds = getAccountCredentials2(accountId);
6428
+ if (!creds) {
6429
+ throw new Error(`Playwright page unavailable for account: ${accountId}`);
6430
+ }
6431
+ await initPlaywrightForAccount(
6432
+ creds,
6433
+ config.playwright.headless,
6434
+ config.playwright.browser
6435
+ );
6436
+ page = accountPages.get(accountId);
6109
6437
  if (!page || page.isClosed()) {
6110
6438
  throw new Error(`Playwright page unavailable for account: ${accountId}`);
6111
6439
  }
6440
+ return page;
6441
+ }
6442
+ async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, mutexTimeoutMs = PLAYWRIGHT_MUTEX_WAIT_MS, recoverOnTimeout = true) {
6443
+ const page = await ensureAccountPageAvailable(accountId);
6112
6444
  const release = await acquireAccountMutex(
6113
6445
  accountId,
6114
6446
  `page:${accountId.substring(0, 12)}`,
@@ -6127,7 +6459,7 @@ async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION
6127
6459
  return result;
6128
6460
  } catch (error) {
6129
6461
  const message = getErrorMessage(error);
6130
- if (recoverOnTimeout && message.includes("Playwright page operation timed out")) {
6462
+ if (recoverOnTimeout && message.includes("Playwright page operation timed out") && (!page || page.isClosed())) {
6131
6463
  console.warn(
6132
6464
  `\u23F1\uFE0F [Playwright] Resetting account context after a stuck page operation: ${accountId}`
6133
6465
  );
@@ -6409,8 +6741,10 @@ async function evictIdlePlaywrightContextsToLimit() {
6409
6741
  if (accountPages.size <= max) return 0;
6410
6742
  const candidates = priorityOrderForEviction(
6411
6743
  Array.from(accountPages.keys()).filter((accountId) => {
6744
+ if (isAccountServingStream(accountId)) return false;
6745
+ if (isAccountRecentlyActive(accountId, 3e4)) return false;
6412
6746
  const mutex = accountMutexes.get(accountId);
6413
- return mutex?.isIdle() && !isAccountServingStream(accountId);
6747
+ return mutex?.isIdle();
6414
6748
  })
6415
6749
  ).map((accountId) => ({
6416
6750
  accountId,
@@ -6676,7 +7010,7 @@ async function getTokenDiagnostics(accountId) {
6676
7010
  }
6677
7011
  };
6678
7012
  }
6679
- var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
7013
+ var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, activeOtpSessions, mockOtpSessions, PROFILE_RESET_TIMEOUT_MS;
6680
7014
  var init_playwright = __esm({
6681
7015
  "src/services/playwright.ts"() {
6682
7016
  "use strict";
@@ -6721,11 +7055,13 @@ var init_playwright = __esm({
6721
7055
  PLAYWRIGHT_MUTEX_WAIT_MS = 6e4;
6722
7056
  ACCOUNT_PAGE_OPERATION_TIMEOUT_MS = config.timeouts.page;
6723
7057
  SESSION_PROBE_NAVIGATION_TIMEOUT_MS = 15e3;
6724
- HEADER_CAPTURE_TRIGGER_GRACE_MS = 15e3;
6725
- FIRST_TRIGGER_GRACE_MS = 8e3;
7058
+ HEADER_CAPTURE_TRIGGER_GRACE_MS = 2e4;
7059
+ FIRST_TRIGGER_GRACE_MS = 12e3;
6726
7060
  HEADER_CAPTURE_TRIGGER_ATTEMPTS = 3;
6727
7061
  CHAT_INPUT_APPEAR_TIMEOUT_MS = 15e3;
6728
7062
  CHAT_INPUT_ACTION_TIMEOUT_MS = 1e4;
7063
+ activeOtpSessions = /* @__PURE__ */ new Map();
7064
+ mockOtpSessions = /* @__PURE__ */ new Map();
6729
7065
  PROFILE_RESET_TIMEOUT_MS = Math.max(9e4, config.timeouts.headers);
6730
7066
  setWafContextResetListener((accountId) => {
6731
7067
  if (!accountPages.has(accountId)) return;
@@ -6779,7 +7115,8 @@ async function showMenu() {
6779
7115
  console.log("No accounts configured yet.\n");
6780
7116
  }
6781
7117
  console.log("\nOptions:");
6782
- console.log(" [A] Add account");
7118
+ console.log(" [A] Add account (Password)");
7119
+ console.log(" [O] Add account (Passwordless / Email OTP)");
6783
7120
  if (accounts.length > 0) {
6784
7121
  console.log(" [R] Remove an account");
6785
7122
  }
@@ -6790,7 +7127,21 @@ async function showMenu() {
6790
7127
  process.exit(0);
6791
7128
  }
6792
7129
  if (choice === "A") {
6793
- await addAccountFlow();
7130
+ clear();
7131
+ console.log("=== Add New Account ===\n");
7132
+ console.log("Authentication method:");
7133
+ console.log(" [1] Traditional Password (Email + Password)");
7134
+ console.log(" [2] Passwordless (Email Verification Code / OTP)\n");
7135
+ const method = (await askQuestion("Select method [1/2] (default 1): ")).trim();
7136
+ if (method === "2") {
7137
+ await addAccountOtpFlow();
7138
+ } else {
7139
+ await addAccountPasswordFlow();
7140
+ }
7141
+ continue;
7142
+ }
7143
+ if (choice === "O") {
7144
+ await addAccountOtpFlow();
6794
7145
  continue;
6795
7146
  }
6796
7147
  if (choice === "R" && accounts.length > 0) {
@@ -6799,9 +7150,9 @@ async function showMenu() {
6799
7150
  }
6800
7151
  }
6801
7152
  }
6802
- async function addAccountFlow() {
7153
+ async function addAccountPasswordFlow() {
6803
7154
  clear();
6804
- console.log("=== Add New Account ===\n");
7155
+ console.log("=== Add New Account (Password Login) ===\n");
6805
7156
  const email = await askQuestion("Email: ");
6806
7157
  if (!email || !email.trim()) {
6807
7158
  console.log("Email is required.");
@@ -6870,6 +7221,70 @@ async function addAccountFlow() {
6870
7221
  }
6871
7222
  await askQuestion("\nPress Enter to continue...");
6872
7223
  }
7224
+ async function addAccountOtpFlow() {
7225
+ clear();
7226
+ console.log("=== Add New Account (Passwordless / Email OTP) ===\n");
7227
+ const email = await askQuestion("Email: ");
7228
+ if (!email || !email.trim()) {
7229
+ console.log("Email is required.");
7230
+ await askQuestion("Press Enter to continue...");
7231
+ return;
7232
+ }
7233
+ const trimmedEmail = email.trim();
7234
+ const existingAccounts = listAccounts();
7235
+ if (existingAccounts.some(
7236
+ (a) => a.email.toLowerCase() === trimmedEmail.toLowerCase()
7237
+ )) {
7238
+ console.log(
7239
+ `
7240
+ \u274C Error: An account with email "${trimmedEmail}" already exists.`
7241
+ );
7242
+ await askQuestion("Press Enter to continue...");
7243
+ return;
7244
+ }
7245
+ console.log(
7246
+ `
7247
+ \u23F3 Requesting OTP verification code for ${maskEmail(trimmedEmail)}...`
7248
+ );
7249
+ const { startEmailOtpLogin: startEmailOtpLogin2, verifyEmailOtpLogin: verifyEmailOtpLogin2, cancelEmailOtpLogin: cancelEmailOtpLogin2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
7250
+ const startRes = await startEmailOtpLogin2(trimmedEmail, { headless: false });
7251
+ if (!startRes.success || !startRes.sessionId) {
7252
+ console.log(
7253
+ `
7254
+ \u274C Failed to request code: ${startRes.error || "Unknown error"}`
7255
+ );
7256
+ await askQuestion("Press Enter to continue...");
7257
+ return;
7258
+ }
7259
+ console.log(`
7260
+ \u2709\uFE0F Verification code sent to ${trimmedEmail}!`);
7261
+ console.log("Check your email inbox (and spam folder).\n");
7262
+ while (true) {
7263
+ const code = await askQuestion("Enter 6-digit code (or 'C' to cancel): ");
7264
+ if (!code || !code.trim()) continue;
7265
+ if (code.trim().toUpperCase() === "C") {
7266
+ await cancelEmailOtpLogin2(startRes.sessionId);
7267
+ console.log("\nLogin cancelled.");
7268
+ await askQuestion("Press Enter to continue...");
7269
+ return;
7270
+ }
7271
+ console.log("\n\u23F3 Verifying code and establishing 30-day session...");
7272
+ const verifyRes = await verifyEmailOtpLogin2(startRes.sessionId, code.trim());
7273
+ if (verifyRes.success && verifyRes.account) {
7274
+ console.log(
7275
+ `
7276
+ \u2705 Account verified and saved: ${maskEmail(verifyRes.account.email)} (${verifyRes.account.id})`
7277
+ );
7278
+ console.log("Passwordless 30-day session active.");
7279
+ await askQuestion("\nPress Enter to continue...");
7280
+ return;
7281
+ } else {
7282
+ console.log(`
7283
+ \u274C ${verifyRes.error || "Invalid code"}`);
7284
+ console.log("Please try again or enter 'C' to cancel.\n");
7285
+ }
7286
+ }
7287
+ }
6873
7288
  async function removeAccountFlow() {
6874
7289
  const accounts = listAccounts();
6875
7290
  if (accounts.length === 0) return;