diff --git a/fern/credentials/totp.mdx b/fern/credentials/totp.mdx index b102e35dd..322737699 100644 --- a/fern/credentials/totp.mdx +++ b/fern/credentials/totp.mdx @@ -178,13 +178,14 @@ Here's the TOTP endpoint contract you should use: Request (POST): | Parameter | Type | Required? | Sample Value | Description | | --- | --- | --- | --- | --- | -| task_id | String | yes | tsk_123 | The task ID that needs the verification to be done | +| task_id | String | no | tsk_123 | The task ID that needs the verification to be done | +| workflow_run_id | String | no | wr_123456 | The workflow run ID that needs the verification to be done | +| workflow_permanent_id | String | no | wpid_123456 | The permanent workflow ID that needs the verification to be done | Response: | Parameter | Type | Required? | Sample Value | Description | | --- | --- | --- | --- | --- | -| task_id | String | yes | tsk_123 | The task ID that needs the verification to be done | -| verification_code | String | no | 123456 | The verification code | +| verification_code | String | yes | 123456 | The verification code | ### Validate The Sender of The Request Same as the webhook API, your server needs to make sure it’s Skyvern that’s making the request. diff --git a/skyvern-frontend/src/components/GeoTargetSelector.tsx b/skyvern-frontend/src/components/GeoTargetSelector.tsx index 9dc695bab..15454cb2c 100644 --- a/skyvern-frontend/src/components/GeoTargetSelector.tsx +++ b/skyvern-frontend/src/components/GeoTargetSelector.tsx @@ -28,12 +28,16 @@ interface GeoTargetSelectorProps { value: GeoTarget | null; onChange: (value: GeoTarget) => void; className?: string; + allowGranularSearch?: boolean; + modalPopover?: boolean; } export function GeoTargetSelector({ value, onChange, className, + allowGranularSearch = true, + modalPopover = false, }: GeoTargetSelectorProps) { const [open, setOpen] = React.useState(false); const [query, setQuery] = React.useState(""); @@ -47,7 +51,9 @@ export function GeoTargetSelector({ const handleSearch = useDebouncedCallback(async (searchQuery: string) => { setLoading(true); try { - const data = await searchGeoData(searchQuery); + const data = await searchGeoData(searchQuery, { + includeGranularResults: allowGranularSearch, + }); setResults(data); } catch (error) { console.error("Failed to search geo data", error); @@ -84,7 +90,7 @@ export function GeoTargetSelector({ }; return ( - + - + @@ -143,7 +153,7 @@ export function GeoTargetSelector({ )} - {results.subdivisions.length > 0 && ( + {allowGranularSearch && results.subdivisions.length > 0 && ( {results.subdivisions.map((item) => ( )} - {results.cities.length > 0 && ( + {allowGranularSearch && results.cities.length > 0 && ( {results.cities.map((item) => ( void; className?: string; + allowGranularSearch?: boolean; + modalPopover?: boolean; }; -function ProxySelector({ value, onChange, className }: Props) { +function ProxySelector({ + value, + onChange, + className, + allowGranularSearch = true, + modalPopover = false, +}: Props) { // Convert input (string enum or object) to GeoTarget for the selector const geoTargetValue = proxyLocationToGeoTarget(value); @@ -19,6 +27,8 @@ function ProxySelector({ value, onChange, className }: Props) { { // Convert back to ProxyLocation enum if possible (for simple countries) // or keep as GeoTarget object diff --git a/skyvern-frontend/src/routes/browserSessions/BrowserSessions.tsx b/skyvern-frontend/src/routes/browserSessions/BrowserSessions.tsx index 64cc27d43..e8edbcf01 100644 --- a/skyvern-frontend/src/routes/browserSessions/BrowserSessions.tsx +++ b/skyvern-frontend/src/routes/browserSessions/BrowserSessions.tsx @@ -6,6 +6,7 @@ import { ProxyLocation } from "@/api/types"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Drawer, DrawerContent, @@ -25,6 +26,13 @@ import { PaginationPrevious, } from "@/components/ui/pagination"; import { ProxySelector } from "@/components/ProxySelector"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Table, TableBody, @@ -35,7 +43,11 @@ import { } from "@/components/ui/table"; import { useBrowserSessionsQuery } from "@/routes/browserSessions/hooks/useBrowserSessionsQuery"; import { useCreateBrowserSessionMutation } from "@/routes/browserSessions/hooks/useCreateBrowserSessionMutation"; -import { type BrowserSession } from "@/routes/workflows/types/browserSessionTypes"; +import { + type BrowserSession, + type BrowserSessionExtension, + type BrowserSessionType, +} from "@/routes/workflows/types/browserSessionTypes"; import { CopyText } from "@/routes/workflows/editor/Workspace"; import { basicTimeFormat } from "@/util/timeFormat"; import { cn, formatMs, toDate } from "@/util/utils"; @@ -58,6 +70,31 @@ const Yes = () => ( ); +const BROWSER_TYPE_OPTIONS: Array<{ + value: BrowserSessionType; + label: string; +}> = [ + { value: "msedge", label: "Microsoft Edge" }, + { value: "chrome", label: "Google Chrome" }, +]; + +const EXTENSION_OPTIONS: Array<{ + value: BrowserSessionExtension; + label: string; + description: string; +}> = [ + { + value: "ad-blocker", + label: "Ad Blocker", + description: "Blocks ads and common trackers in session pages.", + }, + { + value: "captcha-solver", + label: "Captcha Solver", + description: "Enables automated captcha solving when available.", + }, +]; + function BrowserSessions() { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); @@ -65,9 +102,13 @@ function BrowserSessions() { const [sessionOptions, setSessionOptions] = useState<{ proxyLocation: ProxyLocation; timeoutMinutes: number | null; + browserType: BrowserSessionType | null; + extensions: BrowserSessionExtension[]; }>({ proxyLocation: ProxyLocation.Residential, timeoutMinutes: 60, + browserType: null, + extensions: [], }); const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1; @@ -123,6 +164,18 @@ function BrowserSessions() { } } + function toggleExtension(extension: BrowserSessionExtension) { + setSessionOptions((prev) => { + const exists = prev.extensions.includes(extension); + return { + ...prev, + extensions: exists + ? prev.extensions.filter((item) => item !== extension) + : [...prev.extensions, extension], + }; + }); + } + return (
{/* header */} @@ -336,11 +389,13 @@ function BrowserSessions() {
{ - setSessionOptions({ - ...sessionOptions, + setSessionOptions((prev) => ({ + ...prev, proxyLocation: value, - }); + })); }} /> @@ -367,6 +422,76 @@ function BrowserSessions() { }} /> +
+
+ + +
+ +
+
+
+ + +
+
+ {EXTENSION_OPTIONS.map((extension) => ( +
+ { + toggleExtension(extension.value); + }} + /> +
+ +

+ {extension.description} +

+
+
+ ))} +
+