npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ncs191655320/live

v0.4.4

Published

Agents CLI (Playwright-based) – chạy không cần .env theo mặc định; global bin: agents

Readme

AGENT-JS

Version: 0.2.6
Package: @ncs191655320/live
CLI Command: agents hoặc agents -mkt

AGENT-JS: Trình điều phối tự động hoá trình duyệt bằng Playwright (khung E2E – điều khiển Chromium/Firefox/WebKit) và kiến trúc mở để cắm Fingerprint Suite (bộ tạo & bộ tiêm fingerprint – hồ sơ trình duyệt).

Yêu cầu hệ thống

  • Node.js LTS
  • Windows (đường dẫn kiểu D:\...)
  • Quyền ghi thư mục dự án

Cài đặt

npm install
npm run pw:install

Chạy chương trình

Luồng chính (ephemeral profile)

npm start
# hoặc sau khi cài global
agents

Luồng MKT Login (profile manager)

npm run start:mkt
# hoặc sau khi cài global
agents -mkt

Chương trình sẽ hỏi đường dẫn thư mục chứa nhiều cookie.txt (định dạng Netscape cookie file – định dạng lưu cookie truyền thống). Ví dụ: D:\AZ-REFACTOR\LOGIN_LIVE\COOKIES_BULK.

Lưu ý luồng MKT:

  • Yêu cầu MKT Login API đang chạy tại http://127.0.0.1:4980
  • Tự động chọn profile đầu tiên từ danh sách
  • Không cần nhập workspaceId (sử dụng workspace mặc định)

Luồng xử lý

  1. Tạo BrowserContext bền vững mới với userDataDir tạm (tương đương “Delete browsing data – all time”).
  2. Sinh fingerprint profile → áp dụng Context Options + addInitScript.
  3. Warm-up Google (waitUntil: networkidle, scroll nhẹ).
  4. Tiêm cookie theo domain (context.addCookies) và xác nhận trong jar.
  5. Verify UI (login.live.com) + retry reload; Verify storage (portal.azure.com) qua localStorage/sessionStorage.
  6. Phân loại strict/normal, lưu cookie.txt vào outputs/.

Lưu ý: Đây là luồng chính (ephemeral). Xem thêm Luồng MKT Login bên dưới.

