@hira-core/sdk 1.0.6 → 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 +523 -128
- 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
|
@@ -16654,7 +16654,10 @@ var HidemiumStandaloneAdapter = class {
|
|
|
16654
16654
|
const command = [
|
|
16655
16655
|
`--window-position=${x},${y}`,
|
|
16656
16656
|
`--window-size=${windowConfig.width},${windowConfig.height}`,
|
|
16657
|
-
`--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`
|
|
16658
16661
|
].join(" ");
|
|
16659
16662
|
try {
|
|
16660
16663
|
await this.service.stopProfile(targetProfile.uuid);
|
|
@@ -16812,22 +16815,61 @@ function getSdkConfig() {
|
|
|
16812
16815
|
// src/utils/browser.utils.ts
|
|
16813
16816
|
var BrowserUtils = class {
|
|
16814
16817
|
constructor(context) {
|
|
16818
|
+
/** Currently active iframe — null means main frame. Changed via activeIframe() */
|
|
16819
|
+
this.activeFrame = null;
|
|
16815
16820
|
var _a;
|
|
16816
16821
|
this.ctx = context;
|
|
16817
16822
|
this.logger = context.logger;
|
|
16818
16823
|
this.outputDefs = (_a = context.outputDefinitions) != null ? _a : [];
|
|
16819
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
|
+
}
|
|
16820
16840
|
}
|
|
16821
16841
|
checkAbort() {
|
|
16822
16842
|
const signal = global.__HIRA_ABORT_SIGNAL__;
|
|
16823
16843
|
if (signal == null ? void 0 : signal.aborted) throw new Error("cancelled");
|
|
16824
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
|
+
*/
|
|
16825
16851
|
async sleep(ms) {
|
|
16852
|
+
this.checkAbort();
|
|
16826
16853
|
await this.log("debug", `\u23F3 Sleep ${ms}ms`);
|
|
16827
16854
|
await this.rawSleep(ms);
|
|
16828
16855
|
}
|
|
16829
|
-
|
|
16830
|
-
|
|
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;
|
|
16831
16873
|
try {
|
|
16832
16874
|
this.checkAbort();
|
|
16833
16875
|
const options = { timeout, visible: true };
|
|
@@ -16844,7 +16886,35 @@ var BrowserUtils = class {
|
|
|
16844
16886
|
return null;
|
|
16845
16887
|
}
|
|
16846
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
|
+
*/
|
|
16847
16916
|
async click(target, options) {
|
|
16917
|
+
var _a, _b;
|
|
16848
16918
|
const label = typeof target === "string" ? target : "[ElementHandle]";
|
|
16849
16919
|
try {
|
|
16850
16920
|
this.checkAbort();
|
|
@@ -16853,82 +16923,116 @@ var BrowserUtils = class {
|
|
|
16853
16923
|
if (typeof target === "string") {
|
|
16854
16924
|
element = await this.waitForElement(
|
|
16855
16925
|
target,
|
|
16856
|
-
options == null ? void 0 : options.
|
|
16926
|
+
(_a = options == null ? void 0 : options.waitTimeout) != null ? _a : 2e3,
|
|
16857
16927
|
options == null ? void 0 : options.frame
|
|
16858
16928
|
);
|
|
16859
16929
|
} else {
|
|
16860
16930
|
element = target;
|
|
16861
16931
|
}
|
|
16862
16932
|
if (!element) {
|
|
16863
|
-
|
|
16864
|
-
"error",
|
|
16865
|
-
`\u274C Click failed \u2014 element not found: ${this.shortSelector(label)}`
|
|
16866
|
-
);
|
|
16867
|
-
return false;
|
|
16933
|
+
throw new Error(`Click failed \u2014 element not found: ${this.shortSelector(label)}`);
|
|
16868
16934
|
}
|
|
16869
16935
|
await element.scrollIntoView();
|
|
16870
|
-
|
|
16936
|
+
const clickDelay = (_b = options == null ? void 0 : options.delay) != null ? _b : 1e3;
|
|
16937
|
+
if (clickDelay > 0) await this.sleep(clickDelay);
|
|
16871
16938
|
await element.click();
|
|
16872
16939
|
await this.actionDelay();
|
|
16873
16940
|
return true;
|
|
16874
16941
|
} catch (error) {
|
|
16875
16942
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16876
16943
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16877
|
-
|
|
16878
|
-
"error",
|
|
16879
|
-
`\u274C Click failed: ${this.shortSelector(label)} \u2014 ${msg}`
|
|
16880
|
-
);
|
|
16881
|
-
return false;
|
|
16944
|
+
throw new Error(`Click failed: ${this.shortSelector(label)} \u2014 ${msg}`);
|
|
16882
16945
|
}
|
|
16883
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
|
+
*/
|
|
16884
16966
|
async type(selector, text, options) {
|
|
16885
|
-
var _a;
|
|
16967
|
+
var _a, _b, _c, _d;
|
|
16968
|
+
const mode = (_a = options == null ? void 0 : options.mode) != null ? _a : "replace";
|
|
16886
16969
|
const masked = text.length > 20 ? text.slice(0, 20) + "..." : text;
|
|
16887
16970
|
try {
|
|
16888
16971
|
this.checkAbort();
|
|
16889
16972
|
await this.log(
|
|
16890
16973
|
"info",
|
|
16891
|
-
`\u2328\uFE0F Type "${masked}" \u2192 ${this.shortSelector(selector)}`
|
|
16974
|
+
`\u2328\uFE0F Type [${mode}] "${masked}" \u2192 ${this.shortSelector(selector)}`
|
|
16892
16975
|
);
|
|
16893
|
-
const element = await this.
|
|
16976
|
+
const element = await this.resolveElement(
|
|
16894
16977
|
selector,
|
|
16895
|
-
|
|
16978
|
+
options == null ? void 0 : options.waitTimeout,
|
|
16896
16979
|
options == null ? void 0 : options.frame
|
|
16897
16980
|
);
|
|
16898
16981
|
if (!element) {
|
|
16899
|
-
|
|
16900
|
-
"error",
|
|
16901
|
-
`\u274C Type failed \u2014 element not found: ${this.shortSelector(selector)}`
|
|
16902
|
-
);
|
|
16903
|
-
return false;
|
|
16982
|
+
throw new Error(`Type failed \u2014 element not found: ${this.shortSelector(selector)}`);
|
|
16904
16983
|
}
|
|
16905
16984
|
await element.scrollIntoView();
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
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
|
+
}
|
|
16909
17004
|
await this.actionDelay();
|
|
16910
17005
|
return true;
|
|
16911
17006
|
} catch (error) {
|
|
16912
17007
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16913
17008
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16914
|
-
|
|
16915
|
-
"error",
|
|
16916
|
-
`\u274C Type failed: ${this.shortSelector(selector)} \u2014 ${msg}`
|
|
16917
|
-
);
|
|
16918
|
-
return false;
|
|
17009
|
+
throw new Error(`Type failed: ${this.shortSelector(selector)} \u2014 ${msg}`);
|
|
16919
17010
|
}
|
|
16920
17011
|
}
|
|
16921
|
-
|
|
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;
|
|
16922
17026
|
try {
|
|
16923
17027
|
this.checkAbort();
|
|
16924
17028
|
await this.log("debug", `\u{1F4C4} getText: ${this.shortSelector(selector)}`);
|
|
16925
|
-
const scope = frame != null ?
|
|
16926
|
-
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);
|
|
16927
17031
|
if (!element) return null;
|
|
16928
17032
|
const text = await scope.evaluate(
|
|
16929
17033
|
(el) => {
|
|
16930
|
-
var
|
|
16931
|
-
return ((
|
|
17034
|
+
var _a2;
|
|
17035
|
+
return ((_a2 = el.textContent) == null ? void 0 : _a2.trim()) || "";
|
|
16932
17036
|
},
|
|
16933
17037
|
element
|
|
16934
17038
|
);
|
|
@@ -16942,158 +17046,432 @@ var BrowserUtils = class {
|
|
|
16942
17046
|
return null;
|
|
16943
17047
|
}
|
|
16944
17048
|
}
|
|
16945
|
-
|
|
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) {
|
|
16946
17064
|
this.checkAbort();
|
|
16947
17065
|
const el = await this.waitForElement(selector, timeout, frame);
|
|
16948
17066
|
return el !== null;
|
|
16949
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
|
+
*/
|
|
16950
17080
|
async goto(url2, options) {
|
|
16951
17081
|
try {
|
|
16952
17082
|
this.checkAbort();
|
|
16953
17083
|
await this.log("info", `\u{1F310} Navigate \u2192 ${url2}`);
|
|
16954
|
-
await this.
|
|
16955
|
-
waitUntil: (options == null ? void 0 : options.waitUntil) || "
|
|
16956
|
-
timeout:
|
|
17084
|
+
await this.activePage.goto(url2, {
|
|
17085
|
+
waitUntil: (options == null ? void 0 : options.waitUntil) || "load",
|
|
17086
|
+
timeout: 6e4
|
|
16957
17087
|
});
|
|
16958
17088
|
await this.actionDelay();
|
|
16959
17089
|
return true;
|
|
16960
17090
|
} catch (error) {
|
|
16961
17091
|
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16962
17092
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16963
|
-
|
|
16964
|
-
return false;
|
|
17093
|
+
throw new Error(`Navigate failed: ${url2} \u2014 ${msg}`);
|
|
16965
17094
|
}
|
|
16966
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
|
+
*/
|
|
16967
17108
|
async waitForNavigation(options) {
|
|
16968
17109
|
try {
|
|
16969
17110
|
this.checkAbort();
|
|
16970
17111
|
await this.log("debug", "\u{1F504} Waiting for navigation...");
|
|
16971
|
-
await this.
|
|
16972
|
-
timeout: (options == null ? void 0 : options.timeout) ||
|
|
16973
|
-
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"
|
|
16974
17115
|
});
|
|
16975
17116
|
return true;
|
|
16976
17117
|
} catch (err) {
|
|
16977
17118
|
if (err instanceof Error && err.message === "cancelled") throw err;
|
|
16978
|
-
|
|
16979
|
-
return false;
|
|
17119
|
+
throw new Error("Navigation timeout");
|
|
16980
17120
|
}
|
|
16981
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
|
+
*/
|
|
16982
17132
|
async screenshot(path2) {
|
|
16983
17133
|
try {
|
|
16984
17134
|
this.checkAbort();
|
|
16985
17135
|
await this.log("info", `\u{1F4F8} Screenshot${path2 ? `: ${path2}` : ""}`);
|
|
16986
|
-
return path2 ? await this.
|
|
17136
|
+
return path2 ? await this.activePage.screenshot({ path: path2 }) : await this.activePage.screenshot({ encoding: "base64" });
|
|
16987
17137
|
} catch (error) {
|
|
17138
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
16988
17139
|
const msg = error instanceof Error ? error.message : String(error);
|
|
16989
|
-
await this.log("
|
|
17140
|
+
await this.log("warn", `\u26A0\uFE0F Screenshot failed \u2014 ${msg}`);
|
|
16990
17141
|
return null;
|
|
16991
17142
|
}
|
|
16992
17143
|
}
|
|
16993
|
-
|
|
16994
|
-
|
|
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)";
|
|
16995
17198
|
this.checkAbort();
|
|
16996
|
-
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
|
+
};
|
|
16997
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));
|
|
16998
17260
|
const start = Date.now();
|
|
16999
|
-
const isMatch = (text) => {
|
|
17000
|
-
if (!text) return false;
|
|
17001
|
-
if (typeof matcher === "string") return text.includes(matcher);
|
|
17002
|
-
return matcher.test(text);
|
|
17003
|
-
};
|
|
17004
17261
|
while (Date.now() - start < timeout) {
|
|
17005
17262
|
const pages = await this.ctx.browser.pages();
|
|
17006
17263
|
for (let i = pages.length - 1; i >= 0; i--) {
|
|
17007
17264
|
const page = pages[i];
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
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 {
|
|
17013
17274
|
}
|
|
17014
17275
|
}
|
|
17015
17276
|
await new Promise((r) => setTimeout(r, 500));
|
|
17016
17277
|
}
|
|
17017
|
-
|
|
17018
|
-
"warn",
|
|
17019
|
-
`\u26A0\uFE0F Popup not found: ${label} (timeout: ${timeout}ms)`
|
|
17020
|
-
);
|
|
17021
|
-
return null;
|
|
17278
|
+
throw new Error(`Popup not found: ${label} (timeout: ${timeout}ms)`);
|
|
17022
17279
|
} catch (error) {
|
|
17280
|
+
if (error instanceof Error && error.message === "cancelled") throw error;
|
|
17023
17281
|
const msg = error instanceof Error ? error.message : String(error);
|
|
17024
|
-
|
|
17025
|
-
return null;
|
|
17282
|
+
throw new Error(`waitForPopup failed: ${label} \u2014 ${msg}`);
|
|
17026
17283
|
}
|
|
17027
17284
|
}
|
|
17028
|
-
|
|
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;
|
|
17367
|
+
}
|
|
17368
|
+
return page;
|
|
17369
|
+
}
|
|
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) {
|
|
17029
17385
|
try {
|
|
17030
17386
|
this.checkAbort();
|
|
17031
|
-
await this.log("info", `\u{1F504}
|
|
17387
|
+
await this.log("info", `\u{1F504} Active tab[${index}]...`);
|
|
17032
17388
|
const pages = await this.ctx.browser.pages();
|
|
17033
17389
|
if (index >= 0 && index < pages.length) {
|
|
17034
17390
|
const page = pages[index];
|
|
17035
17391
|
await page.bringToFront();
|
|
17392
|
+
await this.enableFocusEmulation(page);
|
|
17393
|
+
this.activePage = page;
|
|
17394
|
+
this.activeFrame = null;
|
|
17036
17395
|
return page;
|
|
17037
17396
|
}
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
);
|
|
17042
|
-
|
|
17043
|
-
} catch {
|
|
17044
|
-
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}`);
|
|
17045
17402
|
}
|
|
17046
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
|
+
*/
|
|
17047
17411
|
async closeCurrentTab() {
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
}
|
|
17054
|
-
} 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;
|
|
17055
17417
|
}
|
|
17056
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
|
+
*/
|
|
17057
17425
|
async closeOtherTabs() {
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
await Promise.all(toClose.map((p) => p.close()));
|
|
17064
|
-
} catch (error) {
|
|
17065
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
17066
|
-
await this.log("error", `\u274C closeOtherTabs failed \u2014 ${msg}`);
|
|
17067
|
-
}
|
|
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()));
|
|
17068
17431
|
}
|
|
17069
17432
|
/**
|
|
17070
|
-
* Close ALL tabs
|
|
17071
|
-
* 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();
|
|
17072
17438
|
*/
|
|
17073
17439
|
async closeAllTabs() {
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
await Promise.all(toClose.map((p) => p.close()));
|
|
17080
|
-
} catch (error) {
|
|
17081
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
17082
|
-
await this.log("error", `\u274C closeAllTabs failed \u2014 ${msg}`);
|
|
17083
|
-
}
|
|
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()));
|
|
17084
17445
|
}
|
|
17085
17446
|
/**
|
|
17086
|
-
* Switch back to the default (initial)
|
|
17087
|
-
*
|
|
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
|
|
17088
17457
|
*/
|
|
17089
|
-
async
|
|
17458
|
+
async activeDefault() {
|
|
17090
17459
|
this.checkAbort();
|
|
17091
|
-
await this.log("info", `\u{1F504} Switching to default page
|
|
17092
|
-
if (!this.
|
|
17093
|
-
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);
|
|
17094
17464
|
}
|
|
17095
|
-
|
|
17465
|
+
this.activePage = this.defaultPage;
|
|
17466
|
+
this.activeFrame = null;
|
|
17467
|
+
return this.activePage;
|
|
17096
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
|
+
*/
|
|
17097
17475
|
async logConfig(config, label = "Config") {
|
|
17098
17476
|
const lines = Object.entries(config).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`).join(",\n");
|
|
17099
17477
|
await this.log("info", `\u{1F4CB} ${label}:
|
|
@@ -17101,6 +17479,7 @@ var BrowserUtils = class {
|
|
|
17101
17479
|
${lines}
|
|
17102
17480
|
}`);
|
|
17103
17481
|
}
|
|
17482
|
+
/** Log the current profile input values for debugging. */
|
|
17104
17483
|
async logProfileInput() {
|
|
17105
17484
|
await this.logConfig(
|
|
17106
17485
|
this.ctx.profileInput,
|
|
@@ -17108,8 +17487,8 @@ ${lines}
|
|
|
17108
17487
|
);
|
|
17109
17488
|
}
|
|
17110
17489
|
/**
|
|
17111
|
-
* Log
|
|
17112
|
-
*
|
|
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.
|
|
17113
17492
|
*/
|
|
17114
17493
|
async logProfileOutput() {
|
|
17115
17494
|
const output = this.ctx.output;
|
|
@@ -17120,6 +17499,7 @@ ${lines}
|
|
|
17120
17499
|
}
|
|
17121
17500
|
await this.logConfig(output, "Profile Output");
|
|
17122
17501
|
}
|
|
17502
|
+
/** Log the current global input values for debugging. */
|
|
17123
17503
|
async logGlobalInput() {
|
|
17124
17504
|
await this.logConfig(
|
|
17125
17505
|
this.ctx.globalInput,
|
|
@@ -17127,16 +17507,24 @@ ${lines}
|
|
|
17127
17507
|
);
|
|
17128
17508
|
}
|
|
17129
17509
|
/**
|
|
17130
|
-
*
|
|
17131
|
-
*
|
|
17132
|
-
*
|
|
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.
|
|
17515
|
+
*
|
|
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)
|
|
17133
17520
|
*
|
|
17134
|
-
*
|
|
17521
|
+
* @param key - Output key (must match config.output definition)
|
|
17522
|
+
* @param value - The value to write
|
|
17135
17523
|
*
|
|
17136
|
-
*
|
|
17137
|
-
*
|
|
17138
|
-
*
|
|
17139
|
-
*
|
|
17524
|
+
* @example
|
|
17525
|
+
* await utils.writeOutput("status", "success");
|
|
17526
|
+
* await utils.writeOutput("balance", 1234.56);
|
|
17527
|
+
* await utils.writeOutput("tokens", ["ETH", "USDT"]);
|
|
17140
17528
|
*/
|
|
17141
17529
|
async writeOutput(key, value) {
|
|
17142
17530
|
this.checkAbort();
|
|
@@ -17157,9 +17545,16 @@ ${lines}
|
|
|
17157
17545
|
});
|
|
17158
17546
|
}
|
|
17159
17547
|
/**
|
|
17160
|
-
*
|
|
17161
|
-
* key
|
|
17162
|
-
*
|
|
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);
|
|
17163
17558
|
*/
|
|
17164
17559
|
async writeProfileInput(key, value) {
|
|
17165
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"
|