@hira-core/sdk 1.0.5 → 1.0.7
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.d.ts +327 -29
- package/dist/index.js +540 -133
- package/package.json +1 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _packages_shared from '@packages/shared';
|
|
2
2
|
import { ProfileOutputValue, IWorkerLogMessage, ProfileStatus, IWorkerOutputMessage } from '@packages/shared';
|
|
3
|
-
import * as
|
|
3
|
+
import * as puppeteer_core from 'puppeteer-core';
|
|
4
4
|
import { Browser, Page, Frame, ElementHandle } from 'puppeteer-core';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
|
|
@@ -521,8 +521,8 @@ declare class GpmStandaloneAdapter implements IBrowserAdapter {
|
|
|
521
521
|
constructor(logger: ILogger);
|
|
522
522
|
private getBoundLogger;
|
|
523
523
|
open(profileName: string, index: number, windowConfig: IBrowserWindowConfig): Promise<{
|
|
524
|
-
browser:
|
|
525
|
-
page:
|
|
524
|
+
browser: puppeteer_core.Browser;
|
|
525
|
+
page: puppeteer_core.Page;
|
|
526
526
|
profile: IAntidetectProfile;
|
|
527
527
|
}>;
|
|
528
528
|
close(profileId: string): Promise<void>;
|
|
@@ -595,8 +595,8 @@ declare class HidemiumStandaloneAdapter implements IBrowserAdapter {
|
|
|
595
595
|
constructor(logger: ILogger);
|
|
596
596
|
private getBoundLogger;
|
|
597
597
|
open(profileName: string, index: number, windowConfig: IBrowserWindowConfig): Promise<{
|
|
598
|
-
browser:
|
|
599
|
-
page:
|
|
598
|
+
browser: puppeteer_core.Browser;
|
|
599
|
+
page: puppeteer_core.Page;
|
|
600
600
|
profile: IAntidetectProfile;
|
|
601
601
|
}>;
|
|
602
602
|
close(profileId: string): Promise<void>;
|
|
@@ -606,72 +606,370 @@ declare class HidemiumStandaloneAdapter implements IBrowserAdapter {
|
|
|
606
606
|
declare class BrowserUtils<TConfig extends IFlowConfig = IFlowConfig> {
|
|
607
607
|
private ctx;
|
|
608
608
|
private logger;
|
|
609
|
-
/** Output definitions
|
|
609
|
+
/** Output definitions from flow config — used to validate writeOutput keys */
|
|
610
610
|
private readonly outputDefs;
|
|
611
|
-
/**
|
|
611
|
+
/** Valid output keys set — built once from outputDefs */
|
|
612
612
|
private readonly validOutputKeys;
|
|
613
|
+
/** The initial page when flow starts — activeDefault() returns here */
|
|
614
|
+
private readonly defaultPage;
|
|
615
|
+
/** The currently active page all methods operate on — changed via activeTab() */
|
|
616
|
+
private activePage;
|
|
617
|
+
/** Currently active iframe — null means main frame. Changed via activeIframe() */
|
|
618
|
+
private activeFrame;
|
|
613
619
|
constructor(context: IScriptContext<TConfig>);
|
|
620
|
+
/**
|
|
621
|
+
* CDP trick — Chromium nghĩ tab luôn focused, không bị throttle
|
|
622
|
+
* khi user chuyển sang tab khác hoặc click vào trình duyệt.
|
|
623
|
+
*/
|
|
624
|
+
private enableFocusEmulation;
|
|
614
625
|
private checkAbort;
|
|
626
|
+
/**
|
|
627
|
+
* Pause execution for the given duration.
|
|
628
|
+
*
|
|
629
|
+
* @param ms - Duration in milliseconds
|
|
630
|
+
* @example await utils.sleep(2000); // wait 2 seconds
|
|
631
|
+
*/
|
|
615
632
|
sleep(ms: number): Promise<void>;
|
|
633
|
+
/**
|
|
634
|
+
* Wait for an element to appear and become visible in the DOM.
|
|
635
|
+
* Returns null if not found (soft fail — does NOT throw).
|
|
636
|
+
* Supports CSS selectors and XPath (auto-detected by `//` or `(` prefix).
|
|
637
|
+
*
|
|
638
|
+
* @param selector - CSS selector or XPath expression
|
|
639
|
+
* @param timeout - Max wait time in ms (default: 8000)
|
|
640
|
+
* @param scope - Optional Frame to search within
|
|
641
|
+
* @returns The element handle, or null if not found
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* const el = await utils.waitForElement("#my-btn");
|
|
645
|
+
* if (el) await el.click();
|
|
646
|
+
*/
|
|
616
647
|
waitForElement(selector: string, timeout?: number, scope?: Frame): Promise<ElementHandle | null>;
|
|
648
|
+
/**
|
|
649
|
+
* Resolve an element: if waitTimeout > 0, wait for it; otherwise query directly with $().
|
|
650
|
+
*/
|
|
651
|
+
private resolveElement;
|
|
652
|
+
/**
|
|
653
|
+
* Click on an element. Scrolls the element into view before clicking.
|
|
654
|
+
* Throws if the element is not found.
|
|
655
|
+
*
|
|
656
|
+
* @param target - CSS selector, XPath, or an existing ElementHandle
|
|
657
|
+
* @param options.delay - Delay in ms before clicking (default: 1000)
|
|
658
|
+
* @param options.waitTimeout - Max wait time for element to appear (default: 2000)
|
|
659
|
+
* @param options.frame - Optional Frame to search within
|
|
660
|
+
* @returns true on success
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* await utils.click("#submit-btn");
|
|
664
|
+
* await utils.click("#btn", { delay: 0 }); // click immediately
|
|
665
|
+
* await utils.click("#btn", { waitTimeout: 8000 }); // wait longer
|
|
666
|
+
*/
|
|
617
667
|
click(target: string | ElementHandle, options?: {
|
|
618
668
|
delay?: number;
|
|
619
|
-
|
|
669
|
+
waitTimeout?: number;
|
|
620
670
|
frame?: Frame;
|
|
621
671
|
}): Promise<boolean>;
|
|
672
|
+
/**
|
|
673
|
+
* Type text into an input element. Throws if the element is not found.
|
|
674
|
+
*
|
|
675
|
+
* @param selector - CSS selector or XPath of the input
|
|
676
|
+
* @param text - The text to type
|
|
677
|
+
* @param options.mode - Typing mode:
|
|
678
|
+
* - `"replace"` (default): Clear existing text, then type new text
|
|
679
|
+
* - `"append"`: Type without clearing — appends to existing text
|
|
680
|
+
* - `"paste"`: Set value directly via JS (fast, no keystroke simulation)
|
|
681
|
+
* @param options.delay - Delay between keystrokes in ms (default: 50). Ignored in paste mode.
|
|
682
|
+
* @param options.waitTimeout - Max wait time for element to appear in ms (default: 0 — instant)
|
|
683
|
+
* @param options.frame - Optional Frame to search within
|
|
684
|
+
* @returns true on success
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* await utils.type("#email", "user@example.com"); // replace mode
|
|
688
|
+
* await utils.type("#input", " more text", { mode: "append" }); // append
|
|
689
|
+
* await utils.type("#address", "0x1234...abcd", { mode: "paste" }); // instant paste
|
|
690
|
+
*/
|
|
622
691
|
type(selector: string, text: string, options?: {
|
|
692
|
+
/** Typing mode: replace (default) = clear then type, append = type without clearing, paste = set value directly */
|
|
693
|
+
mode?: "replace" | "append" | "paste";
|
|
623
694
|
delay?: number;
|
|
695
|
+
waitTimeout?: number;
|
|
624
696
|
frame?: Frame;
|
|
625
697
|
}): Promise<boolean>;
|
|
626
|
-
|
|
698
|
+
/**
|
|
699
|
+
* Get the text content of an element. Returns null if not found (soft fail).
|
|
700
|
+
*
|
|
701
|
+
* @param selector - CSS selector or XPath
|
|
702
|
+
* @param options.waitTimeout - Max wait time for element to appear in ms (default: 0 — instant)
|
|
703
|
+
* @param options.frame - Optional Frame to search within
|
|
704
|
+
* @returns Trimmed text content, or null if element not found
|
|
705
|
+
*
|
|
706
|
+
* @example
|
|
707
|
+
* const price = await utils.getText(".price");
|
|
708
|
+
* const el = await utils.getText(".lazy-el", { waitTimeout: 8000 });
|
|
709
|
+
*/
|
|
710
|
+
getText(selector: string, options?: {
|
|
711
|
+
waitTimeout?: number;
|
|
712
|
+
frame?: Frame;
|
|
713
|
+
}): Promise<string | null>;
|
|
714
|
+
/**
|
|
715
|
+
* Check if an element exists and is visible on the page.
|
|
716
|
+
* Returns false if not found (soft fail — does NOT throw).
|
|
717
|
+
*
|
|
718
|
+
* @param selector - CSS selector or XPath
|
|
719
|
+
* @param timeout - Max wait time in ms (default: 4000)
|
|
720
|
+
* @param frame - Optional Frame to search within
|
|
721
|
+
* @returns true if element exists and is visible
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* if (await utils.exists("#popup-overlay")) {
|
|
725
|
+
* await utils.click("#close-popup");
|
|
726
|
+
* }
|
|
727
|
+
*/
|
|
627
728
|
exists(selector: string, timeout?: number, frame?: Frame): Promise<boolean>;
|
|
729
|
+
/**
|
|
730
|
+
* Navigate the active page to a URL. Waits until the page fully loads.
|
|
731
|
+
* Throws on navigation failure or timeout.
|
|
732
|
+
*
|
|
733
|
+
* @param url - The URL to navigate to
|
|
734
|
+
* @param options.waitUntil - When to consider navigation complete (default: "load")
|
|
735
|
+
* @returns true on success
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* await utils.goto("https://example.com");
|
|
739
|
+
* await utils.goto("https://app.com", { waitUntil: "networkidle0" });
|
|
740
|
+
*/
|
|
628
741
|
goto(url: string, options?: {
|
|
629
742
|
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
630
743
|
}): Promise<boolean>;
|
|
744
|
+
/**
|
|
745
|
+
* Wait for the current page to complete a navigation (e.g. after a form submit).
|
|
746
|
+
* Throws on timeout.
|
|
747
|
+
*
|
|
748
|
+
* @param options.timeout - Max wait time in ms (default: 60000)
|
|
749
|
+
* @param options.waitUntil - When to consider navigation complete (default: "load")
|
|
750
|
+
* @returns true on success
|
|
751
|
+
*
|
|
752
|
+
* @example
|
|
753
|
+
* await utils.click("#submit");
|
|
754
|
+
* await utils.waitForNavigation();
|
|
755
|
+
*/
|
|
631
756
|
waitForNavigation(options?: {
|
|
632
757
|
timeout?: number;
|
|
633
758
|
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
|
|
634
759
|
}): Promise<boolean>;
|
|
760
|
+
/**
|
|
761
|
+
* Take a screenshot of the active page. Returns null on failure (soft fail).
|
|
762
|
+
*
|
|
763
|
+
* @param path - Optional file path to save the screenshot. If omitted, returns base64 string.
|
|
764
|
+
* @returns Screenshot buffer (if path given) or base64 string, or null on failure
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* await utils.screenshot("./debug.png"); // save to file
|
|
768
|
+
* const base64 = await utils.screenshot(); // get base64
|
|
769
|
+
*/
|
|
635
770
|
screenshot(path?: string): Promise<unknown>;
|
|
636
|
-
|
|
637
|
-
|
|
771
|
+
/**
|
|
772
|
+
* Switch scope into an iframe within the current page.
|
|
773
|
+
* After switching, all methods (click, type, exists...) operate inside the iframe.
|
|
774
|
+
* Use `activeMainFrame()` to exit back to the main page.
|
|
775
|
+
* Throws if the iframe is not found.
|
|
776
|
+
*
|
|
777
|
+
* @param selector - CSS selector or XPath of the iframe element
|
|
778
|
+
* @param options.waitTimeout - Max wait time for iframe element to appear in ms (default: 0 — instant)
|
|
779
|
+
* @returns The Frame handle
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* await utils.activeIframe("#my-iframe");
|
|
783
|
+
* await utils.click("#btn-inside-iframe");
|
|
784
|
+
* await utils.activeMainFrame();
|
|
785
|
+
*/
|
|
786
|
+
activeIframe(selector: string, options?: {
|
|
787
|
+
waitTimeout?: number;
|
|
788
|
+
}): Promise<Frame | null>;
|
|
789
|
+
/**
|
|
790
|
+
* Exit the current iframe and return to the main frame of the active page.
|
|
791
|
+
* After calling this, all methods operate on the main page (outside any iframe).
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* await utils.activeIframe("#my-iframe");
|
|
795
|
+
* await utils.click("#btn-in-iframe");
|
|
796
|
+
* await utils.activeMainFrame(); // back to main page
|
|
797
|
+
*/
|
|
798
|
+
activeMainFrame(): Promise<void>;
|
|
799
|
+
private _findNewTab;
|
|
800
|
+
private _findPopup;
|
|
801
|
+
/**
|
|
802
|
+
* Wait for a new tab to appear, but do NOT switch to it.
|
|
803
|
+
* The active page remains unchanged. Throws if no new tab appears before timeout.
|
|
804
|
+
*
|
|
805
|
+
* @param opts.matcher - String or RegExp to match the new tab's title or URL. If omitted, any new tab matches.
|
|
806
|
+
* @param opts.timeout - Max wait time in ms (default: 8000)
|
|
807
|
+
* @returns The new Page handle (without switching)
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* await utils.click("#open-link");
|
|
811
|
+
* const page = await utils.waitForNewTab();
|
|
812
|
+
* const page = await utils.waitForNewTab({ timeout: 20000 });
|
|
813
|
+
* const page = await utils.waitForNewTab({ matcher: "Google" });
|
|
814
|
+
*/
|
|
815
|
+
waitForNewTab(opts?: {
|
|
816
|
+
matcher?: string | RegExp;
|
|
817
|
+
timeout?: number;
|
|
818
|
+
}): Promise<puppeteer_core.Page | null>;
|
|
819
|
+
/**
|
|
820
|
+
* Switch to a new tab immediately (or wait if timeout is specified).
|
|
821
|
+
* After calling this, all methods operate on the new tab.
|
|
822
|
+
* Use `activeDefault()` to return to the original tab.
|
|
823
|
+
*
|
|
824
|
+
* @param opts.matcher - String or RegExp to match the new tab's title or URL
|
|
825
|
+
* @param opts.timeout - Max wait time in ms (default: 0 — instant, throws if not found)
|
|
826
|
+
* @returns The new Page handle (now active)
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* await utils.click("#open-link");
|
|
830
|
+
* await utils.activeNewTab(); // switch immediately
|
|
831
|
+
* await utils.activeNewTab({ timeout: 8000 }); // wait up to 8s
|
|
832
|
+
* await utils.type("#input", "hello"); // types on the new tab
|
|
833
|
+
* await utils.activeDefault(); // back to original tab
|
|
834
|
+
*/
|
|
835
|
+
activeNewTab(opts?: {
|
|
836
|
+
matcher?: string | RegExp;
|
|
837
|
+
timeout?: number;
|
|
838
|
+
}): Promise<puppeteer_core.Page | null>;
|
|
839
|
+
/**
|
|
840
|
+
* Wait for a popup/tab matching the given criteria to appear, but do NOT switch to it.
|
|
841
|
+
* Throws if no matching popup appears before timeout.
|
|
842
|
+
*
|
|
843
|
+
* @param opts.matcher - String or RegExp to match title or URL. If omitted, any new popup matches.
|
|
844
|
+
* @param opts.timeout - Max wait time in ms (default: 8000)
|
|
845
|
+
* @returns The popup Page handle (without switching)
|
|
846
|
+
*
|
|
847
|
+
* @example
|
|
848
|
+
* await utils.click("#connect-wallet");
|
|
849
|
+
* const popup = await utils.waitForPopup({ matcher: "MetaMask" });
|
|
850
|
+
*/
|
|
851
|
+
waitForPopup(opts?: {
|
|
852
|
+
matcher?: string | RegExp;
|
|
853
|
+
timeout?: number;
|
|
854
|
+
}): Promise<puppeteer_core.Page | null>;
|
|
855
|
+
/**
|
|
856
|
+
* Switch to a popup/tab immediately (or wait if timeout is specified).
|
|
857
|
+
* After calling this, all methods operate on the popup.
|
|
858
|
+
* Use `activeDefault()` to return to the original tab.
|
|
859
|
+
*
|
|
860
|
+
* @param opts.matcher - String or RegExp to match title or URL
|
|
861
|
+
* @param opts.timeout - Max wait time in ms (default: 0 — instant, throws if not found)
|
|
862
|
+
* @returns The popup Page handle (now active)
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
* await utils.click("#connect-wallet");
|
|
866
|
+
* await utils.activePopup({ matcher: "MetaMask" });
|
|
867
|
+
* await utils.click("#approve"); // clicks on the popup
|
|
868
|
+
* await utils.activeDefault(); // back to original tab
|
|
869
|
+
*/
|
|
870
|
+
activePopup(opts?: {
|
|
871
|
+
matcher?: string | RegExp;
|
|
872
|
+
timeout?: number;
|
|
873
|
+
}): Promise<puppeteer_core.Page | null>;
|
|
874
|
+
/**
|
|
875
|
+
* Switch focus to a tab by its index (0-based, ordered by creation time).
|
|
876
|
+
* All subsequent methods (click, type, goto...) will operate on this tab.
|
|
877
|
+
* User interactions (opening tabs, clicking browser) do NOT affect the active tab.
|
|
878
|
+
* Throws if the index is out of range.
|
|
879
|
+
*
|
|
880
|
+
* @param index - Zero-based tab index
|
|
881
|
+
* @returns The Page handle of the activated tab
|
|
882
|
+
*
|
|
883
|
+
* @example
|
|
884
|
+
* await utils.activeTab(1); // switch to second tab
|
|
885
|
+
* await utils.click("#btn"); // clicks on tab 1
|
|
886
|
+
* await utils.activeTab(0); // back to first tab
|
|
887
|
+
*/
|
|
888
|
+
activeTab(index: number): Promise<puppeteer_core.Page | null>;
|
|
889
|
+
/**
|
|
890
|
+
* Close the currently active tab and switch back to the default page.
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* await utils.activeTab(1);
|
|
894
|
+
* await utils.closeCurrentTab(); // closes tab 1, returns to tab 0
|
|
895
|
+
*/
|
|
638
896
|
closeCurrentTab(): Promise<void>;
|
|
897
|
+
/**
|
|
898
|
+
* Close all tabs except the currently active one.
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* await utils.closeOtherTabs(); // keeps only the active tab open
|
|
902
|
+
*/
|
|
639
903
|
closeOtherTabs(): Promise<void>;
|
|
640
904
|
/**
|
|
641
|
-
* Close ALL tabs
|
|
642
|
-
* Useful for full cleanup before flow ends.
|
|
905
|
+
* Close ALL tabs including the current one.
|
|
906
|
+
* Useful for full cleanup before a flow ends.
|
|
907
|
+
*
|
|
908
|
+
* @example
|
|
909
|
+
* await utils.closeAllTabs();
|
|
643
910
|
*/
|
|
644
911
|
closeAllTabs(): Promise<void>;
|
|
645
912
|
/**
|
|
646
|
-
* Switch back to the default (initial)
|
|
647
|
-
*
|
|
913
|
+
* Switch back to the default (initial) tab — the first tab opened when the flow started.
|
|
914
|
+
* Resets both the active page and active frame (exits any iframe).
|
|
915
|
+
* Typically used after `activePopup()` or `activeTab()` to return to the main tab.
|
|
916
|
+
*
|
|
917
|
+
* @returns The default Page handle
|
|
918
|
+
*
|
|
919
|
+
* @example
|
|
920
|
+
* await utils.activePopup({ matcher: "MetaMask" });
|
|
921
|
+
* await utils.click("#approve");
|
|
922
|
+
* await utils.activeDefault(); // back to original tab
|
|
923
|
+
*/
|
|
924
|
+
activeDefault(): Promise<puppeteer_core.Page>;
|
|
925
|
+
/**
|
|
926
|
+
* Log a config object in a readable format.
|
|
927
|
+
*
|
|
928
|
+
* @param config - Key-value object to log
|
|
929
|
+
* @param label - Label for the log entry (default: "Config")
|
|
648
930
|
*/
|
|
649
|
-
switchToDefault(): Promise<puppeteer.Page>;
|
|
650
931
|
logConfig(config: Record<string, unknown>, label?: string): Promise<void>;
|
|
932
|
+
/** Log the current profile input values for debugging. */
|
|
651
933
|
logProfileInput(): Promise<void>;
|
|
652
934
|
/**
|
|
653
|
-
* Log
|
|
654
|
-
*
|
|
935
|
+
* Log all current output values (including values from previous runs and newly written values).
|
|
936
|
+
* Useful for debugging — see what outputs have been set.
|
|
655
937
|
*/
|
|
656
938
|
logProfileOutput(): Promise<void>;
|
|
939
|
+
/** Log the current global input values for debugging. */
|
|
657
940
|
logGlobalInput(): Promise<void>;
|
|
658
941
|
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
942
|
+
* Write an output value for the current profile.
|
|
943
|
+
* Dispatched via a dedicated channel (type: "profile_output") — separate from logs.
|
|
944
|
+
* Also logs the value to console/UI for debugging.
|
|
945
|
+
*
|
|
946
|
+
* ⚠️ Key must be defined in `config.output[]` — throws Error if invalid.
|
|
947
|
+
*
|
|
948
|
+
* Accepted value types:
|
|
949
|
+
* - `string | number | boolean`
|
|
950
|
+
* - `Array` (max 20 elements, each must be primitive)
|
|
951
|
+
* - `Object` (max 10 entries, values must be primitive)
|
|
662
952
|
*
|
|
663
|
-
*
|
|
953
|
+
* @param key - Output key (must match config.output definition)
|
|
954
|
+
* @param value - The value to write
|
|
664
955
|
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
956
|
+
* @example
|
|
957
|
+
* await utils.writeOutput("status", "success");
|
|
958
|
+
* await utils.writeOutput("balance", 1234.56);
|
|
959
|
+
* await utils.writeOutput("tokens", ["ETH", "USDT"]);
|
|
669
960
|
*/
|
|
670
961
|
writeOutput(key: InferOutputKeys<TConfig>, value: ProfileOutputValue): Promise<void>;
|
|
671
962
|
/**
|
|
672
|
-
*
|
|
673
|
-
* key
|
|
674
|
-
*
|
|
963
|
+
* Update a profile input field for the currently running profile.
|
|
964
|
+
* The key must be defined in the profileInput schema.
|
|
965
|
+
* The updated value is sent to the server to persist in AgentFlowConfig after execution.
|
|
966
|
+
*
|
|
967
|
+
* @param key - Profile input key (must match schema definition)
|
|
968
|
+
* @param value - The new value (string, number, or boolean)
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* await utils.writeProfileInput("lastLoginDate", "2024-01-15");
|
|
972
|
+
* await utils.writeProfileInput("retryCount", 3);
|
|
675
973
|
*/
|
|
676
974
|
writeProfileInput(key: InferProfileInputKeys<TConfig>, value: string | number | boolean): Promise<void>;
|
|
677
975
|
private sanitizeOutputValue;
|
package/dist/index.js
CHANGED
|
@@ -16631,10 +16631,16 @@ var HidemiumStandaloneAdapter = class {
|
|
|
16631
16631
|
profileLogger.info(`\u{1F310} Opening Hidemium profile: ${profileName}`);
|
|
16632
16632
|
this.pendingProfiles.add(profileName);
|
|
16633
16633
|
try {
|
|
16634
|
-
|
|
16634
|
+
let profiles = await this.service.getProfiles({
|
|
16635
16635
|
search: profileName
|
|
16636
|
-
});
|
|
16637
|
-
|
|
16636
|
+
}, true);
|
|
16637
|
+
let targetProfile = profiles.find((p) => p.name === profileName);
|
|
16638
|
+
if (!targetProfile) {
|
|
16639
|
+
profiles = await this.service.getProfiles({
|
|
16640
|
+
search: profileName
|
|
16641
|
+
});
|
|
16642
|
+
targetProfile = profiles.find((p) => p.name === profileName);
|
|
16643
|
+
}
|
|
16638
16644
|
if (!targetProfile) {
|
|
16639
16645
|
throw new Error(`Profile not found: ${profileName}`);
|
|
16640
16646
|
}
|
|
@@ -16648,7 +16654,10 @@ var HidemiumStandaloneAdapter = class {
|
|
|
16648
16654
|
const command = [
|
|
16649
16655
|
`--window-position=${x},${y}`,
|
|
16650
16656
|
`--window-size=${windowConfig.width},${windowConfig.height}`,
|
|
16651
|
-
`--force-device-scale-factor=${windowConfig.scale}
|
|
16657
|
+
`--force-device-scale-factor=${windowConfig.scale}`,
|
|
16658
|
+
`--disable-background-timer-throttling`,
|
|
16659
|
+
`--disable-backgrounding-occluded-windows`,
|
|
16660
|
+
`--disable-renderer-backgrounding`
|
|
16652
16661
|
].join(" ");
|
|
16653
16662
|
try {
|
|
16654
16663
|
await this.service.stopProfile(targetProfile.uuid);
|
|
@@ -16712,8 +16721,14 @@ var HidemiumStandaloneAdapter = class {
|
|
|
16712
16721
|
this.logger.log(`\u{1F512} Closing pending profile: ${profileName}`);
|
|
16713
16722
|
const profiles = await this.service.getProfiles({
|
|
16714
16723
|
search: profileName
|
|
16715
|
-
});
|
|
16716
|
-
|
|
16724
|
+
}, true);
|
|
16725
|
+
let target = profiles.find((p) => p.name === profileName);
|
|
16726
|
+
if (!target) {
|
|
16727
|
+
const allProfiles = await this.service.getProfiles({
|
|
16728
|
+
search: profileName
|
|
16729
|
+
});
|
|
16730
|
+
target = allProfiles.find((p) => p.name === profileName);
|
|
16731
|
+
}
|
|
16717
16732
|
if (target) {
|
|
16718
16733
|
await this.service.stopProfile(target.uuid);
|
|
16719
16734
|
}
|
|
@@ -16800,22 +16815,61 @@ function getSdkConfig() {
|
|
|
16800
16815
|
// src/utils/browser.utils.ts
|
|
16801
16816
|
var BrowserUtils = class {
|
|
16802
16817
|
constructor(context) {
|
|
16818
|
+
/** Currently active iframe — null means main frame. Changed via activeIframe() */
|
|
16819
|
+
this.activeFrame = null;
|
|
16803
16820
|
var _a;
|
|
16804
16821
|
this.ctx = context;
|
|
16805
16822
|
this.logger = context.logger;
|
|
16806
16823
|
this.outputDefs = (_a = context.outputDefinitions) != null ? _a : [];
|
|
16807
16824
|
this.validOutputKeys = new Set(this.outputDefs.map((d) => d.key));
|
|
16825
|
+
this.defaultPage = context.page;
|
|
16826
|
+
this.activePage = context.page;
|
|
16827
|
+
this.enableFocusEmulation(this.activePage).catch(() => {
|
|
16828
|
+
});
|
|
16829
|
+
}
|
|
16830
|
+
/**
|
|
16831
|
+
* CDP trick — Chromium nghĩ tab luôn focused, không bị throttle
|
|
16832
|
+
* khi user chuyển sang tab khác hoặc click vào trình duyệt.
|
|
16833
|
+
*/
|
|
16834
|
+
async enableFocusEmulation(page) {
|
|
16835
|
+
try {
|
|
16836
|
+
const cdp = await page.createCDPSession();
|
|
16837
|
+
await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true });
|
|
16838
|
+
} catch {
|
|
16839
|
+
}
|
|
16808
16840
|
}
|
|
16809
16841
|
checkAbort() {
|
|
16810
16842
|
const signal = global.__HIRA_ABORT_SIGNAL__;
|
|
16811
16843
|
if (signal == null ? void 0 : signal.aborted) throw new Error("cancelled");
|
|
16812
16844
|
}
|
|
16845
|
+
/**
|
|
16846
|
+
* Pause execution for the given duration.
|
|
16847
|
+
*
|
|
16848
|
+
* @param ms - Duration in milliseconds
|
|
16849
|
+
* @example await utils.sleep(2000); // wait 2 seconds
|
|
16850
|
+
*/
|
|
16813
16851
|
async sleep(ms) {
|
|
16852
|
+
this.checkAbort();
|
|
16814
16853
|
await this.log("debug", `\u23F3 Sleep ${ms}ms`);
|
|
16815
16854
|
await this.rawSleep(ms);
|
|
16816
16855
|
}
|
|
16817
|
-
|
|
16818
|
-
|
|
16856
|
+
/**
|
|
16857
|
+
* Wait for an element to appear and become visible in the DOM.
|
|
16858
|
+
* Returns null if not found (soft fail — does NOT throw).
|
|
16859
|
+
* Supports CSS selectors and XPath (auto-detected by `//` or `(` prefix).
|
|
16860
|
+
*
|
|
16861
|
+
* @param selector - CSS selector or XPath expression
|
|
16862
|
+
* @param timeout - Max wait time in ms (default: 8000)
|
|
16863
|
+
* @param scope - Optional Frame to search within
|
|
16864
|
+
* @returns The element handle, or null if not found
|
|
16865
|
+
*
|
|
16866
|
+
* @example
|
|
16867
|
+
* const el = await utils.waitForElement("#my-btn");
|
|
16868
|
+
* if (el) await el.click();
|
|
16869
|
+
*/
|
|
16870
|
+
async waitForElement(selector, timeout = 8e3, scope) {
|
|
16871
|
+
var _a;
|
|
16872
|
+
const target = (_a = scope != null ? scope : this.activeFrame) != null ? _a : this.activePage;
|
|
16819
16873
|
try {
|
|
16820
16874
|
this.checkAbort();
|
|
16821
16875
|
const options = { timeout, visible: true };
|
|
@@ -16832,7 +16886,35 @@ var BrowserUtils = class {
|
|
|
16832
16886
|
return null;
|
|
16833
16887
|
}
|
|
16834
16888
|
}
|
|
16889
|
+
/**
|
|
16890
|
+
* Resolve an element: if waitTimeout > 0, wait for it; otherwise query directly with $().
|
|
16891
|
+
*/
|
|
16892
|
+
async resolveElement(selector, waitTimeout, scope) {
|
|
16893
|
+
var _a;
|
|
16894
|
+
if (waitTimeout && waitTimeout > 0) {
|
|
16895
|
+
return this.waitForElement(selector, waitTimeout, scope);
|
|
16896
|
+
}
|
|
16897
|
+
const target = (_a = scope != null ? scope : this.activeFrame) != null ? _a : this.activePage;
|
|
16898
|
+
const resolved = selector.startsWith("//") || selector.startsWith("(") ? `xpath/${selector}` : selector;
|
|
16899
|
+
return target.$(resolved);
|
|
16900
|
+
}
|
|
16901
|
+
/**
|
|
16902
|
+
* Click on an element. Scrolls the element into view before clicking.
|
|
16903
|
+
* Throws if the element is not found.
|
|
16904
|
+
*
|
|
16905
|
+
* @param target - CSS selector, XPath, or an existing ElementHandle
|
|
16906
|
+
* @param options.delay - Delay in ms before clicking (default: 1000)
|
|
16907
|
+
* @param options.waitTimeout - Max wait time for element to appear (default: 2000)
|
|
16908
|
+
* @param options.frame - Optional Frame to search within
|
|
16909
|
+
* @returns true on success
|
|
16910
|
+
*
|
|
16911
|
+
* @example
|
|
16912
|
+
* await utils.click("#submit-btn");
|
|
16913
|
+
* await utils.click("#btn", { delay: 0 }); // click immediately
|
|
16914
|
+
* await utils.click("#btn", { waitTimeout: 8000 }); // wait longer
|
|
16915
|
+
*/
|
|
16835
16916
|
async click(target, options) {
|
|
16917
|
+
var _a, _b;
|
|
16836
16918
|
const label = typeof target === "string" ? target : "[ElementHandle]";
|
|
16837
16919
|
try {
|
|
16838
16920
|
this.checkAbort();
|
|
@@ -16841,82 +16923,116 @@ var BrowserUtils = class {
|
|
|
16841
16923
|
if (typeof target === "string") {
|
|
16842
16924
|
element = await this.waitForElement(
|
|
16843
16925
|
target,
|
|
16844
|
-
options == null ? void 0 : options.
|
|
16926
|
+
(_a = options == null ? void 0 : options.waitTimeout) != null ? _a : 2e3,
|
|
16845
16927
|
options == null ? void 0 : options.frame
|
|
16846
16928
|
);
|
|
16847
16929
|
} else {
|
|
16848
16930
|
element = target;
|
|
16849
16931
|
}
|
|
16850
16932
|
if (!element) {
|
|
16851
|
-
|
|
16852
|
-
"error",
|
|
16853
|
-
`\u274C Click failed \u2014 element not found: ${this.shortSelector(label)}`
|
|
16854
|
-
);
|
|
16855
|
-
return false;
|
|
16933
|
+
throw new Error(`Click failed \u2014 element not found: ${this.shortSelector(label)}`);
|
|
16856
16934
|
}
|
|
16857
16935
|
await element.scrollIntoView();
|
|
16858
|
-
|
|
16936
|
+
const clickDelay = (_b = options == null ? void 0 : options.delay) != null ? _b : 1e3;
|
|
16937
|
+
if (clickDelay > 0) await this.sleep(clickDelay);
|
|
16859
16938
|
await element.click();
|
|
16860
16939
|
await this.actionDelay();
|
|
16861
16940
|
return true;
|
|
16862
16941
|
} catch (error) {
|
|
16863
16942
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16864
16943
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16865
|
-
|
|
16866
|
-
"error",
|
|
16867
|
-
`\u274C Click failed: ${this.shortSelector(label)} \u2014 ${msg}`
|
|
16868
|
-
);
|
|
16869
|
-
return false;
|
|
16944
|
+
throw new Error(`Click failed: ${this.shortSelector(label)} \u2014 ${msg}`);
|
|
16870
16945
|
}
|
|
16871
16946
|
}
|
|
16947
|
+
/**
|
|
16948
|
+
* Type text into an input element. Throws if the element is not found.
|
|
16949
|
+
*
|
|
16950
|
+
* @param selector - CSS selector or XPath of the input
|
|
16951
|
+
* @param text - The text to type
|
|
16952
|
+
* @param options.mode - Typing mode:
|
|
16953
|
+
* - `"replace"` (default): Clear existing text, then type new text
|
|
16954
|
+
* - `"append"`: Type without clearing — appends to existing text
|
|
16955
|
+
* - `"paste"`: Set value directly via JS (fast, no keystroke simulation)
|
|
16956
|
+
* @param options.delay - Delay between keystrokes in ms (default: 50). Ignored in paste mode.
|
|
16957
|
+
* @param options.waitTimeout - Max wait time for element to appear in ms (default: 0 — instant)
|
|
16958
|
+
* @param options.frame - Optional Frame to search within
|
|
16959
|
+
* @returns true on success
|
|
16960
|
+
*
|
|
16961
|
+
* @example
|
|
16962
|
+
* await utils.type("#email", "user@example.com"); // replace mode
|
|
16963
|
+
* await utils.type("#input", " more text", { mode: "append" }); // append
|
|
16964
|
+
* await utils.type("#address", "0x1234...abcd", { mode: "paste" }); // instant paste
|
|
16965
|
+
*/
|
|
16872
16966
|
async type(selector, text, options) {
|
|
16873
|
-
var _a;
|
|
16967
|
+
var _a, _b, _c, _d;
|
|
16968
|
+
const mode = (_a = options == null ? void 0 : options.mode) != null ? _a : "replace";
|
|
16874
16969
|
const masked = text.length > 20 ? text.slice(0, 20) + "..." : text;
|
|
16875
16970
|
try {
|
|
16876
16971
|
this.checkAbort();
|
|
16877
16972
|
await this.log(
|
|
16878
16973
|
"info",
|
|
16879
|
-
`\u2328\uFE0F Type "${masked}" \u2192 ${this.shortSelector(selector)}`
|
|
16974
|
+
`\u2328\uFE0F Type [${mode}] "${masked}" \u2192 ${this.shortSelector(selector)}`
|
|
16880
16975
|
);
|
|
16881
|
-
const element = await this.
|
|
16976
|
+
const element = await this.resolveElement(
|
|
16882
16977
|
selector,
|
|
16883
|
-
|
|
16978
|
+
options == null ? void 0 : options.waitTimeout,
|
|
16884
16979
|
options == null ? void 0 : options.frame
|
|
16885
16980
|
);
|
|
16886
16981
|
if (!element) {
|
|
16887
|
-
|
|
16888
|
-
"error",
|
|
16889
|
-
`\u274C Type failed \u2014 element not found: ${this.shortSelector(selector)}`
|
|
16890
|
-
);
|
|
16891
|
-
return false;
|
|
16982
|
+
throw new Error(`Type failed \u2014 element not found: ${this.shortSelector(selector)}`);
|
|
16892
16983
|
}
|
|
16893
16984
|
await element.scrollIntoView();
|
|
16894
|
-
|
|
16895
|
-
|
|
16896
|
-
|
|
16985
|
+
if (mode === "paste") {
|
|
16986
|
+
const target = (_b = this.activeFrame) != null ? _b : this.activePage;
|
|
16987
|
+
await target.evaluate(
|
|
16988
|
+
(el, val) => {
|
|
16989
|
+
el.value = val;
|
|
16990
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
16991
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
16992
|
+
},
|
|
16993
|
+
element,
|
|
16994
|
+
text
|
|
16995
|
+
);
|
|
16996
|
+
} else if (mode === "append") {
|
|
16997
|
+
await element.click();
|
|
16998
|
+
await element.type(text, { delay: (_c = options == null ? void 0 : options.delay) != null ? _c : 50 });
|
|
16999
|
+
} else {
|
|
17000
|
+
await element.click({ clickCount: 3 });
|
|
17001
|
+
await element.press("Backspace");
|
|
17002
|
+
await element.type(text, { delay: (_d = options == null ? void 0 : options.delay) != null ? _d : 50 });
|
|
17003
|
+
}
|
|
16897
17004
|
await this.actionDelay();
|
|
16898
17005
|
return true;
|
|
16899
17006
|
} catch (error) {
|
|
16900
17007
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16901
17008
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16902
|
-
|
|
16903
|
-
"error",
|
|
16904
|
-
`\u274C Type failed: ${this.shortSelector(selector)} \u2014 ${msg}`
|
|
16905
|
-
);
|
|
16906
|
-
return false;
|
|
17009
|
+
throw new Error(`Type failed: ${this.shortSelector(selector)} \u2014 ${msg}`);
|
|
16907
17010
|
}
|
|
16908
17011
|
}
|
|
16909
|
-
|
|
17012
|
+
/**
|
|
17013
|
+
* Get the text content of an element. Returns null if not found (soft fail).
|
|
17014
|
+
*
|
|
17015
|
+
* @param selector - CSS selector or XPath
|
|
17016
|
+
* @param options.waitTimeout - Max wait time for element to appear in ms (default: 0 — instant)
|
|
17017
|
+
* @param options.frame - Optional Frame to search within
|
|
17018
|
+
* @returns Trimmed text content, or null if element not found
|
|
17019
|
+
*
|
|
17020
|
+
* @example
|
|
17021
|
+
* const price = await utils.getText(".price");
|
|
17022
|
+
* const el = await utils.getText(".lazy-el", { waitTimeout: 8000 });
|
|
17023
|
+
*/
|
|
17024
|
+
async getText(selector, options) {
|
|
17025
|
+
var _a, _b;
|
|
16910
17026
|
try {
|
|
16911
17027
|
this.checkAbort();
|
|
16912
17028
|
await this.log("debug", `\u{1F4C4} getText: ${this.shortSelector(selector)}`);
|
|
16913
|
-
const scope = frame != null ?
|
|
16914
|
-
const element = await this.
|
|
17029
|
+
const scope = (_b = (_a = options == null ? void 0 : options.frame) != null ? _a : this.activeFrame) != null ? _b : this.activePage;
|
|
17030
|
+
const element = await this.resolveElement(selector, options == null ? void 0 : options.waitTimeout, options == null ? void 0 : options.frame);
|
|
16915
17031
|
if (!element) return null;
|
|
16916
17032
|
const text = await scope.evaluate(
|
|
16917
17033
|
(el) => {
|
|
16918
|
-
var
|
|
16919
|
-
return ((
|
|
17034
|
+
var _a2;
|
|
17035
|
+
return ((_a2 = el.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
16920
17036
|
},
|
|
16921
17037
|
element
|
|
16922
17038
|
);
|
|
@@ -16930,158 +17046,432 @@ var BrowserUtils = class {
|
|
|
16930
17046
|
return null;
|
|
16931
17047
|
}
|
|
16932
17048
|
}
|
|
16933
|
-
|
|
17049
|
+
/**
|
|
17050
|
+
* Check if an element exists and is visible on the page.
|
|
17051
|
+
* Returns false if not found (soft fail — does NOT throw).
|
|
17052
|
+
*
|
|
17053
|
+
* @param selector - CSS selector or XPath
|
|
17054
|
+
* @param timeout - Max wait time in ms (default: 4000)
|
|
17055
|
+
* @param frame - Optional Frame to search within
|
|
17056
|
+
* @returns true if element exists and is visible
|
|
17057
|
+
*
|
|
17058
|
+
* @example
|
|
17059
|
+
* if (await utils.exists("#popup-overlay")) {
|
|
17060
|
+
* await utils.click("#close-popup");
|
|
17061
|
+
* }
|
|
17062
|
+
*/
|
|
17063
|
+
async exists(selector, timeout = 4e3, frame) {
|
|
16934
17064
|
this.checkAbort();
|
|
16935
17065
|
const el = await this.waitForElement(selector, timeout, frame);
|
|
16936
17066
|
return el !== null;
|
|
16937
17067
|
}
|
|
17068
|
+
/**
|
|
17069
|
+
* Navigate the active page to a URL. Waits until the page fully loads.
|
|
17070
|
+
* Throws on navigation failure or timeout.
|
|
17071
|
+
*
|
|
17072
|
+
* @param url - The URL to navigate to
|
|
17073
|
+
* @param options.waitUntil - When to consider navigation complete (default: "load")
|
|
17074
|
+
* @returns true on success
|
|
17075
|
+
*
|
|
17076
|
+
* @example
|
|
17077
|
+
* await utils.goto("https://example.com");
|
|
17078
|
+
* await utils.goto("https://app.com", { waitUntil: "networkidle0" });
|
|
17079
|
+
*/
|
|
16938
17080
|
async goto(url2, options) {
|
|
16939
17081
|
try {
|
|
16940
17082
|
this.checkAbort();
|
|
16941
17083
|
await this.log("info", `\u{1F310} Navigate \u2192 ${url2}`);
|
|
16942
|
-
await this.
|
|
16943
|
-
waitUntil: (options == null ? void 0 : options.waitUntil) || "
|
|
16944
|
-
timeout:
|
|
17084
|
+
await this.activePage.goto(url2, {
|
|
17085
|
+
waitUntil: (options == null ? void 0 : options.waitUntil) || "load",
|
|
17086
|
+
timeout: 6e4
|
|
16945
17087
|
});
|
|
16946
17088
|
await this.actionDelay();
|
|
16947
17089
|
return true;
|
|
16948
17090
|
} catch (error) {
|
|
16949
17091
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16950
17092
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16951
|
-
|
|
16952
|
-
return false;
|
|
17093
|
+
throw new Error(`Navigate failed: ${url2} \u2014 ${msg}`);
|
|
16953
17094
|
}
|
|
16954
17095
|
}
|
|
17096
|
+
/**
|
|
17097
|
+
* Wait for the current page to complete a navigation (e.g. after a form submit).
|
|
17098
|
+
* Throws on timeout.
|
|
17099
|
+
*
|
|
17100
|
+
* @param options.timeout - Max wait time in ms (default: 60000)
|
|
17101
|
+
* @param options.waitUntil - When to consider navigation complete (default: "load")
|
|
17102
|
+
* @returns true on success
|
|
17103
|
+
*
|
|
17104
|
+
* @example
|
|
17105
|
+
* await utils.click("#submit");
|
|
17106
|
+
* await utils.waitForNavigation();
|
|
17107
|
+
*/
|
|
16955
17108
|
async waitForNavigation(options) {
|
|
16956
17109
|
try {
|
|
16957
17110
|
this.checkAbort();
|
|
16958
17111
|
await this.log("debug", "\u{1F504} Waiting for navigation...");
|
|
16959
|
-
await this.
|
|
16960
|
-
timeout: (options == null ? void 0 : options.timeout) ||
|
|
16961
|
-
waitUntil: (options == null ? void 0 : options.waitUntil) || "
|
|
17112
|
+
await this.activePage.waitForNavigation({
|
|
17113
|
+
timeout: (options == null ? void 0 : options.timeout) || 16e3,
|
|
17114
|
+
waitUntil: (options == null ? void 0 : options.waitUntil) || "load"
|
|
16962
17115
|
});
|
|
16963
17116
|
return true;
|
|
16964
17117
|
} catch (err) {
|
|
16965
17118
|
if (err instanceof Error && err.message === "cancelled") throw err;
|
|
16966
|
-
|
|
16967
|
-
return false;
|
|
17119
|
+
throw new Error("Navigation timeout");
|
|
16968
17120
|
}
|
|
16969
17121
|
}
|
|
17122
|
+
/**
|
|
17123
|
+
* Take a screenshot of the active page. Returns null on failure (soft fail).
|
|
17124
|
+
*
|
|
17125
|
+
* @param path - Optional file path to save the screenshot. If omitted, returns base64 string.
|
|
17126
|
+
* @returns Screenshot buffer (if path given) or base64 string, or null on failure
|
|
17127
|
+
*
|
|
17128
|
+
* @example
|
|
17129
|
+
* await utils.screenshot("./debug.png"); // save to file
|
|
17130
|
+
* const base64 = await utils.screenshot(); // get base64
|
|
17131
|
+
*/
|
|
16970
17132
|
async screenshot(path2) {
|
|
16971
17133
|
try {
|
|
16972
17134
|
this.checkAbort();
|
|
16973
17135
|
await this.log("info", `\u{1F4F8} Screenshot${path2 ? `: ${path2}` : ""}`);
|
|
16974
|
-
return path2 ? await this.
|
|
17136
|
+
return path2 ? await this.activePage.screenshot({ path: path2 }) : await this.activePage.screenshot({ encoding: "base64" });
|
|
16975
17137
|
} catch (error) {
|
|
17138
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16976
17139
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16977
|
-
await this.log("
|
|
17140
|
+
await this.log("warn", `\u26A0\uFE0F Screenshot failed \u2014 ${msg}`);
|
|
16978
17141
|
return null;
|
|
16979
17142
|
}
|
|
16980
17143
|
}
|
|
16981
|
-
|
|
16982
|
-
|
|
17144
|
+
/**
|
|
17145
|
+
* Switch scope into an iframe within the current page.
|
|
17146
|
+
* After switching, all methods (click, type, exists...) operate inside the iframe.
|
|
17147
|
+
* Use `activeMainFrame()` to exit back to the main page.
|
|
17148
|
+
* Throws if the iframe is not found.
|
|
17149
|
+
*
|
|
17150
|
+
* @param selector - CSS selector or XPath of the iframe element
|
|
17151
|
+
* @param options.waitTimeout - Max wait time for iframe element to appear in ms (default: 0 — instant)
|
|
17152
|
+
* @returns The Frame handle
|
|
17153
|
+
*
|
|
17154
|
+
* @example
|
|
17155
|
+
* await utils.activeIframe("#my-iframe");
|
|
17156
|
+
* await utils.click("#btn-inside-iframe");
|
|
17157
|
+
* await utils.activeMainFrame();
|
|
17158
|
+
*/
|
|
17159
|
+
async activeIframe(selector, options) {
|
|
17160
|
+
try {
|
|
17161
|
+
this.checkAbort();
|
|
17162
|
+
await this.log("info", `\u{1F5BC}\uFE0F Active iframe: ${this.shortSelector(selector)}`);
|
|
17163
|
+
const resolved = selector.startsWith("//") || selector.startsWith("(") ? `xpath/${selector}` : selector;
|
|
17164
|
+
const el = (options == null ? void 0 : options.waitTimeout) && options.waitTimeout > 0 ? await this.activePage.waitForSelector(resolved, { timeout: options.waitTimeout }) : await this.activePage.$(resolved);
|
|
17165
|
+
if (!el) {
|
|
17166
|
+
throw new Error(`Iframe not found: ${this.shortSelector(selector)}`);
|
|
17167
|
+
}
|
|
17168
|
+
const frame = await el.contentFrame();
|
|
17169
|
+
if (!frame) {
|
|
17170
|
+
throw new Error(`Cannot get contentFrame from: ${this.shortSelector(selector)}`);
|
|
17171
|
+
}
|
|
17172
|
+
this.activeFrame = frame;
|
|
17173
|
+
return frame;
|
|
17174
|
+
} catch (error) {
|
|
17175
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
17176
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17177
|
+
throw new Error(`activeIframe failed: ${this.shortSelector(selector)} \u2014 ${msg}`);
|
|
17178
|
+
}
|
|
17179
|
+
}
|
|
17180
|
+
/**
|
|
17181
|
+
* Exit the current iframe and return to the main frame of the active page.
|
|
17182
|
+
* After calling this, all methods operate on the main page (outside any iframe).
|
|
17183
|
+
*
|
|
17184
|
+
* @example
|
|
17185
|
+
* await utils.activeIframe("#my-iframe");
|
|
17186
|
+
* await utils.click("#btn-in-iframe");
|
|
17187
|
+
* await utils.activeMainFrame(); // back to main page
|
|
17188
|
+
*/
|
|
17189
|
+
async activeMainFrame() {
|
|
17190
|
+
this.checkAbort();
|
|
17191
|
+
this.activeFrame = null;
|
|
17192
|
+
await this.log("info", `\u{1F5BC}\uFE0F Switched to main frame`);
|
|
17193
|
+
}
|
|
17194
|
+
// ── Internal: tìm tab mới ─────────────────────────────────
|
|
17195
|
+
async _findNewTab(opts) {
|
|
17196
|
+
const { matcher, timeout = 8e3 } = opts != null ? opts : {};
|
|
17197
|
+
const label = matcher ? typeof matcher === "string" ? matcher : matcher.toString() : "(any)";
|
|
16983
17198
|
this.checkAbort();
|
|
16984
|
-
await this.log("info", `\
|
|
17199
|
+
await this.log("info", `\u23F3 Waiting for new tab ${label}... (${timeout}ms)`);
|
|
17200
|
+
const isMatch = (text) => {
|
|
17201
|
+
if (!matcher) return true;
|
|
17202
|
+
if (!text) return false;
|
|
17203
|
+
if (typeof matcher === "string") return text.includes(matcher);
|
|
17204
|
+
return matcher.test(text);
|
|
17205
|
+
};
|
|
16985
17206
|
try {
|
|
17207
|
+
const currentCount = (await this.ctx.browser.pages()).length;
|
|
17208
|
+
const start = Date.now();
|
|
17209
|
+
while (Date.now() - start < timeout) {
|
|
17210
|
+
const pages = await this.ctx.browser.pages();
|
|
17211
|
+
if (pages.length > currentCount) {
|
|
17212
|
+
for (let i = pages.length - 1; i >= currentCount; i--) {
|
|
17213
|
+
const p = pages[i];
|
|
17214
|
+
try {
|
|
17215
|
+
await p.waitForNavigation({
|
|
17216
|
+
waitUntil: "domcontentloaded",
|
|
17217
|
+
timeout: 3e3
|
|
17218
|
+
}).catch(() => {
|
|
17219
|
+
});
|
|
17220
|
+
} catch {
|
|
17221
|
+
}
|
|
17222
|
+
if (!matcher) {
|
|
17223
|
+
await this.log("success", `\u2705 New tab detected \u2014 tab[${i}]`);
|
|
17224
|
+
return p;
|
|
17225
|
+
}
|
|
17226
|
+
try {
|
|
17227
|
+
const title = await p.title();
|
|
17228
|
+
const url2 = p.url();
|
|
17229
|
+
if (isMatch(title) || isMatch(url2)) {
|
|
17230
|
+
await this.log("success", `\u2705 New tab matched: ${label} \u2014 tab[${i}]`);
|
|
17231
|
+
return p;
|
|
17232
|
+
}
|
|
17233
|
+
} catch {
|
|
17234
|
+
}
|
|
17235
|
+
}
|
|
17236
|
+
}
|
|
17237
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
17238
|
+
}
|
|
17239
|
+
throw new Error(`New tab not found: ${label} (timeout: ${timeout}ms)`);
|
|
17240
|
+
} catch (error) {
|
|
17241
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
17242
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17243
|
+
throw new Error(`waitForNewTab failed \u2014 ${msg}`);
|
|
17244
|
+
}
|
|
17245
|
+
}
|
|
17246
|
+
// ── Internal: tìm popup đã có (hoặc chờ xuất hiện) ──────
|
|
17247
|
+
async _findPopup(opts) {
|
|
17248
|
+
const { matcher, timeout = 8e3 } = opts != null ? opts : {};
|
|
17249
|
+
const label = matcher ? typeof matcher === "string" ? matcher : matcher.toString() : "(any new)";
|
|
17250
|
+
this.checkAbort();
|
|
17251
|
+
await this.log("info", `\u23F3 Waiting for popup ${label}... (${timeout}ms)`);
|
|
17252
|
+
const isMatch = (text) => {
|
|
17253
|
+
if (!matcher) return true;
|
|
17254
|
+
if (!text) return false;
|
|
17255
|
+
if (typeof matcher === "string") return text.includes(matcher);
|
|
17256
|
+
return matcher.test(text);
|
|
17257
|
+
};
|
|
17258
|
+
try {
|
|
17259
|
+
const currentPages = new Set((await this.ctx.browser.pages()).map((p) => p));
|
|
16986
17260
|
const start = Date.now();
|
|
16987
|
-
const isMatch = (text) => {
|
|
16988
|
-
if (!text) return false;
|
|
16989
|
-
if (typeof matcher === "string") return text.includes(matcher);
|
|
16990
|
-
return matcher.test(text);
|
|
16991
|
-
};
|
|
16992
17261
|
while (Date.now() - start < timeout) {
|
|
16993
17262
|
const pages = await this.ctx.browser.pages();
|
|
16994
17263
|
for (let i = pages.length - 1; i >= 0; i--) {
|
|
16995
17264
|
const page = pages[i];
|
|
16996
|
-
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17265
|
+
if (!matcher && currentPages.has(page)) continue;
|
|
17266
|
+
try {
|
|
17267
|
+
const title = await page.title();
|
|
17268
|
+
const url2 = page.url();
|
|
17269
|
+
if (isMatch(title) || isMatch(url2)) {
|
|
17270
|
+
await this.log("success", `\u2705 Popup found: ${label} \u2014 tab[${i}]`);
|
|
17271
|
+
return page;
|
|
17272
|
+
}
|
|
17273
|
+
} catch {
|
|
17001
17274
|
}
|
|
17002
17275
|
}
|
|
17003
17276
|
await new Promise((r) => setTimeout(r, 500));
|
|
17004
17277
|
}
|
|
17005
|
-
|
|
17006
|
-
"warn",
|
|
17007
|
-
`\u26A0\uFE0F Popup not found: ${label} (timeout: ${timeout}ms)`
|
|
17008
|
-
);
|
|
17009
|
-
return null;
|
|
17278
|
+
throw new Error(`Popup not found: ${label} (timeout: ${timeout}ms)`);
|
|
17010
17279
|
} catch (error) {
|
|
17280
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
17011
17281
|
const msg = error instanceof Error ? error.message : String(error);
|
|
17012
|
-
|
|
17013
|
-
|
|
17282
|
+
throw new Error(`waitForPopup failed: ${label} \u2014 ${msg}`);
|
|
17283
|
+
}
|
|
17284
|
+
}
|
|
17285
|
+
/**
|
|
17286
|
+
* Wait for a new tab to appear, but do NOT switch to it.
|
|
17287
|
+
* The active page remains unchanged. Throws if no new tab appears before timeout.
|
|
17288
|
+
*
|
|
17289
|
+
* @param opts.matcher - String or RegExp to match the new tab's title or URL. If omitted, any new tab matches.
|
|
17290
|
+
* @param opts.timeout - Max wait time in ms (default: 8000)
|
|
17291
|
+
* @returns The new Page handle (without switching)
|
|
17292
|
+
*
|
|
17293
|
+
* @example
|
|
17294
|
+
* await utils.click("#open-link");
|
|
17295
|
+
* const page = await utils.waitForNewTab();
|
|
17296
|
+
* const page = await utils.waitForNewTab({ timeout: 20000 });
|
|
17297
|
+
* const page = await utils.waitForNewTab({ matcher: "Google" });
|
|
17298
|
+
*/
|
|
17299
|
+
async waitForNewTab(opts) {
|
|
17300
|
+
return this._findNewTab(opts);
|
|
17301
|
+
}
|
|
17302
|
+
/**
|
|
17303
|
+
* Switch to a new tab immediately (or wait if timeout is specified).
|
|
17304
|
+
* After calling this, all methods operate on the new tab.
|
|
17305
|
+
* Use `activeDefault()` to return to the original tab.
|
|
17306
|
+
*
|
|
17307
|
+
* @param opts.matcher - String or RegExp to match the new tab's title or URL
|
|
17308
|
+
* @param opts.timeout - Max wait time in ms (default: 0 — instant, throws if not found)
|
|
17309
|
+
* @returns The new Page handle (now active)
|
|
17310
|
+
*
|
|
17311
|
+
* @example
|
|
17312
|
+
* await utils.click("#open-link");
|
|
17313
|
+
* await utils.activeNewTab(); // switch immediately
|
|
17314
|
+
* await utils.activeNewTab({ timeout: 8000 }); // wait up to 8s
|
|
17315
|
+
* await utils.type("#input", "hello"); // types on the new tab
|
|
17316
|
+
* await utils.activeDefault(); // back to original tab
|
|
17317
|
+
*/
|
|
17318
|
+
async activeNewTab(opts) {
|
|
17319
|
+
var _a;
|
|
17320
|
+
const page = await this._findNewTab({ ...opts, timeout: (_a = opts == null ? void 0 : opts.timeout) != null ? _a : 0 });
|
|
17321
|
+
if (page) {
|
|
17322
|
+
await page.bringToFront();
|
|
17323
|
+
await this.enableFocusEmulation(page);
|
|
17324
|
+
this.activePage = page;
|
|
17325
|
+
this.activeFrame = null;
|
|
17326
|
+
}
|
|
17327
|
+
return page;
|
|
17328
|
+
}
|
|
17329
|
+
/**
|
|
17330
|
+
* Wait for a popup/tab matching the given criteria to appear, but do NOT switch to it.
|
|
17331
|
+
* Throws if no matching popup appears before timeout.
|
|
17332
|
+
*
|
|
17333
|
+
* @param opts.matcher - String or RegExp to match title or URL. If omitted, any new popup matches.
|
|
17334
|
+
* @param opts.timeout - Max wait time in ms (default: 8000)
|
|
17335
|
+
* @returns The popup Page handle (without switching)
|
|
17336
|
+
*
|
|
17337
|
+
* @example
|
|
17338
|
+
* await utils.click("#connect-wallet");
|
|
17339
|
+
* const popup = await utils.waitForPopup({ matcher: "MetaMask" });
|
|
17340
|
+
*/
|
|
17341
|
+
async waitForPopup(opts) {
|
|
17342
|
+
return this._findPopup(opts);
|
|
17343
|
+
}
|
|
17344
|
+
/**
|
|
17345
|
+
* Switch to a popup/tab immediately (or wait if timeout is specified).
|
|
17346
|
+
* After calling this, all methods operate on the popup.
|
|
17347
|
+
* Use `activeDefault()` to return to the original tab.
|
|
17348
|
+
*
|
|
17349
|
+
* @param opts.matcher - String or RegExp to match title or URL
|
|
17350
|
+
* @param opts.timeout - Max wait time in ms (default: 0 — instant, throws if not found)
|
|
17351
|
+
* @returns The popup Page handle (now active)
|
|
17352
|
+
*
|
|
17353
|
+
* @example
|
|
17354
|
+
* await utils.click("#connect-wallet");
|
|
17355
|
+
* await utils.activePopup({ matcher: "MetaMask" });
|
|
17356
|
+
* await utils.click("#approve"); // clicks on the popup
|
|
17357
|
+
* await utils.activeDefault(); // back to original tab
|
|
17358
|
+
*/
|
|
17359
|
+
async activePopup(opts) {
|
|
17360
|
+
var _a;
|
|
17361
|
+
const page = await this._findPopup({ ...opts, timeout: (_a = opts == null ? void 0 : opts.timeout) != null ? _a : 0 });
|
|
17362
|
+
if (page) {
|
|
17363
|
+
await page.bringToFront();
|
|
17364
|
+
await this.enableFocusEmulation(page);
|
|
17365
|
+
this.activePage = page;
|
|
17366
|
+
this.activeFrame = null;
|
|
17014
17367
|
}
|
|
17368
|
+
return page;
|
|
17015
17369
|
}
|
|
17016
|
-
|
|
17370
|
+
/**
|
|
17371
|
+
* Switch focus to a tab by its index (0-based, ordered by creation time).
|
|
17372
|
+
* All subsequent methods (click, type, goto...) will operate on this tab.
|
|
17373
|
+
* User interactions (opening tabs, clicking browser) do NOT affect the active tab.
|
|
17374
|
+
* Throws if the index is out of range.
|
|
17375
|
+
*
|
|
17376
|
+
* @param index - Zero-based tab index
|
|
17377
|
+
* @returns The Page handle of the activated tab
|
|
17378
|
+
*
|
|
17379
|
+
* @example
|
|
17380
|
+
* await utils.activeTab(1); // switch to second tab
|
|
17381
|
+
* await utils.click("#btn"); // clicks on tab 1
|
|
17382
|
+
* await utils.activeTab(0); // back to first tab
|
|
17383
|
+
*/
|
|
17384
|
+
async activeTab(index) {
|
|
17017
17385
|
try {
|
|
17018
17386
|
this.checkAbort();
|
|
17019
|
-
await this.log("info", `\u{1F504}
|
|
17387
|
+
await this.log("info", `\u{1F504} Active tab[${index}]...`);
|
|
17020
17388
|
const pages = await this.ctx.browser.pages();
|
|
17021
17389
|
if (index >= 0 && index < pages.length) {
|
|
17022
17390
|
const page = pages[index];
|
|
17023
17391
|
await page.bringToFront();
|
|
17392
|
+
await this.enableFocusEmulation(page);
|
|
17393
|
+
this.activePage = page;
|
|
17394
|
+
this.activeFrame = null;
|
|
17024
17395
|
return page;
|
|
17025
17396
|
}
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
);
|
|
17030
|
-
|
|
17031
|
-
} catch {
|
|
17032
|
-
return null;
|
|
17397
|
+
throw new Error(`Tab[${index}] not found (total: ${pages.length})`);
|
|
17398
|
+
} catch (error) {
|
|
17399
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
17400
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17401
|
+
throw new Error(`activeTab failed \u2014 ${msg}`);
|
|
17033
17402
|
}
|
|
17034
17403
|
}
|
|
17404
|
+
/**
|
|
17405
|
+
* Close the currently active tab and switch back to the default page.
|
|
17406
|
+
*
|
|
17407
|
+
* @example
|
|
17408
|
+
* await utils.activeTab(1);
|
|
17409
|
+
* await utils.closeCurrentTab(); // closes tab 1, returns to tab 0
|
|
17410
|
+
*/
|
|
17035
17411
|
async closeCurrentTab() {
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
}
|
|
17042
|
-
} catch {
|
|
17412
|
+
this.checkAbort();
|
|
17413
|
+
if (!this.activePage.isClosed()) {
|
|
17414
|
+
await this.log("info", `\u{1F5D1}\uFE0F Closing tab: ${this.activePage.url()}`);
|
|
17415
|
+
await this.activePage.close();
|
|
17416
|
+
this.activePage = this.defaultPage;
|
|
17043
17417
|
}
|
|
17044
17418
|
}
|
|
17419
|
+
/**
|
|
17420
|
+
* Close all tabs except the currently active one.
|
|
17421
|
+
*
|
|
17422
|
+
* @example
|
|
17423
|
+
* await utils.closeOtherTabs(); // keeps only the active tab open
|
|
17424
|
+
*/
|
|
17045
17425
|
async closeOtherTabs() {
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
await Promise.all(toClose.map((p) => p.close()));
|
|
17052
|
-
} catch (error) {
|
|
17053
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
17054
|
-
await this.log("error", `\u274C closeOtherTabs failed \u2014 ${msg}`);
|
|
17055
|
-
}
|
|
17426
|
+
this.checkAbort();
|
|
17427
|
+
const pages = await this.ctx.browser.pages();
|
|
17428
|
+
const toClose = pages.filter((p) => p !== this.activePage && !p.isClosed());
|
|
17429
|
+
await this.log("info", `\u{1F5D1}\uFE0F Closing ${toClose.length} other tab(s)...`);
|
|
17430
|
+
await Promise.all(toClose.map((p) => p.close()));
|
|
17056
17431
|
}
|
|
17057
17432
|
/**
|
|
17058
|
-
* Close ALL tabs
|
|
17059
|
-
* Useful for full cleanup before flow ends.
|
|
17433
|
+
* Close ALL tabs including the current one.
|
|
17434
|
+
* Useful for full cleanup before a flow ends.
|
|
17435
|
+
*
|
|
17436
|
+
* @example
|
|
17437
|
+
* await utils.closeAllTabs();
|
|
17060
17438
|
*/
|
|
17061
17439
|
async closeAllTabs() {
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
17066
|
-
|
|
17067
|
-
await Promise.all(toClose.map((p) => p.close()));
|
|
17068
|
-
} catch (error) {
|
|
17069
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
17070
|
-
await this.log("error", `\u274C closeAllTabs failed \u2014 ${msg}`);
|
|
17071
|
-
}
|
|
17440
|
+
this.checkAbort();
|
|
17441
|
+
const pages = await this.ctx.browser.pages();
|
|
17442
|
+
const toClose = pages.filter((p) => !p.isClosed());
|
|
17443
|
+
await this.log("info", `\u{1F5D1}\uFE0F Closing all ${toClose.length} tab(s)...`);
|
|
17444
|
+
await Promise.all(toClose.map((p) => p.close()));
|
|
17072
17445
|
}
|
|
17073
17446
|
/**
|
|
17074
|
-
* Switch back to the default (initial)
|
|
17075
|
-
*
|
|
17447
|
+
* Switch back to the default (initial) tab — the first tab opened when the flow started.
|
|
17448
|
+
* Resets both the active page and active frame (exits any iframe).
|
|
17449
|
+
* Typically used after `activePopup()` or `activeTab()` to return to the main tab.
|
|
17450
|
+
*
|
|
17451
|
+
* @returns The default Page handle
|
|
17452
|
+
*
|
|
17453
|
+
* @example
|
|
17454
|
+
* await utils.activePopup({ matcher: "MetaMask" });
|
|
17455
|
+
* await utils.click("#approve");
|
|
17456
|
+
* await utils.activeDefault(); // back to original tab
|
|
17076
17457
|
*/
|
|
17077
|
-
async
|
|
17458
|
+
async activeDefault() {
|
|
17078
17459
|
this.checkAbort();
|
|
17079
|
-
await this.log("info", `\u{1F504} Switching to default page
|
|
17080
|
-
if (!this.
|
|
17081
|
-
await this.
|
|
17460
|
+
await this.log("info", `\u{1F504} Switching to default page...`);
|
|
17461
|
+
if (!this.defaultPage.isClosed()) {
|
|
17462
|
+
await this.defaultPage.bringToFront();
|
|
17463
|
+
await this.enableFocusEmulation(this.defaultPage);
|
|
17082
17464
|
}
|
|
17083
|
-
|
|
17465
|
+
this.activePage = this.defaultPage;
|
|
17466
|
+
this.activeFrame = null;
|
|
17467
|
+
return this.activePage;
|
|
17084
17468
|
}
|
|
17469
|
+
/**
|
|
17470
|
+
* Log a config object in a readable format.
|
|
17471
|
+
*
|
|
17472
|
+
* @param config - Key-value object to log
|
|
17473
|
+
* @param label - Label for the log entry (default: "Config")
|
|
17474
|
+
*/
|
|
17085
17475
|
async logConfig(config, label = "Config") {
|
|
17086
17476
|
const lines = Object.entries(config).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join(",\n");
|
|
17087
17477
|
await this.log("info", `\u{1F4CB} ${label}:
|
|
@@ -17089,6 +17479,7 @@ var BrowserUtils = class {
|
|
|
17089
17479
|
${lines}
|
|
17090
17480
|
}`);
|
|
17091
17481
|
}
|
|
17482
|
+
/** Log the current profile input values for debugging. */
|
|
17092
17483
|
async logProfileInput() {
|
|
17093
17484
|
await this.logConfig(
|
|
17094
17485
|
this.ctx.profileInput,
|
|
@@ -17096,8 +17487,8 @@ ${lines}
|
|
|
17096
17487
|
);
|
|
17097
17488
|
}
|
|
17098
17489
|
/**
|
|
17099
|
-
* Log
|
|
17100
|
-
*
|
|
17490
|
+
* Log all current output values (including values from previous runs and newly written values).
|
|
17491
|
+
* Useful for debugging — see what outputs have been set.
|
|
17101
17492
|
*/
|
|
17102
17493
|
async logProfileOutput() {
|
|
17103
17494
|
const output = this.ctx.output;
|
|
@@ -17108,6 +17499,7 @@ ${lines}
|
|
|
17108
17499
|
}
|
|
17109
17500
|
await this.logConfig(output, "Profile Output");
|
|
17110
17501
|
}
|
|
17502
|
+
/** Log the current global input values for debugging. */
|
|
17111
17503
|
async logGlobalInput() {
|
|
17112
17504
|
await this.logConfig(
|
|
17113
17505
|
this.ctx.globalInput,
|
|
@@ -17115,16 +17507,24 @@ ${lines}
|
|
|
17115
17507
|
);
|
|
17116
17508
|
}
|
|
17117
17509
|
/**
|
|
17118
|
-
*
|
|
17119
|
-
*
|
|
17120
|
-
*
|
|
17510
|
+
* Write an output value for the current profile.
|
|
17511
|
+
* Dispatched via a dedicated channel (type: "profile_output") — separate from logs.
|
|
17512
|
+
* Also logs the value to console/UI for debugging.
|
|
17513
|
+
*
|
|
17514
|
+
* ⚠️ Key must be defined in `config.output[]` — throws Error if invalid.
|
|
17121
17515
|
*
|
|
17122
|
-
*
|
|
17516
|
+
* Accepted value types:
|
|
17517
|
+
* - `string | number | boolean`
|
|
17518
|
+
* - `Array` (max 20 elements, each must be primitive)
|
|
17519
|
+
* - `Object` (max 10 entries, values must be primitive)
|
|
17123
17520
|
*
|
|
17124
|
-
*
|
|
17125
|
-
* -
|
|
17126
|
-
*
|
|
17127
|
-
*
|
|
17521
|
+
* @param key - Output key (must match config.output definition)
|
|
17522
|
+
* @param value - The value to write
|
|
17523
|
+
*
|
|
17524
|
+
* @example
|
|
17525
|
+
* await utils.writeOutput("status", "success");
|
|
17526
|
+
* await utils.writeOutput("balance", 1234.56);
|
|
17527
|
+
* await utils.writeOutput("tokens", ["ETH", "USDT"]);
|
|
17128
17528
|
*/
|
|
17129
17529
|
async writeOutput(key, value) {
|
|
17130
17530
|
this.checkAbort();
|
|
@@ -17145,9 +17545,16 @@ ${lines}
|
|
|
17145
17545
|
});
|
|
17146
17546
|
}
|
|
17147
17547
|
/**
|
|
17148
|
-
*
|
|
17149
|
-
* key
|
|
17150
|
-
*
|
|
17548
|
+
* Update a profile input field for the currently running profile.
|
|
17549
|
+
* The key must be defined in the profileInput schema.
|
|
17550
|
+
* The updated value is sent to the server to persist in AgentFlowConfig after execution.
|
|
17551
|
+
*
|
|
17552
|
+
* @param key - Profile input key (must match schema definition)
|
|
17553
|
+
* @param value - The new value (string, number, or boolean)
|
|
17554
|
+
*
|
|
17555
|
+
* @example
|
|
17556
|
+
* await utils.writeProfileInput("lastLoginDate", "2024-01-15");
|
|
17557
|
+
* await utils.writeProfileInput("retryCount", 3);
|
|
17151
17558
|
*/
|
|
17152
17559
|
async writeProfileInput(key, value) {
|
|
17153
17560
|
this.checkAbort();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hira-core/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "SDK for building Hira automation flows with TypeScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,10 +26,6 @@
|
|
|
26
26
|
],
|
|
27
27
|
"author": "tttKiet",
|
|
28
28
|
"homepage": "https://hira-sdk.vercel.app/",
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "https://github.com/tttKiet/hira-automation"
|
|
32
|
-
},
|
|
33
29
|
"license": "ISC",
|
|
34
30
|
"engines": {
|
|
35
31
|
"node": ">=18.0.0"
|