Luồng MKT Login (v0.2.6+)

  1. Kết nối MKT Login API (http://127.0.0.1:4980).
  2. Tự động phát hiện profileId: Lấy danh sách profiles và chọn profile đầu tiên.
  3. Mở profile và kết nối qua CDP (Chrome DevTools Protocol).
  4. Tiêm fingerprint Apify: Sinh fingerprint deterministic (seed từ SHA1 của tên file cookie) và inject vào page-level.
  5. Warm-up → Tiêm cookies → Verify Live → Verify Azure.
  6. Phân loại và di chuyển file cookie như luồng chính.
  7. Đóng profile sau khi hoàn tất.

Quy trình phân loại & di chuyển tệp cookie

  1. Đánh giá kết quả

    • Chế độ strict (chuẩn duy nhất): khi cả hai kiểm tra đều vượt qua: uiPassed === trueapiPassed === true (UI + API). Lưu ý quan trọng: mọi PASS UI bắt buộc phải thu thập được email tài khoản (capturedEmail). UI pass ở verifyLive.ts (strict chain: account → email → portal), API pass ở verifyAzure.ts.
    • Chế độ normal: mọi trường hợp còn lại (UI fail hoặc API fail), bao gồm cả trường hợp thiếu email (EMAIL_MISSING), OTP/MFA bắt buộc, hoặc các lỗi URL.
  2. Xử lý tệp cookie

    • Đánh dấu đã xử lý: kết quả của mỗi tệp được push vào mảng outcomes để ghi ra báo cáo JSON cuối cùng.
    • Tổ chức lưu trữ đích:
      • Thư mục gốc và các nhánh con được đảm bảo tồn tại khi khởi động: artifacts/, outputs/, outputs/strict/, outputs/normal/ (khởi tạo tại src/config.ts, hàm ensureDirectories() – xem config.ts:237-242).
      • Chọn thư mục đích theo kết quả:
        • strict nếu uiPassed && apiPassed.
        • normal nếu không thỏa điều kiện trên (bao gồm UI fail hoặc Azure fail, hoặc bị skip Azure do Live ở trang đăng nhập).
      • Di chuyển/sao chép file: chương trình sao chép cookie.txt vào thư mục tương ứng và lưu lại đường dẫn đích (dest) trong kết quả (thực hiện tại src/orchestrator/main.ts – đoạn phân loại & copy, xem main.ts:299-307).
    • Ghi báo cáo: cuối quá trình, toàn bộ outcomes được ghi ra file JSON trong artifacts/ (xem main.ts:314-316).

Scripts

  • npm run build — biên dịch TypeScript.
  • npm start — build + chạy orchestrator (luồng chính).
  • npm run start:mkt — build + chạy orchestrator (luồng MKT Login).
  • npm run pw:install — cài đặt trình duyệt Playwright Chromium.
  • npm run test:e2e — chạy kiểm thử E2E cơ bản (Playwright Test).
  • npm publish — publish gói lên npm registry.

Phase 1 – Củng cố nền tảng

  • Đã dựng bộ chuẩn ESLint (trình phân tích tĩnh – enforce rule TypeScript/bảo mật) + Prettier (trình định dạng code) với script:
    • npm run lint / npm run lint:fix
    • npm run format / npm run format:fix
  • Cung cấp hàm validateEnv() tại src/config.ts để kiểm tra biến môi trường bằng Zod (thư viện validate schema); mọi module có thể gọi lại để đọc cấu hình sạch.
  • Chuẩn hóa logging với wrapper Pino (logger hiệu năng cao) tại src/logger.ts; các module sử dụng logger.info/warn/error (không dùng console.log).
  • Bật các flag TypeScript nghiêm ngặt: noImplicitAny, exactOptionalPropertyTypes, noUnusedParameters; đã xử lý cảnh báo trong toàn bộ codebase.
  • Cập nhật tsconfig.eslint.json phục vụ ESLint với Project References.
  • Bổ sung tài liệu chi tiết trong docs/de-xuat-toi-uu.md mô tả roadmap tối ưu theo phase.

Phase 2 – Ổn định trình duyệt và DOM

  • Thêm bộ helper DOM tại src/utils/dom.ts với getStableLocator() (locator ổn định + timeout động) và ensureDomStable() (snapshot DOM + retry, log { step, retryCount, message: "DOM khong on dinh" }).
  • Các flow (authFlows.ts, verifyLive.ts) chuyển sang dùng helper thay vì truy cập selector trực tiếp; mọi thao tác quan trọng đều đo ổn định DOM.
  • Giới thiệu ContextPool (src/browser/contextPool.ts) giới hạn đồng thời, tái sử dụng BrowserContext, tự động context.clearCookies() giữa job và đóng context incognito khi cần.
  • Đảm bảo ephemeralProfile và profile tạm đều cleanup trong finally.
  • Bổ sung biến môi trường điều khiển pool: CONTEXT_MAX_CONCURRENCY, CONTEXT_REUSE, CONTEXT_CLEAR_COOKIES.
  • Ghi nhận metric DOM: trung bình thời gian xử lý, số lần retry → phục vụ báo cáo Phase 2.

Phase 3 – Tối ưu API & Dữ liệu

  • mktlogin-client/core/request.ts dùng p-retry (retry + backoff jitter) và AbortController; log structured { event: "api_retry", attempt, status }, timeout mặc định 15s (tùy chỉnh qua biến môi trường).
  • ApiError mapping giữ nguyên nhưng response được phân loại rõ ràng hơn qua logging chính xác tên lỗi, trạng thái.
  • DataMover refactor sang streaming (fs.createReadStream/createWriteStream), kiểm tra checksum SHA-1 sau copy trước khi xoá nguồn.
  • Biến môi trường hỗ trợ Phase 3: API_RETRY_ATTEMPTS, API_RETRY_MIN_TIMEOUT_MS, API_REQUEST_TIMEOUT_MS.
  • Tài liệu Phase 3 cập nhật tại docs/de-xuat-toi-uu.md; cần chạy thử để thu thập metric retry và mức RAM.

Phase 4 – Quan sát & Kiểm thử

  • Logger hỗ trợ LOG_TRANSPORT_TARGET + LOG_TRANSPORT_OPTIONS để chuyển log sang hệ thống quan sát (ví dụ Elastic APM) thông qua pino.transport.
  • src/utils/dom.ts phát hành metric DOM (dom_metric_snapshot) giúp dashboard hoá retry/delay.
  • Thêm npm run bench (Playwright) lưu kết quả vào outputs/benchmarks/; CI chạy benchmark nhẹ.
  • CI GitHub Actions (.github/workflows/ci.yml) chạy lint → test → bench tự động.
  • Test bổ sung tests/domHelpers.spec.ts đảm bảo helper DOM hoạt động ổn định.
  • Lưu ý: SEO lint tạm thời ở chế độ "warning only" cho các thư mục legacy (src/browser, src/services, src/utils) trong khi dọn dẹp dần; xem runbook để biết kế hoạch làm sạch.

Cấu trúc thư mục

AGENT-JS/
  package.json
  tsconfig.json
  .gitignore
  README.md
  src/
    config.ts
    logger.ts
    utils/
      cookies.ts
    browser/
      fingerprint.ts
      launch.ts
      context.ts
    services/
      warmup.ts
      injectCookies.ts
      verifyLive.ts
      verifyAzure.ts
    orchestrator/
      main.ts
  tests/
    e2e.spec.ts
  artifacts/
  outputs/
    strict/
    normal/

Vận hành an toàn

  • Không sử dụng vào mục đích bất chính.
  • HEADLESS có thể bật/tắt qua biến môi trường.
  • Thời gian chờ/Retry có thể tinh chỉnh qua src/config.ts.

Citations (Bằng chứng tài liệu chính thức)

  • Playwright — BrowserContext.addCookies
    https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies
    Tóm tắt: Thêm nhiều cookie vào BrowserContext; cookie gồm name, value, domain, path, expires, httpOnly, secure, sameSite.

  • Playwright — launchPersistentContext (Chromium)
    https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context
    Tóm tắt: Khởi chạy context bền vững dùng userDataDir; tạo thư mục mới tương đương hồ sơ sạch (xoá dữ liệu cũ).

  • Playwright — page.goto (waitUntil: 'networkidle')
    https://playwright.dev/docs/api/class-page#page-goto
    Tóm tắt: waitUntil có thể là networkidle (không còn kết nối mạng ≥ 500 ms), phù hợp để chờ trang ổn định trước thao tác.

  • Playwright — addInitScript
    https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script
    Tóm tắt: Tiêm script chạy trước mọi script của trang; có thể tinh chỉnh navigator.* ở mức JS.

  • cURL — Netscape cookie file format
    https://curl.se/docs/http-cookies.html
    Tóm tắt: Định dạng “Netscape HTTP Cookie File”: domain, flag, path, secure, expiration, name, value; dòng # là comment; có tiền tố #HttpOnly_.

Assumptions & Gaps (Giả định & Khoảng trống cần đối chiếu)

  • Fingerprint Suite (generator + injector): CHƯA xác minh tên gói và API chính thức trên npm. Cần chỉ định vendor + tài liệu để viết adapter thực thi thật (thay thế generateFingerprint() và mở rộng injectFingerprint()).
  • Heuristics verify login.live.com và portal.azure.com: cần bằng chứng runtime để tối ưu (URL/selector ổn định, key storage phổ biến). Hiện dùng chiến lược phòng thủ, có retry và bằng chứng ảnh.
  • Môi trường Python gốc: cần cây thư mục và file nguồn (main.py, nơi xử lý cookie, trình duyệt) để so khớp 1-1.
  • Chính sách bảo mật/tốc độ: có thể cần điều chỉnh timeout và MIN_LS_KEYS theo mạng/tài khoản thực tế.

Kiểm thử

npm run test:e2e

Góp ý & mở rộng

  • Có thể mở rộng module fingerprint để cắm vendor chính thức khi có tài liệu.
  • Bổ sung lưu HAR và thêm báo cáo chi tiết nếu cần.

Cấu hình Azure (verifyAzure)

  • Biến môi trường quan trọng (xem ánh xạ trong src/config.ts):
    • AZURE_REQUIRE_USERID_STRICT (mặc định true) — siết chặt: bắt buộc có userId hợp lệ kể cả khi có github (tham chiếu src/config.ts:74-76, src/config.ts:243-245).
    • STORAGE_RETRIES (mặc định 2) — số lần thử lại khi kiểm tra storage (tham chiếu src/config.ts:240-241, dùng trong verifyAzure.ts:51-56).
    • STORAGE_BACKOFF_MS (mặc định 2000) — thời gian chờ giữa các lần thử (tham chiếu src/config.ts:240-241, dùng trong verifyAzure.ts:51-56).
    • STORAGE_REQUIRE_LOCAL_ONLY (mặc định true) — gate readiness ưu tiên localStorage (tham chiếu src/config.ts:239-241, dùng trong verifyAzure.ts:51-76).
    • STORAGE_WAIT_PATTERNS — danh sách pattern chờ key xuất hiện (ví dụ: sessionTracking_,msal.account.keys) (tham chiếu src/config.ts:219-224, dùng trong verifyAzure.ts:51-76).
    • STORAGE_VERIFY_WRITE — probe ghi thử (none | sessionOnly | localPreferred) (tham chiếu src/config.ts:226-231, dùng trong verifyAzure.ts:51-76).
    • STORAGE_MIN_KEYS — ngưỡng tối thiểu key để pass về mặt số lượng (tham chiếu src/config.ts:225-226, dùng trong verifyAzure.ts qua tham số minLocalStorageKeys).

Quy trình verifyAzure (tóm tắt kỹ thuật)

  1. Mở https://portal.azure.com và ổn định trang: stabilizePage(...) (tham chiếu src/services/verifyAzure.ts:48-49).
  2. Vòng lặp kiểm tra readiness + refresh/backoff theo config (tham chiếu src/services/verifyAzure.ts:51-93):
    • checkStorageReady() gate trước khi đọc chi tiết (tham chiếu src/utils/checkStorageReady.ts:10-21,34-44,146-183).
    • Trước lượt cuối: page.reload() để phục hồi nếu cần (tham chiếu src/services/verifyAzure.ts:83-88).
  3. Đọc bằng chứng storage: readAzureStorage() (tham chiếu src/utils/storage_scripts.ts) và đánh giá tiêu chí pass/fail (tham chiếu src/services/verifyAzure.ts:95-123).
  4. Ghi ảnh azure-*.png và artifact chi tiết azure-detail-*.json (tham chiếu src/services/verifyAzure.ts:126-142).

Tiêu chí pass (điều kiện đầy đủ):

  • Số key localStorageSTORAGE_MIN_KEYS
  • Có đầy đủ nhóm key bắt buộc userId, displayName, domainName, email HOẶC có key github
  • userId hợp lệ theo cấu hình siết chặt (khi AZURE_REQUIRE_USERID_STRICT=true, luôn bắt buộc userId hợp lệ; tham chiếu src/services/verifyAzure.ts:108-112).

Reason codes (Azure)

  • OK — đạt tiêu chí pass (tham chiếu src/types/resultCodes.ts:15).
  • STORAGE_UNAVAILABLE — không truy cập được Web Storage (gate readiness fail) (tham chiếu src/types/resultCodes.ts:16).
  • STORAGE_INSUFFICIENT — đếm không đủ key hoặc thiếu các key bắt buộc (tham chiếu src/types/resultCodes.ts:17).
  • RETRY_EXCEEDED — hết số lần thử mà vẫn không đạt (tham chiếu src/types/resultCodes.ts:18).
  • RETRIED_REFRESH_RECOVERED — hồi phục sau khi refresh (tham chiếu src/types/resultCodes.ts:19).
  • INTERNAL_ERROR — lỗi evaluate/ngoại lệ (tham chiếu src/types/resultCodes.ts:20).
  • UNKNOWN — dự phòng (tham chiếu src/types/resultCodes.ts:21).

Artifact chi tiết (azure-detail-*.json)

  • Ghi tại thư mục artifacts/ mỗi lần verify Azure (tham chiếu src/services/verifyAzure.ts:130-142).
  • Trường dữ liệu chính:
    • url, attempts, refreshed, ready, reasonCode, apiPassed.

    • evidence: snapshot storage (đã ẩn giá trị PII — xem sample trong src/utils/storage_scripts.ts).

Chuẩn xác thực tài khoản (strict only) – login.live.com → portal.azure.com

  • Nguyên tắc bất di bất dịch: PASS UI chỉ hợp lệ khi đã thu thập được email tài khoản (capturedEmail) sau khi vào account.* và trước/đồng thời khi vào portal.
  • Sau khi điều hướng đến login.live.com và ổn định lần đầu:
    • Nếu URL vẫn là trang đăng nhập (LOGIN_PAGE) → FAIL sớm.
    • Nếu phát hiện OTP/MFA → FAIL với OTP_REQUIRED/MFA_REQUIRED.
    • Nếu gặp passkey prompt → cố gắng bỏ qua (handlePasskeyInterrupt) và tiếp tục.
  • Chuỗi strict:
    1. Vào account.* → thu thập email (qua /me, JWT cookies, URL param dev, hoặc Web Storage với checkStorageReady siết ổn định tên keys).
    2. Điều hướng/ở portal.azure.com → điền email + Next → safe-load → ổn định thích ứng.
    3. Kiểm tra OTP/MFA sau khi nhập email.
    4. Nếu có capturedEmail và không gặp OTP/MFA → PASS OK_STRICT_CHAIN.

Tăng cường độ tin cậy thu thập email (Web Storage)

  • getEmailFromWebStorage(...) chỉ chạy khi checkStorageReady(...) đạt sẵn sàng với ổn định tên keys (names stability):
    • Bật requireStableNames=true và yêu cầu consistencyPasses ≥ 2 (mặc định 2) với scanIntervalMs hợp lý (mặc định 200 ms).
    • Nếu readiness không đạt, hàm dừng (return null), KHÔNG quét best‑effort.
  • Điều này giảm false‑positive và đảm bảo email được lấy khi storage đã “ổn định”.

Adaptive stabilization – Hướng dẫn đọc thống kê & checklist debug

Mục này giúp đọc nhanh thống kê ổn định thích ứng (AdaptiveStat) và xử lý khi ready=false mà không cần tăng timeout.

AdaptiveStat gồm những gì

  • label: nhãn của lần đo (ví dụ: verifyLive-adaptive, verifyLive-portalNav-adaptive, verifyAzure-adaptive). Gán nhãn tại src/services/verifyLive.ts quanh các lần push thống kê.
  • scans: số vòng quét DOM/console trong nhịp; xấp xỉ durationMs / scanIntervalMs (tham số ở src/config.ts – runtime mapping adaptiveScanIntervalMsLive/Azure).
  • durationMs: thời lượng của nhịp (ms); bị chặn bởi maxDurationMs (ví dụ Live đặt Math.min(safeWaitMs, 4000) tại src/services/verifyLive.ts).
  • firstStableAtMs: thời điểm lần đầu đạt trạng thái ổn định trong nhịp; có thể rỗng nếu không ổn định.
  • ready: true nếu điều kiện ổn định thỏa trong nhịp; false nếu hết thời lượng mà chưa đạt.
  • phase: pha nhận diện được (ví dụ PRE, POST, hoặc NONE).
  • passes: số lần liên tiếp thỏa điều kiện trong nhịp (chống nhiễu); trần bởi consecutivePasses (src/config.ts – runtime mapping adaptiveConsecPassesLive/Azure).

Quy tắc quyết định từ SELECTORS (pre/post)

  • Any-Of: trong mỗi tập preSelectorspostSelectors, chỉ cần tìm thấy 1 selector đáng tin là coi như “match” cho nhãn tương ứng; sau đó cần đạt liên tiếp consecutivePasses để kết luận “ready”.
  • Nguồn cấu hình: selector nạp từ config.livePreSelectorsconfig.livePostSelectors (mặc định đã ghim trong src/config.ts). Ở runtime được dùng tại src/services/verifyLive.ts:392-399.
  • Ưu tiên: tập POST nên ưu tiên selector shell ổn định của Azure Portal: div.fxs-portal, #shell-header, #appBar, div.fxs-topbar, nav.fxs-sidebar, div.fxs-avatarmenu, div.fxs-blade-content.

Checklist khi ready=false

  • Selector – kiểm tính phù hợp
    • Xác nhận selector POST thực sự xuất hiện trên tenant: kiểm nhanh bằng page.$(selector) hoặc thêm log.
    • Điều chỉnh danh sách POST (giữ 5–7 selector “nòng cốt”) trong src/config.ts để tăng xác suất match sớm, không cần tăng timeout.
  • URL guard – đúng đích portal
    • Điều hướng HTTPS trực tiếp https://portal.azure.com và ép #home khi cần (xem src/services/verifyAzure.ts).
    • Sau mỗi goto()/reload() có guard “chrome-error bounce” để phục hồi (xem src/services/verifyAzure.tssrc/services/verifyLive.ts).
  • Service Worker/Network – giảm nhiễu
    • Bật CDP Network.enable + Network.setBypassServiceWorker({ bypass: true }) ngay sau khi có page (xem src/services/verifyLive.ts, src/services/verifyAzure.ts).
  • Storage readiness – Azure
    • Dùng checkStorageReady() để gate trước khi đọc chi tiết (gọi trong src/services/verifyAzure.ts).
    • Ngưỡng đếm và yêu cầu keys cấu hình ở src/config.ts (nhóm STORAGE_*).
  • Consecutive passes – chống nhiễu
    • Nếu selector đúng nhưng vẫn nhiễu, có thể tăng nhẹ ADAPTIVE_CONSEC_PASSES_* (không cần tăng timeout) trong src/config.ts.
  • Ảnh chứng cứ
    • Chức năng chụp ảnh đã vô hiệu hóa trong mã; hãy dựa vào log và artifact JSON.

Đoạn kiểm thử nhanh selector (TypeScript)

// Chạy sau khi điều hướng portal để kiểm xem selector shell có xuất hiện hay không
const candidates = [
  'div.fxs-portal', '#shell-header', '#appBar',
  'div.fxs-topbar', 'nav.fxs-sidebar', 'div.fxs-avatarmenu',
  'div.fxs-blade-content'
];
for (const sel of candidates) {
  const ok = !!(await page.$(sel));
  console.log({ sel, ok });
}

Nếu nhiều selector phía trên trả về ok=true, hãy giữ chúng trong LIVE_POST_SELECTORS để adaptive “ready” sớm hơn mà không cần tăng thời gian chờ.

Quy tắc xử lý (STRICT ONLY – account → email → portal):

  1. LOGIN_PAGE hoặc OTP/MFA → uiPassed=false (failCode: LOGIN_PAGE | OTP_REQUIRED | MFA_REQUIRED | MFA_PUSH_NOTIFICATION_REQUIRED).

    • MFA_PUSH_NOTIFICATION_REQUIRED: Màn hình "Get a code to sign in" + nút "Send notification" yêu cầu xác thực qua push notification đến thiết bị di động → không thể tự động bypass.
  2. ERROR_URL → tiếp tục retry trong giới hạn; nếu vẫn lỗi → uiPassed=false (ERROR_URL).

    • Nếu gặp passkey: dùng handlePasskeyInterrupt để bỏ qua rồi tiếp tục quy trình.
    • Không dùng OK_LOGGED_IN để PASS; đây chỉ là trạng thái tham chiếu.
  3. Ở portal.azure.com và đã nhập email:

    • Chỉ FAIL bởi OTP/MFA sau khi nhập email.
    • PASS chỉ khi đã thu thập được email (capturedEmail) và không gặp OTP/MFA → reasonCode = OK_STRICT_CHAIN, uiPassed=true.
    • Hậu kiểm: nếu uiPassed=true nhưng thiếu email → ép FAIL EMAIL_MISSING.

điều chỉnh thêm trường hợp Ở portal.azure.com và đã nhập email:

  • Chỉ FAIL bởi OTP/MFA sau khi nhập email.
  • ERROR_URL → sau khi nhập email tiếp tục retry trong giới hạn 2 lần; nếu vẫn lỗi → uiPassed=false (ERROR_URL).
  • PASS chỉ khi đã thu thập được email (capturedEmail) và không gặp OTP/MFA → reasonCode = OK_STRICT_CHAIN, uiPassed=true.
  • Hậu kiểm: nếu uiPassed=true nhưng thiếu email → ép FAIL EMAIL_MISSING.