From 51ce4e8627dd09b59ae06154544e7ff9556c9acb Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Tue, 22 Jul 2025 07:21:45 -0700 Subject: [PATCH] add login to skyvern library (#3016) --- fern/docs.yml | 1 + fern/openapi/skyvern_openapi.json | 272 ++++++++++++------ skyvern/client/__init__.py | 14 + skyvern/client/client.py | 151 +++++++++- skyvern/client/core/client_wrapper.py | 2 +- skyvern/client/types/__init__.py | 16 ++ .../client/types/bitwarden_login_request.py | 77 +++++ skyvern/client/types/login_request_body.py | 82 ++++++ .../types/one_password_login_request.py | 77 +++++ skyvern/client/types/skyvern_login_request.py | 72 +++++ skyvern/forge/sdk/routes/code_samples.py | 25 +- skyvern/library/skyvern.py | 21 ++ 12 files changed, 707 insertions(+), 103 deletions(-) create mode 100644 skyvern/client/types/bitwarden_login_request.py create mode 100644 skyvern/client/types/login_request_body.py create mode 100644 skyvern/client/types/one_password_login_request.py create mode 100644 skyvern/client/types/skyvern_login_request.py diff --git a/fern/docs.yml b/fern/docs.yml index 5461b8ae..02ff5109 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -162,6 +162,7 @@ navigation: - GET /v1/runs/{run_id} - POST /v1/runs/{run_id}/cancel - POST /v1/runs/{run_id}/retry_webhook + - POST /v1/run/tasks/login - section: Workflows contents: - POST /v1/run/workflows diff --git a/fern/openapi/skyvern_openapi.json b/fern/openapi/skyvern_openapi.json index 27390dbf..98743c5d 100644 --- a/fern/openapi/skyvern_openapi.json +++ b/fern/openapi/skyvern_openapi.json @@ -363,7 +363,9 @@ "description": "Successful Response", "content": { "application/json": { - "schema": {} + "schema": { + + } } } }, @@ -450,7 +452,7 @@ "code-samples": [ { "sdk": "curl", - "code": "curl -X POST https://api.skyvern.com/v1/workflows --header 'x-api-key: {{x-api-key}}' --header 'Content-Type: text/plain' --data-raw 'title: Contact Forms\ndescription: Fill the contact form on the website\nproxy_location: RESIDENTIAL\nwebhook_callback_url: https://example.com/webhook\ntotp_verification_url: https://example.com/totp\npersist_browser_session: false\nmodel:\n name: gpt-4.1\nworkflow_definition:\n parameters:\n - key: website_url\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: name\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: additional_information\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: |-\n Message: I'd love to learn more about your...\n Phone: 123-456-7890\n Inquiry type: sales\n Optional Subject: Hello from [Company Here]\n - key: email\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n blocks:\n - label: Fill_Out_Contact_Form\n continue_on_failure: true\n block_type: navigation\n url: \"{{website_url}}\"\n title: Fill_Out_Contact_Form\n engine: skyvern-1.0\n navigation_goal: >-\n Find the contact form. Fill out the contact us form and submit it. Your\n goal is complete when the page says your message has been sent. In the\n case you can't find a contact us form, terminate.\n\n\n Fill out required fields as best you can using the following\n information:\n\n {{name}}\n\n {{email}}\n\n {{additional_information}}\n error_code_mapping: null\n max_retries: 0\n max_steps_per_run: null\n complete_on_download: false\n download_suffix: null\n parameter_keys: []\n totp_identifier: null\n totp_verification_url: null\n cache_actions: false\n complete_criterion: \"\"\n terminate_criterion: \"\"\n include_action_history_in_verification: false\n - label: Extract_Email\n continue_on_failure: false\n block_type: extraction\n url: \"\"\n title: Extract_Email\n data_extraction_goal: \"Extract a company email if available \"\n data_schema: null\n max_retries: 0\n max_steps_per_run: null\n parameter_keys: []\n cache_actions: false\n'\n" + "code": "curl -X POST https://api.skyvern.com/v1/workflows --header 'x-api-key: {{x-api-key}}' --header 'Content-Type: text/plain' --data-raw 'title: Contact Forms\ndescription: Fill the contact form on the website\nproxy_location: RESIDENTIAL\nwebhook_callback_url: https://example.com/webhook\ntotp_verification_url: https://example.com/totp\npersist_browser_session: false\nmodel:\n name: gpt-4.1\nworkflow_definition:\n parameters:\n - key: website_url\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: name\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: additional_information\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: |-\n Message: I'd love to learn more about your...\n Phone: 123-456-7890\n Inquiry type: sales\n Optional Subject: Hello from [Company Here]\n - key: email\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n blocks:\n - label: Fill_Out_Contact_Form\n continue_on_failure: true\n block_type: navigation\n url: \"{{website_url}}\"\n title: Fill_Out_Contact_Form\n engine: skyvern-1.0\n navigation_goal: \u003E-\n Find the contact form. Fill out the contact us form and submit it. Your\n goal is complete when the page says your message has been sent. In the\n case you can't find a contact us form, terminate.\n\n\n Fill out required fields as best you can using the following\n information:\n\n {{name}}\n\n {{email}}\n\n {{additional_information}}\n error_code_mapping: null\n max_retries: 0\n max_steps_per_run: null\n complete_on_download: false\n download_suffix: null\n parameter_keys: []\n totp_identifier: null\n totp_verification_url: null\n cache_actions: false\n complete_criterion: \"\"\n terminate_criterion: \"\"\n include_action_history_in_verification: false\n - label: Extract_Email\n continue_on_failure: false\n block_type: extraction\n url: \"\"\n title: Extract_Email\n data_extraction_goal: \"Extract a company email if available \"\n data_schema: null\n max_retries: 0\n max_steps_per_run: null\n parameter_keys: []\n cache_actions: false\n'\n" }, { "sdk": "python", @@ -661,7 +663,7 @@ "code-samples": [ { "sdk": "curl", - "code": "curl -X POST https://api.skyvern.com/v1/workflows/wpid_123 --header 'x-api-key: {{x-api-key}}' --header 'Content-Type: text/plain' --data-raw 'title: Contact Forms\ndescription: Fill the contact form on the website\nproxy_location: RESIDENTIAL\nwebhook_callback_url: https://example.com/webhook\ntotp_verification_url: https://example.com/totp\npersist_browser_session: false\nmodel:\n name: gpt-4.1\nworkflow_definition:\n parameters:\n - key: website_url\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: name\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: additional_information\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: |-\n Message: I'd love to learn more about your...\n Phone: 123-456-7890\n Inquiry type: sales\n Optional Subject: Hello from [Company Here]\n - key: email\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n blocks:\n - label: Fill_Out_Contact_Form\n continue_on_failure: true\n block_type: navigation\n url: \"{{website_url}}\"\n title: Fill_Out_Contact_Form\n engine: skyvern-1.0\n navigation_goal: >-\n Find the contact form. Fill out the contact us form and submit it. Your\n goal is complete when the page says your message has been sent. In the\n case you can't find a contact us form, terminate.\n\n\n Fill out required fields as best you can using the following\n information:\n\n {{name}}\n\n {{email}}\n\n {{additional_information}}\n error_code_mapping: null\n max_retries: 0\n max_steps_per_run: null\n complete_on_download: false\n download_suffix: null\n parameter_keys: []\n totp_identifier: null\n totp_verification_url: null\n cache_actions: false\n complete_criterion: \"\"\n terminate_criterion: \"\"\n include_action_history_in_verification: false\n - label: Extract_Email\n continue_on_failure: false\n block_type: extraction\n url: \"\"\n title: Extract_Email\n data_extraction_goal: \"Extract a company email if available \"\n data_schema: null\n max_retries: 0\n max_steps_per_run: null\n parameter_keys: []\n cache_actions: false\n'\n" + "code": "curl -X POST https://api.skyvern.com/v1/workflows/wpid_123 --header 'x-api-key: {{x-api-key}}' --header 'Content-Type: text/plain' --data-raw 'title: Contact Forms\ndescription: Fill the contact form on the website\nproxy_location: RESIDENTIAL\nwebhook_callback_url: https://example.com/webhook\ntotp_verification_url: https://example.com/totp\npersist_browser_session: false\nmodel:\n name: gpt-4.1\nworkflow_definition:\n parameters:\n - key: website_url\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: name\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n - key: additional_information\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: |-\n Message: I'd love to learn more about your...\n Phone: 123-456-7890\n Inquiry type: sales\n Optional Subject: Hello from [Company Here]\n - key: email\n description: null\n parameter_type: workflow\n workflow_parameter_type: string\n default_value: null\n blocks:\n - label: Fill_Out_Contact_Form\n continue_on_failure: true\n block_type: navigation\n url: \"{{website_url}}\"\n title: Fill_Out_Contact_Form\n engine: skyvern-1.0\n navigation_goal: \u003E-\n Find the contact form. Fill out the contact us form and submit it. Your\n goal is complete when the page says your message has been sent. In the\n case you can't find a contact us form, terminate.\n\n\n Fill out required fields as best you can using the following\n information:\n\n {{name}}\n\n {{email}}\n\n {{additional_information}}\n error_code_mapping: null\n max_retries: 0\n max_steps_per_run: null\n complete_on_download: false\n download_suffix: null\n parameter_keys: []\n totp_identifier: null\n totp_verification_url: null\n cache_actions: false\n complete_criterion: \"\"\n terminate_criterion: \"\"\n include_action_history_in_verification: false\n - label: Extract_Email\n continue_on_failure: false\n block_type: extraction\n url: \"\"\n title: Extract_Email\n data_extraction_goal: \"Extract a company email if available \"\n data_schema: null\n max_retries: 0\n max_steps_per_run: null\n parameter_keys: []\n cache_actions: false\n'\n" }, { "sdk": "python", @@ -719,7 +721,9 @@ "description": "Successfully deleted workflow", "content": { "application/json": { - "schema": {} + "schema": { + + } } } }, @@ -947,7 +951,9 @@ "description": "Successful Response", "content": { "application/json": { - "schema": {} + "schema": { + + } } } }, @@ -980,8 +986,8 @@ "tags": [ "Browser Sessions" ], - "summary": "Create a new browser session", - "description": "Create a new browser session", + "summary": "Create a session", + "description": "Create a browser session that persists across multiple runs", "operationId": "create_browser_session_v1_browser_sessions_post", "parameters": [ { @@ -1054,7 +1060,7 @@ "tags": [ "Browser Sessions" ], - "summary": "Get all active browser sessions", + "summary": "Get active browser sessions", "description": "Get all active browser sessions for the organization", "operationId": "get_browser_sessions_v1_browser_sessions_get", "parameters": [ @@ -1124,8 +1130,8 @@ "tags": [ "Browser Sessions" ], - "summary": "Close a browser session", - "description": "Close a browser session", + "summary": "Close a session", + "description": "Close a session. Once closed, the session cannot be used again.", "operationId": "close_browser_session_v1_browser_sessions__browser_session_id__close_post", "parameters": [ { @@ -1166,7 +1172,9 @@ "description": "Successfully closed browser session", "content": { "application/json": { - "schema": {} + "schema": { + + } } } }, @@ -1202,8 +1210,8 @@ "tags": [ "Browser Sessions" ], - "summary": "Get browser session details", - "description": "Get details about a specific browser session by ID", + "summary": "Get a session", + "description": "Get details about a specific browser session, including the browser address for cdp connection.", "operationId": "get_browser_session_v1_browser_sessions__browser_session_id__get", "parameters": [ { @@ -1250,12 +1258,12 @@ } } }, - "403": { - "description": "Unauthorized - Invalid or missing authentication" - }, "404": { "description": "Browser session not found" }, + "403": { + "description": "Unauthorized - Invalid or missing authentication" + }, "422": { "description": "Validation Error", "content": { @@ -1458,9 +1466,7 @@ "type": "integer", "minimum": 1, "description": "Page number for pagination", - "examples": [ - 1 - ], + "examples": [1], "openapi_extra": { "x-fern-sdk-parameter-name": "page" }, @@ -1477,9 +1483,7 @@ "type": "integer", "minimum": 1, "description": "Number of items per page", - "examples": [ - 10 - ], + "examples": [10], "openapi_extra": { "x-fern-sdk-parameter-name": "page_size" }, @@ -1701,7 +1705,11 @@ }, "/v1/run/tasks/login": { "post": { - "summary": "Login", + "tags": [ + "Agent" + ], + "summary": "Login Task", + "description": "Log in to a website using either credential stored in Skyvern, Bitwarden or 1Password", "operationId": "login_v1_run_tasks_login_post", "parameters": [ { @@ -1730,7 +1738,7 @@ "schema": { "oneOf": [ { - "$ref": "#/components/schemas/SkyvernCredentialLoginRequest" + "$ref": "#/components/schemas/SkyvernLoginRequest" }, { "$ref": "#/components/schemas/BitwardenLoginRequest" @@ -1742,7 +1750,7 @@ "discriminator": { "propertyName": "credential_type", "mapping": { - "skyvern": "#/components/schemas/SkyvernCredentialLoginRequest", + "skyvern": "#/components/schemas/SkyvernLoginRequest", "bitwarden": "#/components/schemas/BitwardenLoginRequest", "1password": "#/components/schemas/OnePasswordLoginRequest" } @@ -1773,7 +1781,26 @@ } } } - } + }, + "x-fern-sdk-method-name": "login", + "x-fern-examples": [ + { + "code-samples": [ + { + "sdk": "python", + "code": "# Login with password saved in Skyvern\nfrom skyvern import Skyvern\n\nskyvern = Skyvern(api_key=\"YOUR_API_KEY\")\nawait skyvern.login(credential_id=\"cred_123\")\n" + }, + { + "sdk": "python", + "code": "# Login with password saved in Bitwarden\nfrom skyvern import Skyvern\n\nskyvern = Skyvern(api_key=\"YOUR_API_KEY\")\n# Login with a Bitwarden collection and website url filter\nawait skyvern.login(bitwarden_collection_id=\"BITWARDEN COLLECTION ID\", url_filter=\"https://example.com\")\n\n# Login with a Bitwarden item\nawait skyvern.login(bitwarden_item_id=\"BITWARDEN ITEM ID\")\n" + }, + { + "sdk": "python", + "code": "# Login with password saved in 1Password\nfrom skyvern import Skyvern\n\nskyvern = Skyvern(api_key=\"YOUR_API_KEY\")\nawait skyvern.login(onepassword_vault_id=\"ONEPASSWORD VAULT ID\", onepassword_item_id=\"ONEPASSWORD ITEM ID\")\n" + } + ] + } + ] } } }, @@ -1991,7 +2018,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -2796,7 +2825,8 @@ "type": "null" } ], - "title": "Url" + "title": "Url", + "description": "Website url" }, "prompt": { "anyOf": [ @@ -2807,7 +2837,8 @@ "type": "null" } ], - "title": "Prompt" + "title": "Prompt", + "description": "Login instructions. Skyvern has default prompt/instruction for login if this field is not provided." }, "webhook_url": { "anyOf": [ @@ -2818,7 +2849,8 @@ "type": "null" } ], - "title": "Webhook Url" + "title": "Webhook Url", + "description": "Webhook URL to send login status updates" }, "proxy_location": { "anyOf": [ @@ -2828,7 +2860,8 @@ { "type": "null" } - ] + ], + "description": "Proxy location to use" }, "totp_identifier": { "anyOf": [ @@ -2839,7 +2872,8 @@ "type": "null" } ], - "title": "Totp Identifier" + "title": "Totp Identifier", + "description": "Identifier for TOTP (Time-based One-Time Password) if required" }, "totp_url": { "anyOf": [ @@ -2850,7 +2884,8 @@ "type": "null" } ], - "title": "Totp Url" + "title": "Totp Url", + "description": "TOTP URL to fetch one-time passwords" }, "browser_session_id": { "anyOf": [ @@ -2861,7 +2896,8 @@ "type": "null" } ], - "title": "Browser Session Id" + "title": "Browser Session Id", + "description": "ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`" }, "extra_http_headers": { "anyOf": [ @@ -2875,7 +2911,8 @@ "type": "null" } ], - "title": "Extra Http Headers" + "title": "Extra Http Headers", + "description": "Additional HTTP headers to include in requests" }, "max_screenshot_scrolling_times": { "anyOf": [ @@ -2886,7 +2923,8 @@ "type": "null" } ], - "title": "Max Screenshot Scrolling Times" + "title": "Max Screenshot Scrolling Times", + "description": "Maximum number of times to scroll for screenshots" }, "credential_type": { "type": "string", @@ -2894,7 +2932,7 @@ "title": "Credential Type", "default": "bitwarden" }, - "collection_id": { + "bitwarden_collection_id": { "anyOf": [ { "type": "string" @@ -2903,9 +2941,10 @@ "type": "null" } ], - "title": "Collection Id" + "title": "Bitwarden Collection Id", + "description": "Bitwarden collection ID" }, - "item_id": { + "bitwarden_item_id": { "anyOf": [ { "type": "string" @@ -2914,11 +2953,13 @@ "type": "null" } ], - "title": "Item Id" + "title": "Bitwarden Item Id", + "description": "Bitwarden item ID" } }, "type": "object", - "title": "BitwardenLoginRequest" + "title": "BitwardenLoginRequest", + "description": "Login with password saved in Bitwarden" }, "BitwardenSensitiveInformationParameter": { "properties": { @@ -3138,10 +3179,7 @@ ], "title": "Timeout", "description": "Timeout in minutes for the session. Timeout is applied after the session is started. Defaults to 60 minutes.", - "examples": [ - 60, - 120 - ] + "examples": [60, 120] }, "browser_address": { "anyOf": [ @@ -3460,7 +3498,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -3947,7 +3987,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -4161,7 +4203,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -4324,7 +4368,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -5655,7 +5701,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -6074,7 +6122,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -6646,7 +6696,8 @@ "type": "null" } ], - "title": "Url" + "title": "Url", + "description": "Website url" }, "prompt": { "anyOf": [ @@ -6657,7 +6708,8 @@ "type": "null" } ], - "title": "Prompt" + "title": "Prompt", + "description": "Login instructions. Skyvern has default prompt/instruction for login if this field is not provided." }, "webhook_url": { "anyOf": [ @@ -6668,7 +6720,8 @@ "type": "null" } ], - "title": "Webhook Url" + "title": "Webhook Url", + "description": "Webhook URL to send login status updates" }, "proxy_location": { "anyOf": [ @@ -6678,7 +6731,8 @@ { "type": "null" } - ] + ], + "description": "Proxy location to use" }, "totp_identifier": { "anyOf": [ @@ -6689,7 +6743,8 @@ "type": "null" } ], - "title": "Totp Identifier" + "title": "Totp Identifier", + "description": "Identifier for TOTP (Time-based One-Time Password) if required" }, "totp_url": { "anyOf": [ @@ -6700,7 +6755,8 @@ "type": "null" } ], - "title": "Totp Url" + "title": "Totp Url", + "description": "TOTP URL to fetch one-time passwords" }, "browser_session_id": { "anyOf": [ @@ -6711,7 +6767,8 @@ "type": "null" } ], - "title": "Browser Session Id" + "title": "Browser Session Id", + "description": "ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`" }, "extra_http_headers": { "anyOf": [ @@ -6725,7 +6782,8 @@ "type": "null" } ], - "title": "Extra Http Headers" + "title": "Extra Http Headers", + "description": "Additional HTTP headers to include in requests" }, "max_screenshot_scrolling_times": { "anyOf": [ @@ -6736,7 +6794,8 @@ "type": "null" } ], - "title": "Max Screenshot Scrolling Times" + "title": "Max Screenshot Scrolling Times", + "description": "Maximum number of times to scroll for screenshots" }, "credential_type": { "type": "string", @@ -6744,21 +6803,24 @@ "title": "Credential Type", "default": "1password" }, - "vault_id": { + "onepassword_vault_id": { "type": "string", - "title": "Vault Id" + "title": "Onepassword Vault Id", + "description": "1Password vault ID." }, - "item_id": { + "onepassword_item_id": { "type": "string", - "title": "Item Id" + "title": "Onepassword Item Id", + "description": "1Password item ID." } }, "type": "object", "required": [ - "vault_id", - "item_id" + "onepassword_vault_id", + "onepassword_item_id" ], - "title": "OnePasswordLoginRequest" + "title": "OnePasswordLoginRequest", + "description": "Login with password saved in 1Password" }, "OutputParameter": { "properties": { @@ -7212,7 +7274,7 @@ ], "title": "SendEmailBlockYAML" }, - "SkyvernCredentialLoginRequest": { + "SkyvernLoginRequest": { "properties": { "url": { "anyOf": [ @@ -7223,7 +7285,8 @@ "type": "null" } ], - "title": "Url" + "title": "Url", + "description": "Website url" }, "prompt": { "anyOf": [ @@ -7234,7 +7297,8 @@ "type": "null" } ], - "title": "Prompt" + "title": "Prompt", + "description": "Login instructions. Skyvern has default prompt/instruction for login if this field is not provided." }, "webhook_url": { "anyOf": [ @@ -7245,7 +7309,8 @@ "type": "null" } ], - "title": "Webhook Url" + "title": "Webhook Url", + "description": "Webhook URL to send login status updates" }, "proxy_location": { "anyOf": [ @@ -7255,7 +7320,8 @@ { "type": "null" } - ] + ], + "description": "Proxy location to use" }, "totp_identifier": { "anyOf": [ @@ -7266,7 +7332,8 @@ "type": "null" } ], - "title": "Totp Identifier" + "title": "Totp Identifier", + "description": "Identifier for TOTP (Time-based One-Time Password) if required" }, "totp_url": { "anyOf": [ @@ -7277,7 +7344,8 @@ "type": "null" } ], - "title": "Totp Url" + "title": "Totp Url", + "description": "TOTP URL to fetch one-time passwords" }, "browser_session_id": { "anyOf": [ @@ -7288,7 +7356,8 @@ "type": "null" } ], - "title": "Browser Session Id" + "title": "Browser Session Id", + "description": "ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456`" }, "extra_http_headers": { "anyOf": [ @@ -7302,7 +7371,8 @@ "type": "null" } ], - "title": "Extra Http Headers" + "title": "Extra Http Headers", + "description": "Additional HTTP headers to include in requests" }, "max_screenshot_scrolling_times": { "anyOf": [ @@ -7313,7 +7383,8 @@ "type": "null" } ], - "title": "Max Screenshot Scrolling Times" + "title": "Max Screenshot Scrolling Times", + "description": "Maximum number of times to scroll for screenshots" }, "credential_type": { "type": "string", @@ -7323,14 +7394,16 @@ }, "credential_id": { "type": "string", - "title": "Credential Id" + "title": "Credential Id", + "description": "ID of the Skyvern credential to use for login." } }, "type": "object", "required": [ "credential_id" ], - "title": "SkyvernCredentialLoginRequest" + "title": "SkyvernLoginRequest", + "description": "Login with password saved in Skyvern" }, "TOTPCode": { "properties": { @@ -7684,7 +7757,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -7915,7 +7990,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -8119,7 +8196,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -8166,10 +8245,7 @@ ], "title": "Max Steps", "description": "\nMaximum number of steps the task can take. Task will fail if it exceeds this number. Cautions: you are charged per step so please set this number to a reasonable value. Contact sales@skyvern.com for custom pricing.\n", - "examples": [ - 10, - 25 - ] + "examples": [10, 25] }, "webhook_url": { "anyOf": [ @@ -8332,7 +8408,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -9088,7 +9166,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -9391,7 +9471,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -10444,7 +10526,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -10545,7 +10629,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { @@ -10761,7 +10847,9 @@ "type": "object" }, { - "items": {}, + "items": { + + }, "type": "array" }, { diff --git a/skyvern/client/__init__.py b/skyvern/client/__init__.py index c2011c2e..c2b9bcfa 100644 --- a/skyvern/client/__init__.py +++ b/skyvern/client/__init__.py @@ -22,6 +22,7 @@ from .types import ( BitwardenCreditCardDataParameterYaml, BitwardenLoginCredentialParameter, BitwardenLoginCredentialParameterYaml, + BitwardenLoginRequest, BitwardenSensitiveInformationParameter, BitwardenSensitiveInformationParameterYaml, BrowserSessionResponse, @@ -180,6 +181,10 @@ from .types import ( LoginBlockParametersItem_Output, LoginBlockParametersItem_Workflow, LoginBlockYaml, + LoginRequestBody, + LoginRequestBody_1Password, + LoginRequestBody_Bitwarden, + LoginRequestBody_Skyvern, NavigationBlock, NavigationBlockDataSchema, NavigationBlockParametersItem, @@ -197,6 +202,7 @@ from .types import ( NonEmptyPasswordCredential, OnePasswordCredentialParameter, OnePasswordCredentialParameterYaml, + OnePasswordLoginRequest, OutputParameter, OutputParameterYaml, PasswordCredentialResponse, @@ -207,6 +213,7 @@ from .types import ( RunStatus, SendEmailBlock, SendEmailBlockYaml, + SkyvernLoginRequest, TaskBlock, TaskBlockDataSchema, TaskBlockParametersItem, @@ -388,6 +395,7 @@ __all__ = [ "BitwardenCreditCardDataParameterYaml", "BitwardenLoginCredentialParameter", "BitwardenLoginCredentialParameterYaml", + "BitwardenLoginRequest", "BitwardenSensitiveInformationParameter", "BitwardenSensitiveInformationParameterYaml", "BrowserSessionResponse", @@ -547,6 +555,10 @@ __all__ = [ "LoginBlockParametersItem_Output", "LoginBlockParametersItem_Workflow", "LoginBlockYaml", + "LoginRequestBody", + "LoginRequestBody_1Password", + "LoginRequestBody_Bitwarden", + "LoginRequestBody_Skyvern", "NavigationBlock", "NavigationBlockDataSchema", "NavigationBlockParametersItem", @@ -565,6 +577,7 @@ __all__ = [ "NotFoundError", "OnePasswordCredentialParameter", "OnePasswordCredentialParameterYaml", + "OnePasswordLoginRequest", "OutputParameter", "OutputParameterYaml", "PasswordCredentialResponse", @@ -577,6 +590,7 @@ __all__ = [ "SendEmailBlockYaml", "Skyvern", "SkyvernEnvironment", + "SkyvernLoginRequest", "TaskBlock", "TaskBlockDataSchema", "TaskBlockParametersItem", diff --git a/skyvern/client/client.py b/skyvern/client/client.py index 234a7487..ae152bb1 100644 --- a/skyvern/client/client.py +++ b/skyvern/client/client.py @@ -30,6 +30,7 @@ from .types.totp_code import TotpCode from .types.credential_response import CredentialResponse from .types.credential_type import CredentialType from .types.create_credential_request_credential import CreateCredentialRequestCredential +from .types.login_request_body import LoginRequestBody from .core.client_wrapper import AsyncClientWrapper # this is used as the default value for optional parameters @@ -1145,7 +1146,7 @@ class Skyvern: self, *, timeout: typing.Optional[int] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> BrowserSessionResponse: """ - Create a new browser session + Create a browser session that persists across multiple runs Parameters ---------- @@ -1220,7 +1221,7 @@ class Skyvern: self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> typing.Optional[typing.Any]: """ - Close a browser session + Close a session. Once closed, the session cannot be used again. Parameters ---------- @@ -1290,7 +1291,7 @@ class Skyvern: self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> BrowserSessionResponse: """ - Get details about a specific browser session by ID + Get details about a specific browser session, including the browser address for cdp connection. Parameters ---------- @@ -1738,6 +1739,71 @@ class Skyvern: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def login( + self, *, request: LoginRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> WorkflowRunResponse: + """ + Log in to a website using either credential stored in Skyvern, Bitwarden or 1Password + + Parameters + ---------- + request : LoginRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WorkflowRunResponse + Successful Response + + Examples + -------- + from skyvern import LoginRequestBody_Skyvern, Skyvern + + client = Skyvern( + api_key="YOUR_API_KEY", + x_api_key="YOUR_X_API_KEY", + ) + client.login( + request=LoginRequestBody_Skyvern( + credential_id="credential_id", + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + "v1/run/tasks/login", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=LoginRequestBody, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + WorkflowRunResponse, + parse_obj_as( + type_=WorkflowRunResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncSkyvern: """ @@ -2946,7 +3012,7 @@ class AsyncSkyvern: self, *, timeout: typing.Optional[int] = OMIT, request_options: typing.Optional[RequestOptions] = None ) -> BrowserSessionResponse: """ - Create a new browser session + Create a browser session that persists across multiple runs Parameters ---------- @@ -3029,7 +3095,7 @@ class AsyncSkyvern: self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> typing.Optional[typing.Any]: """ - Close a browser session + Close a session. Once closed, the session cannot be used again. Parameters ---------- @@ -3107,7 +3173,7 @@ class AsyncSkyvern: self, browser_session_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> BrowserSessionResponse: """ - Get details about a specific browser session by ID + Get details about a specific browser session, including the browser address for cdp connection. Parameters ---------- @@ -3605,6 +3671,79 @@ class AsyncSkyvern: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def login( + self, *, request: LoginRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> WorkflowRunResponse: + """ + Log in to a website using either credential stored in Skyvern, Bitwarden or 1Password + + Parameters + ---------- + request : LoginRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WorkflowRunResponse + Successful Response + + Examples + -------- + import asyncio + + from skyvern import AsyncSkyvern, LoginRequestBody_Skyvern + + client = AsyncSkyvern( + api_key="YOUR_API_KEY", + x_api_key="YOUR_X_API_KEY", + ) + + + async def main() -> None: + await client.login( + request=LoginRequestBody_Skyvern( + credential_id="credential_id", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "v1/run/tasks/login", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=LoginRequestBody, direction="write" + ), + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + WorkflowRunResponse, + parse_obj_as( + type_=WorkflowRunResponse, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def _get_base_url(*, base_url: typing.Optional[str] = None, environment: SkyvernEnvironment) -> str: if base_url is not None: diff --git a/skyvern/client/core/client_wrapper.py b/skyvern/client/core/client_wrapper.py index 8428e93e..5681910b 100644 --- a/skyvern/client/core/client_wrapper.py +++ b/skyvern/client/core/client_wrapper.py @@ -24,7 +24,7 @@ class BaseClientWrapper: headers: typing.Dict[str, str] = { "X-Fern-Language": "Python", "X-Fern-SDK-Name": "skyvern", - "X-Fern-SDK-Version": "0.2.2", + "X-Fern-SDK-Version": "0.2.6", } if self._api_key is not None: headers["x-api-key"] = self._api_key diff --git a/skyvern/client/types/__init__.py b/skyvern/client/types/__init__.py index fced5d2f..e3948ac2 100644 --- a/skyvern/client/types/__init__.py +++ b/skyvern/client/types/__init__.py @@ -23,6 +23,7 @@ from .bitwarden_credit_card_data_parameter import BitwardenCreditCardDataParamet from .bitwarden_credit_card_data_parameter_yaml import BitwardenCreditCardDataParameterYaml from .bitwarden_login_credential_parameter import BitwardenLoginCredentialParameter from .bitwarden_login_credential_parameter_yaml import BitwardenLoginCredentialParameterYaml +from .bitwarden_login_request import BitwardenLoginRequest from .bitwarden_sensitive_information_parameter import BitwardenSensitiveInformationParameter from .bitwarden_sensitive_information_parameter_yaml import BitwardenSensitiveInformationParameterYaml from .browser_session_response import BrowserSessionResponse @@ -201,6 +202,12 @@ from .login_block_parameters_item import ( LoginBlockParametersItem_Workflow, ) from .login_block_yaml import LoginBlockYaml +from .login_request_body import ( + LoginRequestBody, + LoginRequestBody_1Password, + LoginRequestBody_Bitwarden, + LoginRequestBody_Skyvern, +) from .navigation_block import NavigationBlock from .navigation_block_data_schema import NavigationBlockDataSchema from .navigation_block_parameters_item import ( @@ -220,6 +227,7 @@ from .non_empty_credit_card_credential import NonEmptyCreditCardCredential from .non_empty_password_credential import NonEmptyPasswordCredential from .one_password_credential_parameter import OnePasswordCredentialParameter from .one_password_credential_parameter_yaml import OnePasswordCredentialParameterYaml +from .one_password_login_request import OnePasswordLoginRequest from .output_parameter import OutputParameter from .output_parameter_yaml import OutputParameterYaml from .password_credential_response import PasswordCredentialResponse @@ -230,6 +238,7 @@ from .run_engine import RunEngine from .run_status import RunStatus from .send_email_block import SendEmailBlock from .send_email_block_yaml import SendEmailBlockYaml +from .skyvern_login_request import SkyvernLoginRequest from .task_block import TaskBlock from .task_block_data_schema import TaskBlockDataSchema from .task_block_parameters_item import ( @@ -422,6 +431,7 @@ __all__ = [ "BitwardenCreditCardDataParameterYaml", "BitwardenLoginCredentialParameter", "BitwardenLoginCredentialParameterYaml", + "BitwardenLoginRequest", "BitwardenSensitiveInformationParameter", "BitwardenSensitiveInformationParameterYaml", "BrowserSessionResponse", @@ -580,6 +590,10 @@ __all__ = [ "LoginBlockParametersItem_Output", "LoginBlockParametersItem_Workflow", "LoginBlockYaml", + "LoginRequestBody", + "LoginRequestBody_1Password", + "LoginRequestBody_Bitwarden", + "LoginRequestBody_Skyvern", "NavigationBlock", "NavigationBlockDataSchema", "NavigationBlockParametersItem", @@ -597,6 +611,7 @@ __all__ = [ "NonEmptyPasswordCredential", "OnePasswordCredentialParameter", "OnePasswordCredentialParameterYaml", + "OnePasswordLoginRequest", "OutputParameter", "OutputParameterYaml", "PasswordCredentialResponse", @@ -607,6 +622,7 @@ __all__ = [ "RunStatus", "SendEmailBlock", "SendEmailBlockYaml", + "SkyvernLoginRequest", "TaskBlock", "TaskBlockDataSchema", "TaskBlockParametersItem", diff --git a/skyvern/client/types/bitwarden_login_request.py b/skyvern/client/types/bitwarden_login_request.py new file mode 100644 index 00000000..ec01295d --- /dev/null +++ b/skyvern/client/types/bitwarden_login_request.py @@ -0,0 +1,77 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +import pydantic +from .proxy_location import ProxyLocation +from ..core.pydantic_utilities import IS_PYDANTIC_V2 + + +class BitwardenLoginRequest(UniversalBaseModel): + """ + Login with password saved in Bitwarden + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Website url + """ + + prompt: typing.Optional[str] = pydantic.Field(default=None) + """ + Login instructions. Skyvern has default prompt/instruction for login if this field is not provided. + """ + + webhook_url: typing.Optional[str] = pydantic.Field(default=None) + """ + Webhook URL to send login status updates + """ + + proxy_location: typing.Optional[ProxyLocation] = pydantic.Field(default=None) + """ + Proxy location to use + """ + + totp_identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + Identifier for TOTP (Time-based One-Time Password) if required + """ + + totp_url: typing.Optional[str] = pydantic.Field(default=None) + """ + TOTP URL to fetch one-time passwords + """ + + browser_session_id: typing.Optional[str] = pydantic.Field(default=None) + """ + ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456` + """ + + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = pydantic.Field(default=None) + """ + Additional HTTP headers to include in requests + """ + + max_screenshot_scrolling_times: typing.Optional[int] = pydantic.Field(default=None) + """ + Maximum number of times to scroll for screenshots + """ + + bitwarden_collection_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Bitwarden collection ID + """ + + bitwarden_item_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Bitwarden item ID + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/skyvern/client/types/login_request_body.py b/skyvern/client/types/login_request_body.py new file mode 100644 index 00000000..66883b33 --- /dev/null +++ b/skyvern/client/types/login_request_body.py @@ -0,0 +1,82 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations +from ..core.pydantic_utilities import UniversalBaseModel +import typing +from .proxy_location import ProxyLocation +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +import pydantic + + +class LoginRequestBody_Skyvern(UniversalBaseModel): + credential_type: typing.Literal["skyvern"] = "skyvern" + url: typing.Optional[str] = None + prompt: typing.Optional[str] = None + webhook_url: typing.Optional[str] = None + proxy_location: typing.Optional[ProxyLocation] = None + totp_identifier: typing.Optional[str] = None + totp_url: typing.Optional[str] = None + browser_session_id: typing.Optional[str] = None + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None + max_screenshot_scrolling_times: typing.Optional[int] = None + credential_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class LoginRequestBody_Bitwarden(UniversalBaseModel): + credential_type: typing.Literal["bitwarden"] = "bitwarden" + url: typing.Optional[str] = None + prompt: typing.Optional[str] = None + webhook_url: typing.Optional[str] = None + proxy_location: typing.Optional[ProxyLocation] = None + totp_identifier: typing.Optional[str] = None + totp_url: typing.Optional[str] = None + browser_session_id: typing.Optional[str] = None + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None + max_screenshot_scrolling_times: typing.Optional[int] = None + bitwarden_collection_id: typing.Optional[str] = None + bitwarden_item_id: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class LoginRequestBody_1Password(UniversalBaseModel): + credential_type: typing.Literal["1password"] = "1password" + url: typing.Optional[str] = None + prompt: typing.Optional[str] = None + webhook_url: typing.Optional[str] = None + proxy_location: typing.Optional[ProxyLocation] = None + totp_identifier: typing.Optional[str] = None + totp_url: typing.Optional[str] = None + browser_session_id: typing.Optional[str] = None + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = None + max_screenshot_scrolling_times: typing.Optional[int] = None + onepassword_vault_id: str + onepassword_item_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +LoginRequestBody = typing.Union[LoginRequestBody_Skyvern, LoginRequestBody_Bitwarden, LoginRequestBody_1Password] diff --git a/skyvern/client/types/one_password_login_request.py b/skyvern/client/types/one_password_login_request.py new file mode 100644 index 00000000..be038697 --- /dev/null +++ b/skyvern/client/types/one_password_login_request.py @@ -0,0 +1,77 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +import pydantic +from .proxy_location import ProxyLocation +from ..core.pydantic_utilities import IS_PYDANTIC_V2 + + +class OnePasswordLoginRequest(UniversalBaseModel): + """ + Login with password saved in 1Password + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Website url + """ + + prompt: typing.Optional[str] = pydantic.Field(default=None) + """ + Login instructions. Skyvern has default prompt/instruction for login if this field is not provided. + """ + + webhook_url: typing.Optional[str] = pydantic.Field(default=None) + """ + Webhook URL to send login status updates + """ + + proxy_location: typing.Optional[ProxyLocation] = pydantic.Field(default=None) + """ + Proxy location to use + """ + + totp_identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + Identifier for TOTP (Time-based One-Time Password) if required + """ + + totp_url: typing.Optional[str] = pydantic.Field(default=None) + """ + TOTP URL to fetch one-time passwords + """ + + browser_session_id: typing.Optional[str] = pydantic.Field(default=None) + """ + ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456` + """ + + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = pydantic.Field(default=None) + """ + Additional HTTP headers to include in requests + """ + + max_screenshot_scrolling_times: typing.Optional[int] = pydantic.Field(default=None) + """ + Maximum number of times to scroll for screenshots + """ + + onepassword_vault_id: str = pydantic.Field() + """ + 1Password vault ID. + """ + + onepassword_item_id: str = pydantic.Field() + """ + 1Password item ID. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/skyvern/client/types/skyvern_login_request.py b/skyvern/client/types/skyvern_login_request.py new file mode 100644 index 00000000..5d2d6d63 --- /dev/null +++ b/skyvern/client/types/skyvern_login_request.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.pydantic_utilities import UniversalBaseModel +import typing +import pydantic +from .proxy_location import ProxyLocation +from ..core.pydantic_utilities import IS_PYDANTIC_V2 + + +class SkyvernLoginRequest(UniversalBaseModel): + """ + Login with password saved in Skyvern + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Website url + """ + + prompt: typing.Optional[str] = pydantic.Field(default=None) + """ + Login instructions. Skyvern has default prompt/instruction for login if this field is not provided. + """ + + webhook_url: typing.Optional[str] = pydantic.Field(default=None) + """ + Webhook URL to send login status updates + """ + + proxy_location: typing.Optional[ProxyLocation] = pydantic.Field(default=None) + """ + Proxy location to use + """ + + totp_identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + Identifier for TOTP (Time-based One-Time Password) if required + """ + + totp_url: typing.Optional[str] = pydantic.Field(default=None) + """ + TOTP URL to fetch one-time passwords + """ + + browser_session_id: typing.Optional[str] = pydantic.Field(default=None) + """ + ID of the browser session to use, which is prefixed by `pbs_` e.g. `pbs_123456` + """ + + extra_http_headers: typing.Optional[typing.Dict[str, typing.Optional[str]]] = pydantic.Field(default=None) + """ + Additional HTTP headers to include in requests + """ + + max_screenshot_scrolling_times: typing.Optional[int] = pydantic.Field(default=None) + """ + Maximum number of times to scroll for screenshots + """ + + credential_id: str = pydantic.Field() + """ + ID of the Skyvern credential to use for login. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/skyvern/forge/sdk/routes/code_samples.py b/skyvern/forge/sdk/routes/code_samples.py index f8c5fdbd..b1dedb09 100644 --- a/skyvern/forge/sdk/routes/code_samples.py +++ b/skyvern/forge/sdk/routes/code_samples.py @@ -29,23 +29,40 @@ LOGIN_CODE_SAMPLE_SKYVERN = """# Login with password saved in Skyvern from skyvern import Skyvern skyvern = Skyvern(api_key="YOUR_API_KEY") -await skyvern.login(credential_id="cred_123") +await skyvern.login( + url="https://example.com", + credential_type="skyvern", + credential_id="cred_123"), +) """ LOGIN_CODE_SAMPLE_BITWARDEN = """# Login with password saved in Bitwarden from skyvern import Skyvern skyvern = Skyvern(api_key="YOUR_API_KEY") # Login with a Bitwarden collection and website url filter -await skyvern.login(bitwarden_collection_id="BITWARDEN COLLECTION ID", url_filter="https://example.com") +await skyvern.login( + url="https://example.com", + credential_type="bitwarden", + bitwarden_collection_id="BITWARDEN COLLECTION ID", +) # Login with a Bitwarden item -await skyvern.login(bitwarden_item_id="BITWARDEN ITEM ID") +await skyvern.login( + url="https://example.com", + credential_type="bitwarden", + bitwarden_item_id="BITWARDEN ITEM ID", +) """ LOGIN_CODE_SAMPLE_ONEPASSWORD = """# Login with password saved in 1Password from skyvern import Skyvern skyvern = Skyvern(api_key="YOUR_API_KEY") -await skyvern.login(onepassword_vault_id="ONEPASSWORD VAULT ID", onepassword_item_id="ONEPASSWORD ITEM ID") +await skyvern.login( + url="https://example.com", + credential_type="onepassword", + onepassword_vault_id="1PASSWORD VAULT ID", + onepassword_item_id="1PASSWORD ITEM ID", +) """ # Workflows diff --git a/skyvern/library/skyvern.py b/skyvern/library/skyvern.py index d530f696..7c712e59 100644 --- a/skyvern/library/skyvern.py +++ b/skyvern/library/skyvern.py @@ -24,6 +24,7 @@ from skyvern.forge.sdk.schemas.tasks import CreateTaskResponse, Task, TaskReques from skyvern.forge.sdk.services.org_auth_token_service import API_KEY_LIFETIME from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus from skyvern.library.constants import DEFAULT_AGENT_HEARTBEAT_INTERVAL, DEFAULT_AGENT_TIMEOUT +from skyvern.schemas.run_blocks import CredentialType from skyvern.schemas.runs import CUA_ENGINES, ProxyLocation, RunEngine, RunStatus, RunType from skyvern.services import run_service, task_v1_service, task_v2_service from skyvern.utils import migrate_db @@ -451,6 +452,26 @@ class Skyvern(AsyncSkyvern): await asyncio.sleep(DEFAULT_AGENT_HEARTBEAT_INTERVAL) return WorkflowRunResponse.model_validate(workflow_run.model_dump()) + async def login( + self, + credential_type: CredentialType, + *, + url: str | None = None, + credential_id: str | None = None, + bitwarden_collection_id: str | None = None, + bitwarden_item_id: str | None = None, + onepassword_vault_id: str | None = None, + onepassword_item_id: str | None = None, + prompt: str | None = None, + webhook_url: str | None = None, + proxy_location: ProxyLocation | None = None, + totp_identifier: str | None = None, + totp_url: str | None = None, + browser_session_id: str | None = None, + extra_http_headers: dict[str, str] | None = None, + ) -> None: + return + def from_run_to_task_run_response(run_obj: GetRunResponse) -> TaskRunResponse: return TaskRunResponse.model_validate(run_obj.model_dump())