browserclaw 0.11.6 → 0.12.1
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.cjs +208 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -2
- package/dist/index.d.ts +133 -2
- package/dist/index.js +208 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -628,6 +628,72 @@ interface ChallengeWaitResult {
|
|
|
628
628
|
/** The challenge still present (null if resolved) */
|
|
629
629
|
challenge: ChallengeInfo | null;
|
|
630
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* A single rule for checking whether the browser session is authenticated.
|
|
633
|
+
* Multiple rules can be combined — all must pass for `authenticated: true`.
|
|
634
|
+
*/
|
|
635
|
+
interface AuthCheckRule {
|
|
636
|
+
/** URL must contain this substring (checked against page URL) */
|
|
637
|
+
url?: string;
|
|
638
|
+
/** A cookie with this name must exist (non-empty value) */
|
|
639
|
+
cookie?: string;
|
|
640
|
+
/** A CSS selector that must match at least one element on the page (includes hidden elements) */
|
|
641
|
+
selector?: string;
|
|
642
|
+
/** Text that must be present on the page */
|
|
643
|
+
text?: string;
|
|
644
|
+
/** Text that must NOT be present on the page (e.g. "Sign in", "Log in") */
|
|
645
|
+
textGone?: string;
|
|
646
|
+
/** JavaScript function (as string) that must return a truthy value in the browser context */
|
|
647
|
+
fn?: string;
|
|
648
|
+
}
|
|
649
|
+
/** The kind of auth check rule that was evaluated. */
|
|
650
|
+
type AuthCheckRuleKind = 'url' | 'cookie' | 'selector' | 'text' | 'textGone' | 'fn';
|
|
651
|
+
/** Result of a single auth check rule evaluation. */
|
|
652
|
+
interface AuthCheckDetail {
|
|
653
|
+
/** Which rule type was checked */
|
|
654
|
+
rule: AuthCheckRuleKind;
|
|
655
|
+
/** Whether this individual check passed */
|
|
656
|
+
passed: boolean;
|
|
657
|
+
/** Human-readable detail (e.g. the actual URL, cookie value presence, etc.) */
|
|
658
|
+
detail?: string;
|
|
659
|
+
}
|
|
660
|
+
/** Result of an `isAuthenticated()` call. */
|
|
661
|
+
interface AuthCheckResult {
|
|
662
|
+
/** `true` only if ALL rules passed */
|
|
663
|
+
authenticated: boolean;
|
|
664
|
+
/** Per-rule results for debugging */
|
|
665
|
+
checks: AuthCheckDetail[];
|
|
666
|
+
}
|
|
667
|
+
/** Standard exit reasons for structured telemetry. */
|
|
668
|
+
type ExitReason = 'success' | 'auth_failed' | 'nav_failed' | 'timeout' | 'crash' | 'disconnected' | 'manual' | 'error';
|
|
669
|
+
/** Structured telemetry envelope for a browser session lifecycle. */
|
|
670
|
+
interface RunTelemetry {
|
|
671
|
+
/** Milliseconds to launch Chrome (undefined if connected to existing instance) */
|
|
672
|
+
launchMs?: number;
|
|
673
|
+
/** Milliseconds to establish CDP connection */
|
|
674
|
+
connectMs?: number;
|
|
675
|
+
/** Milliseconds for initial navigation (if a URL was provided at launch) */
|
|
676
|
+
navMs?: number;
|
|
677
|
+
/** Whether auth verification passed (undefined if not checked) */
|
|
678
|
+
authOk?: boolean;
|
|
679
|
+
/** Structured exit reason when the session ends */
|
|
680
|
+
exitReason?: ExitReason | (string & {});
|
|
681
|
+
/** Whether cleanup (process kill, connection close) succeeded */
|
|
682
|
+
cleanupOk?: boolean;
|
|
683
|
+
/** Key timestamps in ISO 8601 format */
|
|
684
|
+
timestamps: {
|
|
685
|
+
/** When the launch/connect sequence started */
|
|
686
|
+
startedAt: string;
|
|
687
|
+
/** When Chrome process became reachable */
|
|
688
|
+
launchedAt?: string;
|
|
689
|
+
/** When Playwright CDP connection was established */
|
|
690
|
+
connectedAt?: string;
|
|
691
|
+
/** When initial navigation completed */
|
|
692
|
+
navigatedAt?: string;
|
|
693
|
+
/** When stop() was called */
|
|
694
|
+
stoppedAt?: string;
|
|
695
|
+
};
|
|
696
|
+
}
|
|
631
697
|
/** Result of DNS pinning resolution — hostname locked to resolved addresses. */
|
|
632
698
|
interface PinnedHostname {
|
|
633
699
|
hostname: string;
|
|
@@ -1443,6 +1509,36 @@ declare class CrawlPage {
|
|
|
1443
1509
|
timeoutMs?: number;
|
|
1444
1510
|
pollMs?: number;
|
|
1445
1511
|
}): Promise<ChallengeWaitResult>;
|
|
1512
|
+
/**
|
|
1513
|
+
* Check whether the current page session appears authenticated.
|
|
1514
|
+
*
|
|
1515
|
+
* Evaluates one or more rules against the page state. All rules must pass
|
|
1516
|
+
* for `authenticated` to be `true`. Returns per-rule details for debugging.
|
|
1517
|
+
*
|
|
1518
|
+
* @param rules - Array of auth check rules (url, cookie, selector, text, textGone, fn)
|
|
1519
|
+
* @returns Authentication status and per-rule check details
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* ```ts
|
|
1523
|
+
* // Check by URL and absence of login text
|
|
1524
|
+
* const result = await page.isAuthenticated([
|
|
1525
|
+
* { url: '/dashboard' },
|
|
1526
|
+
* { textGone: 'Sign in' },
|
|
1527
|
+
* ]);
|
|
1528
|
+
* if (!result.authenticated) {
|
|
1529
|
+
* console.log('Auth failed:', result.checks.filter(c => !c.passed));
|
|
1530
|
+
* }
|
|
1531
|
+
*
|
|
1532
|
+
* // Check by cookie presence
|
|
1533
|
+
* const result = await page.isAuthenticated([{ cookie: 'session_id' }]);
|
|
1534
|
+
*
|
|
1535
|
+
* // Check with custom JS function
|
|
1536
|
+
* const result = await page.isAuthenticated([
|
|
1537
|
+
* { fn: '() => !!document.querySelector("[data-user-id]")' },
|
|
1538
|
+
* ]);
|
|
1539
|
+
* ```
|
|
1540
|
+
*/
|
|
1541
|
+
isAuthenticated(rules: AuthCheckRule[]): Promise<AuthCheckResult>;
|
|
1446
1542
|
/**
|
|
1447
1543
|
* Get the underlying Playwright `Page` object for this tab.
|
|
1448
1544
|
*
|
|
@@ -1516,6 +1612,7 @@ declare class BrowserClaw {
|
|
|
1516
1612
|
private readonly ssrfPolicy;
|
|
1517
1613
|
private readonly recordVideo;
|
|
1518
1614
|
private chrome;
|
|
1615
|
+
private readonly _telemetry;
|
|
1519
1616
|
private constructor();
|
|
1520
1617
|
/**
|
|
1521
1618
|
* Launch a new Chrome instance and connect to it.
|
|
@@ -1631,8 +1728,42 @@ declare class BrowserClaw {
|
|
|
1631
1728
|
* If the browser was launched by `BrowserClaw.launch()`, the Chrome process
|
|
1632
1729
|
* will be terminated. If connected via `BrowserClaw.connect()`, only the
|
|
1633
1730
|
* Playwright connection is closed.
|
|
1731
|
+
*
|
|
1732
|
+
* @param exitReason - Optional structured reason for stopping (e.g. `'success'`, `'auth_failed'`, `'timeout'`)
|
|
1733
|
+
*/
|
|
1734
|
+
stop(exitReason?: ExitReason | (string & {})): Promise<void>;
|
|
1735
|
+
/**
|
|
1736
|
+
* Get structured telemetry for this browser session.
|
|
1737
|
+
*
|
|
1738
|
+
* Returns timing data, timestamps, and exit information collected
|
|
1739
|
+
* throughout the session lifecycle. Useful for diagnosing startup
|
|
1740
|
+
* latency, auth failures, and cleanup issues in cron/unattended runs.
|
|
1741
|
+
*
|
|
1742
|
+
* @returns Telemetry envelope with launch/connect/nav timings and exit info
|
|
1743
|
+
*
|
|
1744
|
+
* @example
|
|
1745
|
+
* ```ts
|
|
1746
|
+
* const browser = await BrowserClaw.launch({ url: 'https://example.com' });
|
|
1747
|
+
* const page = await browser.currentPage();
|
|
1748
|
+
*
|
|
1749
|
+
* // ... do work ...
|
|
1750
|
+
*
|
|
1751
|
+
* const auth = await page.isAuthenticated([{ cookie: 'session' }]);
|
|
1752
|
+
* browser.recordAuthResult(auth.authenticated);
|
|
1753
|
+
*
|
|
1754
|
+
* await browser.stop(auth.authenticated ? 'success' : 'auth_failed');
|
|
1755
|
+
* console.log(browser.telemetry());
|
|
1756
|
+
* // { launchMs: 1823, connectMs: 45, navMs: 620, authOk: true,
|
|
1757
|
+
* // exitReason: 'success', cleanupOk: true, timestamps: { ... } }
|
|
1758
|
+
* ```
|
|
1759
|
+
*/
|
|
1760
|
+
telemetry(): Readonly<RunTelemetry>;
|
|
1761
|
+
/**
|
|
1762
|
+
* Record the result of an authentication check in the telemetry envelope.
|
|
1763
|
+
*
|
|
1764
|
+
* @param ok - Whether authentication was successful
|
|
1634
1765
|
*/
|
|
1635
|
-
|
|
1766
|
+
recordAuthResult(ok: boolean): void;
|
|
1636
1767
|
}
|
|
1637
1768
|
|
|
1638
1769
|
/**
|
|
@@ -1890,4 +2021,4 @@ declare function waitForChallengeViaPlaywright(opts: {
|
|
|
1890
2021
|
pollMs?: number;
|
|
1891
2022
|
}): Promise<ChallengeWaitResult>;
|
|
1892
2023
|
|
|
1893
|
-
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BlockedBrowserTargetError, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightConnection, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingPathsWithinRoot, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
|
2024
|
+
export { type AriaNode, type AriaSnapshotResult, type AuthCheckDetail, type AuthCheckResult, type AuthCheckRule, type AuthCheckRuleKind, type BatchAction, type BatchActionResult, BlockedBrowserTargetError, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type ExitReason, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type RunTelemetry, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightConnection, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingPathsWithinRoot, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
package/dist/index.d.ts
CHANGED
|
@@ -628,6 +628,72 @@ interface ChallengeWaitResult {
|
|
|
628
628
|
/** The challenge still present (null if resolved) */
|
|
629
629
|
challenge: ChallengeInfo | null;
|
|
630
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* A single rule for checking whether the browser session is authenticated.
|
|
633
|
+
* Multiple rules can be combined — all must pass for `authenticated: true`.
|
|
634
|
+
*/
|
|
635
|
+
interface AuthCheckRule {
|
|
636
|
+
/** URL must contain this substring (checked against page URL) */
|
|
637
|
+
url?: string;
|
|
638
|
+
/** A cookie with this name must exist (non-empty value) */
|
|
639
|
+
cookie?: string;
|
|
640
|
+
/** A CSS selector that must match at least one element on the page (includes hidden elements) */
|
|
641
|
+
selector?: string;
|
|
642
|
+
/** Text that must be present on the page */
|
|
643
|
+
text?: string;
|
|
644
|
+
/** Text that must NOT be present on the page (e.g. "Sign in", "Log in") */
|
|
645
|
+
textGone?: string;
|
|
646
|
+
/** JavaScript function (as string) that must return a truthy value in the browser context */
|
|
647
|
+
fn?: string;
|
|
648
|
+
}
|
|
649
|
+
/** The kind of auth check rule that was evaluated. */
|
|
650
|
+
type AuthCheckRuleKind = 'url' | 'cookie' | 'selector' | 'text' | 'textGone' | 'fn';
|
|
651
|
+
/** Result of a single auth check rule evaluation. */
|
|
652
|
+
interface AuthCheckDetail {
|
|
653
|
+
/** Which rule type was checked */
|
|
654
|
+
rule: AuthCheckRuleKind;
|
|
655
|
+
/** Whether this individual check passed */
|
|
656
|
+
passed: boolean;
|
|
657
|
+
/** Human-readable detail (e.g. the actual URL, cookie value presence, etc.) */
|
|
658
|
+
detail?: string;
|
|
659
|
+
}
|
|
660
|
+
/** Result of an `isAuthenticated()` call. */
|
|
661
|
+
interface AuthCheckResult {
|
|
662
|
+
/** `true` only if ALL rules passed */
|
|
663
|
+
authenticated: boolean;
|
|
664
|
+
/** Per-rule results for debugging */
|
|
665
|
+
checks: AuthCheckDetail[];
|
|
666
|
+
}
|
|
667
|
+
/** Standard exit reasons for structured telemetry. */
|
|
668
|
+
type ExitReason = 'success' | 'auth_failed' | 'nav_failed' | 'timeout' | 'crash' | 'disconnected' | 'manual' | 'error';
|
|
669
|
+
/** Structured telemetry envelope for a browser session lifecycle. */
|
|
670
|
+
interface RunTelemetry {
|
|
671
|
+
/** Milliseconds to launch Chrome (undefined if connected to existing instance) */
|
|
672
|
+
launchMs?: number;
|
|
673
|
+
/** Milliseconds to establish CDP connection */
|
|
674
|
+
connectMs?: number;
|
|
675
|
+
/** Milliseconds for initial navigation (if a URL was provided at launch) */
|
|
676
|
+
navMs?: number;
|
|
677
|
+
/** Whether auth verification passed (undefined if not checked) */
|
|
678
|
+
authOk?: boolean;
|
|
679
|
+
/** Structured exit reason when the session ends */
|
|
680
|
+
exitReason?: ExitReason | (string & {});
|
|
681
|
+
/** Whether cleanup (process kill, connection close) succeeded */
|
|
682
|
+
cleanupOk?: boolean;
|
|
683
|
+
/** Key timestamps in ISO 8601 format */
|
|
684
|
+
timestamps: {
|
|
685
|
+
/** When the launch/connect sequence started */
|
|
686
|
+
startedAt: string;
|
|
687
|
+
/** When Chrome process became reachable */
|
|
688
|
+
launchedAt?: string;
|
|
689
|
+
/** When Playwright CDP connection was established */
|
|
690
|
+
connectedAt?: string;
|
|
691
|
+
/** When initial navigation completed */
|
|
692
|
+
navigatedAt?: string;
|
|
693
|
+
/** When stop() was called */
|
|
694
|
+
stoppedAt?: string;
|
|
695
|
+
};
|
|
696
|
+
}
|
|
631
697
|
/** Result of DNS pinning resolution — hostname locked to resolved addresses. */
|
|
632
698
|
interface PinnedHostname {
|
|
633
699
|
hostname: string;
|
|
@@ -1443,6 +1509,36 @@ declare class CrawlPage {
|
|
|
1443
1509
|
timeoutMs?: number;
|
|
1444
1510
|
pollMs?: number;
|
|
1445
1511
|
}): Promise<ChallengeWaitResult>;
|
|
1512
|
+
/**
|
|
1513
|
+
* Check whether the current page session appears authenticated.
|
|
1514
|
+
*
|
|
1515
|
+
* Evaluates one or more rules against the page state. All rules must pass
|
|
1516
|
+
* for `authenticated` to be `true`. Returns per-rule details for debugging.
|
|
1517
|
+
*
|
|
1518
|
+
* @param rules - Array of auth check rules (url, cookie, selector, text, textGone, fn)
|
|
1519
|
+
* @returns Authentication status and per-rule check details
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* ```ts
|
|
1523
|
+
* // Check by URL and absence of login text
|
|
1524
|
+
* const result = await page.isAuthenticated([
|
|
1525
|
+
* { url: '/dashboard' },
|
|
1526
|
+
* { textGone: 'Sign in' },
|
|
1527
|
+
* ]);
|
|
1528
|
+
* if (!result.authenticated) {
|
|
1529
|
+
* console.log('Auth failed:', result.checks.filter(c => !c.passed));
|
|
1530
|
+
* }
|
|
1531
|
+
*
|
|
1532
|
+
* // Check by cookie presence
|
|
1533
|
+
* const result = await page.isAuthenticated([{ cookie: 'session_id' }]);
|
|
1534
|
+
*
|
|
1535
|
+
* // Check with custom JS function
|
|
1536
|
+
* const result = await page.isAuthenticated([
|
|
1537
|
+
* { fn: '() => !!document.querySelector("[data-user-id]")' },
|
|
1538
|
+
* ]);
|
|
1539
|
+
* ```
|
|
1540
|
+
*/
|
|
1541
|
+
isAuthenticated(rules: AuthCheckRule[]): Promise<AuthCheckResult>;
|
|
1446
1542
|
/**
|
|
1447
1543
|
* Get the underlying Playwright `Page` object for this tab.
|
|
1448
1544
|
*
|
|
@@ -1516,6 +1612,7 @@ declare class BrowserClaw {
|
|
|
1516
1612
|
private readonly ssrfPolicy;
|
|
1517
1613
|
private readonly recordVideo;
|
|
1518
1614
|
private chrome;
|
|
1615
|
+
private readonly _telemetry;
|
|
1519
1616
|
private constructor();
|
|
1520
1617
|
/**
|
|
1521
1618
|
* Launch a new Chrome instance and connect to it.
|
|
@@ -1631,8 +1728,42 @@ declare class BrowserClaw {
|
|
|
1631
1728
|
* If the browser was launched by `BrowserClaw.launch()`, the Chrome process
|
|
1632
1729
|
* will be terminated. If connected via `BrowserClaw.connect()`, only the
|
|
1633
1730
|
* Playwright connection is closed.
|
|
1731
|
+
*
|
|
1732
|
+
* @param exitReason - Optional structured reason for stopping (e.g. `'success'`, `'auth_failed'`, `'timeout'`)
|
|
1733
|
+
*/
|
|
1734
|
+
stop(exitReason?: ExitReason | (string & {})): Promise<void>;
|
|
1735
|
+
/**
|
|
1736
|
+
* Get structured telemetry for this browser session.
|
|
1737
|
+
*
|
|
1738
|
+
* Returns timing data, timestamps, and exit information collected
|
|
1739
|
+
* throughout the session lifecycle. Useful for diagnosing startup
|
|
1740
|
+
* latency, auth failures, and cleanup issues in cron/unattended runs.
|
|
1741
|
+
*
|
|
1742
|
+
* @returns Telemetry envelope with launch/connect/nav timings and exit info
|
|
1743
|
+
*
|
|
1744
|
+
* @example
|
|
1745
|
+
* ```ts
|
|
1746
|
+
* const browser = await BrowserClaw.launch({ url: 'https://example.com' });
|
|
1747
|
+
* const page = await browser.currentPage();
|
|
1748
|
+
*
|
|
1749
|
+
* // ... do work ...
|
|
1750
|
+
*
|
|
1751
|
+
* const auth = await page.isAuthenticated([{ cookie: 'session' }]);
|
|
1752
|
+
* browser.recordAuthResult(auth.authenticated);
|
|
1753
|
+
*
|
|
1754
|
+
* await browser.stop(auth.authenticated ? 'success' : 'auth_failed');
|
|
1755
|
+
* console.log(browser.telemetry());
|
|
1756
|
+
* // { launchMs: 1823, connectMs: 45, navMs: 620, authOk: true,
|
|
1757
|
+
* // exitReason: 'success', cleanupOk: true, timestamps: { ... } }
|
|
1758
|
+
* ```
|
|
1759
|
+
*/
|
|
1760
|
+
telemetry(): Readonly<RunTelemetry>;
|
|
1761
|
+
/**
|
|
1762
|
+
* Record the result of an authentication check in the telemetry envelope.
|
|
1763
|
+
*
|
|
1764
|
+
* @param ok - Whether authentication was successful
|
|
1634
1765
|
*/
|
|
1635
|
-
|
|
1766
|
+
recordAuthResult(ok: boolean): void;
|
|
1636
1767
|
}
|
|
1637
1768
|
|
|
1638
1769
|
/**
|
|
@@ -1890,4 +2021,4 @@ declare function waitForChallengeViaPlaywright(opts: {
|
|
|
1890
2021
|
pollMs?: number;
|
|
1891
2022
|
}): Promise<ChallengeWaitResult>;
|
|
1892
2023
|
|
|
1893
|
-
export { type AriaNode, type AriaSnapshotResult, type BatchAction, type BatchActionResult, BlockedBrowserTargetError, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightConnection, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingPathsWithinRoot, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
|
2024
|
+
export { type AriaNode, type AriaSnapshotResult, type AuthCheckDetail, type AuthCheckResult, type AuthCheckRule, type AuthCheckRuleKind, type BatchAction, type BatchActionResult, BlockedBrowserTargetError, BrowserClaw, type BrowserNavigationPolicyOptions, type BrowserNavigationRequestLike, type BrowserTab, BrowserTabNotFoundError, type ChallengeInfo, type ChallengeKind, type ChallengeWaitResult, type ChromeExecutable, type ChromeKind, type ClickOptions, type ColorScheme, type ConnectOptions, type ConsoleMessage, type ContextState, type CookieData, CrawlPage, type DialogEvent, type DialogHandler, type DialogOptions, type DownloadResult, type ExitReason, type FormField, type FrameEvalResult, type GeolocationOptions, type HttpCredentials, InvalidBrowserNavigationUrlError, type LaunchOptions, type LookupFn, type NetworkRequest, type PageError, type PinnedHostname, type RequestResult, type ResponseBodyResult, type RoleRefInfo, type RoleRefs, type RunTelemetry, STEALTH_SCRIPT, type ScreenshotOptions, type SnapshotOptions, type SnapshotResult, type SnapshotStats, type SsrfPolicy, type StorageKind, type TraceStartOptions, type TypeOptions, type UntrustedContentMeta, type WaitOptions, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightConnection, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingPathsWithinRoot, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|
package/dist/index.js
CHANGED
|
@@ -1562,6 +1562,7 @@ ${stderrOutput.slice(0, 2e3)}` : "";
|
|
|
1562
1562
|
userDataDir,
|
|
1563
1563
|
cdpPort,
|
|
1564
1564
|
startedAt,
|
|
1565
|
+
launchMs: Date.now() - startedAt,
|
|
1565
1566
|
proc
|
|
1566
1567
|
};
|
|
1567
1568
|
}
|
|
@@ -6316,6 +6317,137 @@ var CrawlPage = class {
|
|
|
6316
6317
|
pollMs: opts?.pollMs
|
|
6317
6318
|
});
|
|
6318
6319
|
}
|
|
6320
|
+
// ── Auth Health ──────────────────────────────────────────────
|
|
6321
|
+
/**
|
|
6322
|
+
* Check whether the current page session appears authenticated.
|
|
6323
|
+
*
|
|
6324
|
+
* Evaluates one or more rules against the page state. All rules must pass
|
|
6325
|
+
* for `authenticated` to be `true`. Returns per-rule details for debugging.
|
|
6326
|
+
*
|
|
6327
|
+
* @param rules - Array of auth check rules (url, cookie, selector, text, textGone, fn)
|
|
6328
|
+
* @returns Authentication status and per-rule check details
|
|
6329
|
+
*
|
|
6330
|
+
* @example
|
|
6331
|
+
* ```ts
|
|
6332
|
+
* // Check by URL and absence of login text
|
|
6333
|
+
* const result = await page.isAuthenticated([
|
|
6334
|
+
* { url: '/dashboard' },
|
|
6335
|
+
* { textGone: 'Sign in' },
|
|
6336
|
+
* ]);
|
|
6337
|
+
* if (!result.authenticated) {
|
|
6338
|
+
* console.log('Auth failed:', result.checks.filter(c => !c.passed));
|
|
6339
|
+
* }
|
|
6340
|
+
*
|
|
6341
|
+
* // Check by cookie presence
|
|
6342
|
+
* const result = await page.isAuthenticated([{ cookie: 'session_id' }]);
|
|
6343
|
+
*
|
|
6344
|
+
* // Check with custom JS function
|
|
6345
|
+
* const result = await page.isAuthenticated([
|
|
6346
|
+
* { fn: '() => !!document.querySelector("[data-user-id]")' },
|
|
6347
|
+
* ]);
|
|
6348
|
+
* ```
|
|
6349
|
+
*/
|
|
6350
|
+
async isAuthenticated(rules) {
|
|
6351
|
+
if (!rules.length) return { authenticated: true, checks: [] };
|
|
6352
|
+
const page = await getRestoredPageForTarget({ cdpUrl: this.cdpUrl, targetId: this.targetId });
|
|
6353
|
+
const checks = [];
|
|
6354
|
+
for (const rule of rules) {
|
|
6355
|
+
if (rule.url !== void 0) {
|
|
6356
|
+
const currentUrl = page.url();
|
|
6357
|
+
const passed = currentUrl.includes(rule.url);
|
|
6358
|
+
checks.push({ rule: "url", passed, detail: passed ? currentUrl : `expected "${rule.url}" in "${currentUrl}"` });
|
|
6359
|
+
}
|
|
6360
|
+
if (rule.cookie !== void 0) {
|
|
6361
|
+
const cookies = await page.context().cookies();
|
|
6362
|
+
const found = cookies.some((c) => c.name === rule.cookie && c.value !== "");
|
|
6363
|
+
checks.push({
|
|
6364
|
+
rule: "cookie",
|
|
6365
|
+
passed: found,
|
|
6366
|
+
detail: found ? `cookie "${rule.cookie}" present` : `cookie "${rule.cookie}" missing or empty`
|
|
6367
|
+
});
|
|
6368
|
+
}
|
|
6369
|
+
if (rule.selector !== void 0) {
|
|
6370
|
+
try {
|
|
6371
|
+
const count = await page.locator(rule.selector).count();
|
|
6372
|
+
const passed = count > 0;
|
|
6373
|
+
checks.push({
|
|
6374
|
+
rule: "selector",
|
|
6375
|
+
passed,
|
|
6376
|
+
detail: passed ? `"${rule.selector}" found (${String(count)})` : `"${rule.selector}" not found`
|
|
6377
|
+
});
|
|
6378
|
+
} catch (err) {
|
|
6379
|
+
console.warn(
|
|
6380
|
+
`[browserclaw] isAuthenticated selector check failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6381
|
+
);
|
|
6382
|
+
checks.push({ rule: "selector", passed: false, detail: `"${rule.selector}" error during evaluation` });
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
if (rule.text !== void 0 || rule.textGone !== void 0) {
|
|
6386
|
+
let bodyText = null;
|
|
6387
|
+
try {
|
|
6388
|
+
const raw = await evaluateViaPlaywright({
|
|
6389
|
+
cdpUrl: this.cdpUrl,
|
|
6390
|
+
targetId: this.targetId,
|
|
6391
|
+
fn: '() => { const b = document.body; return b ? b.innerText : ""; }'
|
|
6392
|
+
});
|
|
6393
|
+
bodyText = typeof raw === "string" ? raw : null;
|
|
6394
|
+
} catch (err) {
|
|
6395
|
+
console.warn(
|
|
6396
|
+
`[browserclaw] isAuthenticated body text fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6397
|
+
);
|
|
6398
|
+
}
|
|
6399
|
+
if (rule.text !== void 0) {
|
|
6400
|
+
if (bodyText === null) {
|
|
6401
|
+
checks.push({ rule: "text", passed: false, detail: `"${rule.text}" error during evaluation` });
|
|
6402
|
+
} else {
|
|
6403
|
+
const passed = bodyText.includes(rule.text);
|
|
6404
|
+
checks.push({
|
|
6405
|
+
rule: "text",
|
|
6406
|
+
passed,
|
|
6407
|
+
detail: passed ? `"${rule.text}" found` : `"${rule.text}" not found in page text`
|
|
6408
|
+
});
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
if (rule.textGone !== void 0) {
|
|
6412
|
+
if (bodyText === null) {
|
|
6413
|
+
checks.push({ rule: "textGone", passed: false, detail: `"${rule.textGone}" error during evaluation` });
|
|
6414
|
+
} else {
|
|
6415
|
+
const passed = !bodyText.includes(rule.textGone);
|
|
6416
|
+
checks.push({
|
|
6417
|
+
rule: "textGone",
|
|
6418
|
+
passed,
|
|
6419
|
+
detail: passed ? `"${rule.textGone}" absent (good)` : `"${rule.textGone}" still present`
|
|
6420
|
+
});
|
|
6421
|
+
}
|
|
6422
|
+
}
|
|
6423
|
+
}
|
|
6424
|
+
if (rule.fn !== void 0) {
|
|
6425
|
+
try {
|
|
6426
|
+
const result = await evaluateViaPlaywright({
|
|
6427
|
+
cdpUrl: this.cdpUrl,
|
|
6428
|
+
targetId: this.targetId,
|
|
6429
|
+
fn: rule.fn
|
|
6430
|
+
});
|
|
6431
|
+
const passed = result !== null && result !== void 0 && result !== false && result !== 0 && result !== "";
|
|
6432
|
+
checks.push({
|
|
6433
|
+
rule: "fn",
|
|
6434
|
+
passed,
|
|
6435
|
+
detail: passed ? "function returned truthy" : `function returned ${JSON.stringify(result)}`
|
|
6436
|
+
});
|
|
6437
|
+
} catch (err) {
|
|
6438
|
+
checks.push({
|
|
6439
|
+
rule: "fn",
|
|
6440
|
+
passed: false,
|
|
6441
|
+
detail: `function threw: ${err instanceof Error ? err.message : String(err)}`
|
|
6442
|
+
});
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
}
|
|
6446
|
+
return {
|
|
6447
|
+
authenticated: checks.length > 0 && checks.every((c) => c.passed),
|
|
6448
|
+
checks
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6319
6451
|
// ── Playwright Escape Hatches ─────────────────────────────────
|
|
6320
6452
|
/**
|
|
6321
6453
|
* Get the underlying Playwright `Page` object for this tab.
|
|
@@ -6374,9 +6506,11 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6374
6506
|
ssrfPolicy;
|
|
6375
6507
|
recordVideo;
|
|
6376
6508
|
chrome;
|
|
6377
|
-
|
|
6509
|
+
_telemetry;
|
|
6510
|
+
constructor(cdpUrl, chrome, telemetry, ssrfPolicy, recordVideo) {
|
|
6378
6511
|
this.cdpUrl = cdpUrl;
|
|
6379
6512
|
this.chrome = chrome;
|
|
6513
|
+
this._telemetry = telemetry;
|
|
6380
6514
|
this.ssrfPolicy = ssrfPolicy;
|
|
6381
6515
|
this.recordVideo = recordVideo;
|
|
6382
6516
|
}
|
|
@@ -6405,13 +6539,21 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6405
6539
|
* ```
|
|
6406
6540
|
*/
|
|
6407
6541
|
static async launch(opts = {}) {
|
|
6542
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6408
6543
|
const chrome = await launchChrome(opts);
|
|
6409
6544
|
const cdpUrl = `http://127.0.0.1:${String(chrome.cdpPort)}`;
|
|
6410
6545
|
const ssrfPolicy = opts.allowInternal === true ? { ...opts.ssrfPolicy, dangerouslyAllowPrivateNetwork: true } : opts.ssrfPolicy;
|
|
6411
|
-
const
|
|
6546
|
+
const telemetry = {
|
|
6547
|
+
launchMs: chrome.launchMs,
|
|
6548
|
+
timestamps: { startedAt, launchedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
6549
|
+
};
|
|
6550
|
+
const browser = new _BrowserClaw(cdpUrl, chrome, telemetry, ssrfPolicy, opts.recordVideo);
|
|
6412
6551
|
if (opts.url !== void 0 && opts.url !== "") {
|
|
6413
6552
|
const page = await browser.currentPage();
|
|
6553
|
+
const navT0 = Date.now();
|
|
6414
6554
|
await page.goto(opts.url);
|
|
6555
|
+
telemetry.navMs = Date.now() - navT0;
|
|
6556
|
+
telemetry.timestamps.navigatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6415
6557
|
}
|
|
6416
6558
|
return browser;
|
|
6417
6559
|
}
|
|
@@ -6430,6 +6572,8 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6430
6572
|
* ```
|
|
6431
6573
|
*/
|
|
6432
6574
|
static async connect(cdpUrl, opts) {
|
|
6575
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6576
|
+
const connectT0 = Date.now();
|
|
6433
6577
|
let resolvedUrl = cdpUrl;
|
|
6434
6578
|
if (resolvedUrl === void 0 || resolvedUrl === "") {
|
|
6435
6579
|
const discovered = await discoverChromeCdpUrl();
|
|
@@ -6445,7 +6589,11 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6445
6589
|
}
|
|
6446
6590
|
await connectBrowser(resolvedUrl, opts?.authToken);
|
|
6447
6591
|
const ssrfPolicy = opts?.allowInternal === true ? { ...opts.ssrfPolicy, dangerouslyAllowPrivateNetwork: true } : opts?.ssrfPolicy;
|
|
6448
|
-
|
|
6592
|
+
const telemetry = {
|
|
6593
|
+
connectMs: Date.now() - connectT0,
|
|
6594
|
+
timestamps: { startedAt, connectedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
6595
|
+
};
|
|
6596
|
+
return new _BrowserClaw(resolvedUrl, null, telemetry, ssrfPolicy, opts?.recordVideo);
|
|
6449
6597
|
}
|
|
6450
6598
|
/**
|
|
6451
6599
|
* Open a URL in a new tab and return the page handle.
|
|
@@ -6474,7 +6622,12 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6474
6622
|
* @returns CrawlPage for the first/active page
|
|
6475
6623
|
*/
|
|
6476
6624
|
async currentPage() {
|
|
6625
|
+
const connectT0 = Date.now();
|
|
6477
6626
|
const { browser } = await connectBrowser(this.cdpUrl);
|
|
6627
|
+
if (this._telemetry.connectMs === void 0) {
|
|
6628
|
+
this._telemetry.connectMs = Date.now() - connectT0;
|
|
6629
|
+
this._telemetry.timestamps.connectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6630
|
+
}
|
|
6478
6631
|
const pages = getAllPages(browser);
|
|
6479
6632
|
if (!pages.length) throw new Error("No pages available. Use browser.open(url) to create a tab.");
|
|
6480
6633
|
const tid = await pageTargetId(pages[0]).catch(() => null);
|
|
@@ -6551,15 +6704,61 @@ var BrowserClaw = class _BrowserClaw {
|
|
|
6551
6704
|
* If the browser was launched by `BrowserClaw.launch()`, the Chrome process
|
|
6552
6705
|
* will be terminated. If connected via `BrowserClaw.connect()`, only the
|
|
6553
6706
|
* Playwright connection is closed.
|
|
6707
|
+
*
|
|
6708
|
+
* @param exitReason - Optional structured reason for stopping (e.g. `'success'`, `'auth_failed'`, `'timeout'`)
|
|
6554
6709
|
*/
|
|
6555
|
-
async stop() {
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6710
|
+
async stop(exitReason) {
|
|
6711
|
+
this._telemetry.timestamps.stoppedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6712
|
+
if (exitReason !== void 0) this._telemetry.exitReason = exitReason;
|
|
6713
|
+
try {
|
|
6714
|
+
clearRecordingContext(this.cdpUrl);
|
|
6715
|
+
await disconnectBrowser();
|
|
6716
|
+
if (this.chrome) {
|
|
6717
|
+
await stopChrome(this.chrome);
|
|
6718
|
+
this.chrome = null;
|
|
6719
|
+
}
|
|
6720
|
+
this._telemetry.cleanupOk = true;
|
|
6721
|
+
} catch (err) {
|
|
6722
|
+
this._telemetry.cleanupOk = false;
|
|
6723
|
+
throw err;
|
|
6561
6724
|
}
|
|
6562
6725
|
}
|
|
6726
|
+
/**
|
|
6727
|
+
* Get structured telemetry for this browser session.
|
|
6728
|
+
*
|
|
6729
|
+
* Returns timing data, timestamps, and exit information collected
|
|
6730
|
+
* throughout the session lifecycle. Useful for diagnosing startup
|
|
6731
|
+
* latency, auth failures, and cleanup issues in cron/unattended runs.
|
|
6732
|
+
*
|
|
6733
|
+
* @returns Telemetry envelope with launch/connect/nav timings and exit info
|
|
6734
|
+
*
|
|
6735
|
+
* @example
|
|
6736
|
+
* ```ts
|
|
6737
|
+
* const browser = await BrowserClaw.launch({ url: 'https://example.com' });
|
|
6738
|
+
* const page = await browser.currentPage();
|
|
6739
|
+
*
|
|
6740
|
+
* // ... do work ...
|
|
6741
|
+
*
|
|
6742
|
+
* const auth = await page.isAuthenticated([{ cookie: 'session' }]);
|
|
6743
|
+
* browser.recordAuthResult(auth.authenticated);
|
|
6744
|
+
*
|
|
6745
|
+
* await browser.stop(auth.authenticated ? 'success' : 'auth_failed');
|
|
6746
|
+
* console.log(browser.telemetry());
|
|
6747
|
+
* // { launchMs: 1823, connectMs: 45, navMs: 620, authOk: true,
|
|
6748
|
+
* // exitReason: 'success', cleanupOk: true, timestamps: { ... } }
|
|
6749
|
+
* ```
|
|
6750
|
+
*/
|
|
6751
|
+
telemetry() {
|
|
6752
|
+
return this._telemetry;
|
|
6753
|
+
}
|
|
6754
|
+
/**
|
|
6755
|
+
* Record the result of an authentication check in the telemetry envelope.
|
|
6756
|
+
*
|
|
6757
|
+
* @param ok - Whether authentication was successful
|
|
6758
|
+
*/
|
|
6759
|
+
recordAuthResult(ok) {
|
|
6760
|
+
this._telemetry.authOk = ok;
|
|
6761
|
+
}
|
|
6563
6762
|
};
|
|
6564
6763
|
|
|
6565
6764
|
export { BlockedBrowserTargetError, BrowserClaw, BrowserTabNotFoundError, CrawlPage, InvalidBrowserNavigationUrlError, STEALTH_SCRIPT, assertBrowserNavigationAllowed, assertBrowserNavigationRedirectChainAllowed, assertBrowserNavigationResultAllowed, assertSafeUploadPaths, batchViaPlaywright, createPinnedLookup, detectChallengeViaPlaywright, ensureContextState, executeSingleAction, forceDisconnectPlaywrightConnection, forceDisconnectPlaywrightForTarget, getChromeWebSocketUrl, getRestoredPageForTarget, isChromeCdpReady, isChromeReachable, normalizeCdpHttpBaseForJsonEndpoints, parseRoleRef, pressAndHoldViaCdp, requireRef, requireRefOrSelector, requiresInspectableBrowserNavigationRedirects, resolveBoundedDelayMs, resolveInteractionTimeoutMs, resolvePageByTargetIdOrThrow, resolvePinnedHostnameWithPolicy, resolveStrictExistingPathsWithinRoot, resolveStrictExistingUploadPaths, sanitizeUntrustedFileName, setDialogHandler, waitForChallengeViaPlaywright, withBrowserNavigationPolicy, withPageScopedCdpClient, withPlaywrightPageCdpSession, writeViaSiblingTempPath };
|