mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-07-09 16:09:13 +00:00
v1.0.19: make env vars should always overrides api keys stored in the streamlit mount in skyvern image (#4824)
Some checks are pending
Auto Create GitHub Release on Version Change / check-version-change (push) Waiting to run
Auto Create GitHub Release on Version Change / create-release (push) Blocked by required conditions
Run tests and pre-commit / Run tests and pre-commit hooks (push) Waiting to run
Run tests and pre-commit / Frontend Lint and Build (push) Waiting to run
Publish Fern Docs / run (push) Waiting to run
Build Skyvern SDK and publish to PyPI / check-version-change (push) Waiting to run
Build Skyvern SDK and publish to PyPI / run-ci (push) Blocked by required conditions
Build Skyvern SDK and publish to PyPI / build-sdk (push) Blocked by required conditions
Build Skyvern TS SDK and publish to npm / check-version-change (push) Waiting to run
Build Skyvern TS SDK and publish to npm / build-and-publish-sdk (push) Blocked by required conditions
Some checks are pending
Auto Create GitHub Release on Version Change / check-version-change (push) Waiting to run
Auto Create GitHub Release on Version Change / create-release (push) Blocked by required conditions
Run tests and pre-commit / Run tests and pre-commit hooks (push) Waiting to run
Run tests and pre-commit / Frontend Lint and Build (push) Waiting to run
Publish Fern Docs / run (push) Waiting to run
Build Skyvern SDK and publish to PyPI / check-version-change (push) Waiting to run
Build Skyvern SDK and publish to PyPI / run-ci (push) Blocked by required conditions
Build Skyvern SDK and publish to PyPI / build-sdk (push) Blocked by required conditions
Build Skyvern TS SDK and publish to npm / check-version-change (push) Waiting to run
Build Skyvern TS SDK and publish to npm / build-and-publish-sdk (push) Blocked by required conditions
This commit is contained in:
parent
34bb166d4d
commit
b56d724ed8
22 changed files with 454 additions and 19 deletions
|
|
@ -1,13 +1,23 @@
|
|||
FROM node:20.12-slim
|
||||
|
||||
# Install tini for proper signal handling and zombie reaping
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends tini && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY ./skyvern-frontend /app
|
||||
COPY ./entrypoint-skyvernui.sh /app/entrypoint-skyvernui.sh
|
||||
RUN npm install
|
||||
|
||||
ENV VITE_API_BASE_URL=http://localhost:8000/api/v1
|
||||
ENV VITE_WSS_BASE_URL=ws://localhost:8000/api/v1
|
||||
ENV VITE_ARTIFACT_API_BASE_URL=http://localhost:9090
|
||||
# Placeholders for runtime injection (will be replaced by entrypoint script)
|
||||
ENV VITE_API_BASE_URL=__VITE_API_BASE_URL_PLACEHOLDER__
|
||||
ENV VITE_WSS_BASE_URL=__VITE_WSS_BASE_URL_PLACEHOLDER__
|
||||
ENV VITE_ARTIFACT_API_BASE_URL=__VITE_ARTIFACT_API_BASE_URL_PLACEHOLDER__
|
||||
ENV VITE_SKYVERN_API_KEY=__SKYVERN_API_KEY_PLACEHOLDER__
|
||||
|
||||
CMD [ "/bin/bash", "/app/entrypoint-skyvernui.sh" ]
|
||||
# Build at image time
|
||||
RUN npm run build
|
||||
|
||||
# Use tini as init for proper signal handling and zombie reaping
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["/bin/bash", "/app/entrypoint-skyvernui.sh"]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,40 @@
|
|||
|
||||
set -e
|
||||
|
||||
# setting api key
|
||||
VITE_SKYVERN_API_KEY=$(sed -n 's/.*cred\s*=\s*"\([^"]*\)".*/\1/p' .streamlit/secrets.toml)
|
||||
export VITE_SKYVERN_API_KEY
|
||||
npm run start
|
||||
# Default values for environment variables
|
||||
VITE_API_BASE_URL="${VITE_API_BASE_URL:-http://localhost:8000/api/v1}"
|
||||
VITE_WSS_BASE_URL="${VITE_WSS_BASE_URL:-ws://localhost:8000/api/v1}"
|
||||
VITE_ARTIFACT_API_BASE_URL="${VITE_ARTIFACT_API_BASE_URL:-http://localhost:9090}"
|
||||
|
||||
# Priority for VITE_SKYVERN_API_KEY:
|
||||
# 1. Environment variable (from .env file or docker-compose environment)
|
||||
# 2. Fallback to .streamlit/secrets.toml
|
||||
if [ -n "$VITE_SKYVERN_API_KEY" ]; then
|
||||
# Trim whitespace and validate
|
||||
VITE_SKYVERN_API_KEY=$(echo "$VITE_SKYVERN_API_KEY" | xargs)
|
||||
echo "Using VITE_SKYVERN_API_KEY from environment variable"
|
||||
else
|
||||
# Fallback: Extract API key from secrets file
|
||||
VITE_SKYVERN_API_KEY=$(sed -n 's/.*cred\s*=\s*"\([^"]*\)".*/\1/p' .streamlit/secrets.toml 2>/dev/null || echo "")
|
||||
if [ -n "$VITE_SKYVERN_API_KEY" ]; then
|
||||
echo "Using VITE_SKYVERN_API_KEY from .streamlit/secrets.toml"
|
||||
else
|
||||
echo "WARNING: No VITE_SKYVERN_API_KEY found in environment or .streamlit/secrets.toml"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Inject environment variables into pre-built JS files (replace placeholders)
|
||||
# Using | as delimiter since URLs contain /
|
||||
find /app/dist -name "*.js" -exec sed -i \
|
||||
-e "s|__VITE_API_BASE_URL_PLACEHOLDER__|${VITE_API_BASE_URL}|g" \
|
||||
-e "s|__VITE_WSS_BASE_URL_PLACEHOLDER__|${VITE_WSS_BASE_URL}|g" \
|
||||
-e "s|__VITE_ARTIFACT_API_BASE_URL_PLACEHOLDER__|${VITE_ARTIFACT_API_BASE_URL}|g" \
|
||||
-e "s|__SKYVERN_API_KEY_PLACEHOLDER__|${VITE_SKYVERN_API_KEY}|g" \
|
||||
{} \;
|
||||
|
||||
# Start the servers (no rebuild needed)
|
||||
# Tini (configured as ENTRYPOINT) handles signal forwarding and zombie reaping
|
||||
node localServer.js &
|
||||
node artifactServer.js &
|
||||
wait
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "skyvern"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
description = ""
|
||||
authors = [{ name = "Skyvern AI", email = "info@skyvern.com" }]
|
||||
requires-python = ">=3.11,<3.14"
|
||||
|
|
|
|||
4
skyvern-ts/client/package-lock.json
generated
4
skyvern-ts/client/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@skyvern/client",
|
||||
"version": "1.0.18",
|
||||
"version": "1.0.19",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@skyvern/client",
|
||||
"version": "1.0.18",
|
||||
"version": "1.0.19",
|
||||
"dependencies": {
|
||||
"playwright": "^1.48.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@skyvern/client",
|
||||
"version": "1.0.18",
|
||||
"version": "1.0.19",
|
||||
"private": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ export class SkyvernClient {
|
|||
"x-api-key": _options?.apiKey,
|
||||
"X-Fern-Language": "JavaScript",
|
||||
"X-Fern-SDK-Name": "@skyvern/client",
|
||||
"X-Fern-SDK-Version": "1.0.18",
|
||||
"User-Agent": "@skyvern/client/1.0.18",
|
||||
"X-Fern-SDK-Version": "1.0.19",
|
||||
"User-Agent": "@skyvern/client/1.0.19",
|
||||
"X-Fern-Runtime": core.RUNTIME.type,
|
||||
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
||||
},
|
||||
|
|
@ -3139,6 +3139,83 @@ export class SkyvernClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.
|
||||
*
|
||||
* @param {string} workflowPermanentId - The workflow permanent ID to clear cache for
|
||||
* @param {SkyvernClient.RequestOptions} requestOptions - Request-specific configuration.
|
||||
*
|
||||
* @throws {@link Skyvern.UnprocessableEntityError}
|
||||
*
|
||||
* @example
|
||||
* await client.clearWorkflowCache("wpid_abc123")
|
||||
*/
|
||||
public clearWorkflowCache(
|
||||
workflowPermanentId: string,
|
||||
requestOptions?: SkyvernClient.RequestOptions,
|
||||
): core.HttpResponsePromise<Skyvern.ClearCacheResponse> {
|
||||
return core.HttpResponsePromise.fromPromise(this.__clearWorkflowCache(workflowPermanentId, requestOptions));
|
||||
}
|
||||
|
||||
private async __clearWorkflowCache(
|
||||
workflowPermanentId: string,
|
||||
requestOptions?: SkyvernClient.RequestOptions,
|
||||
): Promise<core.WithRawResponse<Skyvern.ClearCacheResponse>> {
|
||||
const _headers: core.Fetcher.Args["headers"] = mergeHeaders(
|
||||
this._options?.headers,
|
||||
mergeOnlyDefinedHeaders({ "x-api-key": requestOptions?.apiKey ?? this._options?.apiKey }),
|
||||
requestOptions?.headers,
|
||||
);
|
||||
const _response = await core.fetcher({
|
||||
url: core.url.join(
|
||||
(await core.Supplier.get(this._options.baseUrl)) ??
|
||||
(await core.Supplier.get(this._options.environment)) ??
|
||||
environments.SkyvernEnvironment.Cloud,
|
||||
`v1/scripts/${core.url.encodePathParam(workflowPermanentId)}/cache`,
|
||||
),
|
||||
method: "DELETE",
|
||||
headers: _headers,
|
||||
queryParameters: requestOptions?.queryParams,
|
||||
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000,
|
||||
maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
|
||||
abortSignal: requestOptions?.abortSignal,
|
||||
});
|
||||
if (_response.ok) {
|
||||
return { data: _response.body as Skyvern.ClearCacheResponse, rawResponse: _response.rawResponse };
|
||||
}
|
||||
|
||||
if (_response.error.reason === "status-code") {
|
||||
switch (_response.error.statusCode) {
|
||||
case 422:
|
||||
throw new Skyvern.UnprocessableEntityError(_response.error.body as unknown, _response.rawResponse);
|
||||
default:
|
||||
throw new errors.SkyvernError({
|
||||
statusCode: _response.error.statusCode,
|
||||
body: _response.error.body,
|
||||
rawResponse: _response.rawResponse,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
switch (_response.error.reason) {
|
||||
case "non-json":
|
||||
throw new errors.SkyvernError({
|
||||
statusCode: _response.error.statusCode,
|
||||
body: _response.error.rawBody,
|
||||
rawResponse: _response.rawResponse,
|
||||
});
|
||||
case "timeout":
|
||||
throw new errors.SkyvernTimeoutError(
|
||||
"Timeout exceeded when calling DELETE /v1/scripts/{workflow_permanent_id}/cache.",
|
||||
);
|
||||
case "unknown":
|
||||
throw new errors.SkyvernError({
|
||||
message: _response.error.errorMessage,
|
||||
rawResponse: _response.rawResponse,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single SDK action with the specified parameters
|
||||
*
|
||||
|
|
|
|||
11
skyvern-ts/client/src/api/types/ClearCacheResponse.ts
Normal file
11
skyvern-ts/client/src/api/types/ClearCacheResponse.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// This file was auto-generated by Fern from our API Definition.
|
||||
|
||||
/**
|
||||
* Response model for cache clearing operations.
|
||||
*/
|
||||
export interface ClearCacheResponse {
|
||||
/** Number of cached entries deleted */
|
||||
deleted_count: number;
|
||||
/** Status message */
|
||||
message: string;
|
||||
}
|
||||
|
|
@ -14,6 +14,10 @@ export interface CredentialResponse {
|
|||
credential_type: Skyvern.CredentialTypeOutput;
|
||||
/** Name of the credential */
|
||||
name: string;
|
||||
/** Browser profile ID linked to this credential */
|
||||
browser_profile_id?: string;
|
||||
/** Login page URL used during the credential test */
|
||||
tested_url?: string;
|
||||
}
|
||||
|
||||
export namespace CredentialResponse {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ export interface WorkflowRun {
|
|||
sequential_key?: string;
|
||||
ai_fallback?: boolean;
|
||||
code_gen?: boolean;
|
||||
waiting_for_verification_code?: boolean;
|
||||
verification_code_identifier?: string;
|
||||
verification_code_polling_started_at?: string;
|
||||
queued_at?: string;
|
||||
started_at?: string;
|
||||
finished_at?: string;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export * from "./BrowserProfile.js";
|
|||
export * from "./BrowserSessionResponse.js";
|
||||
export * from "./ChangeTierResponse.js";
|
||||
export * from "./CheckoutSessionResponse.js";
|
||||
export * from "./ClearCacheResponse.js";
|
||||
export * from "./ClickAction.js";
|
||||
export * from "./ClickContext.js";
|
||||
export * from "./CodeBlock.js";
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export const SDK_VERSION = "1.0.18";
|
||||
export const SDK_VERSION = "1.0.19";
|
||||
|
|
|
|||
|
|
@ -1484,6 +1484,9 @@ describe("SkyvernClient", () => {
|
|||
sequential_key: "sequential_key",
|
||||
ai_fallback: true,
|
||||
code_gen: true,
|
||||
waiting_for_verification_code: true,
|
||||
verification_code_identifier: "verification_code_identifier",
|
||||
verification_code_polling_started_at: "2024-01-15T09:30:00Z",
|
||||
queued_at: "2024-01-15T09:30:00Z",
|
||||
started_at: "2024-01-15T09:30:00Z",
|
||||
finished_at: "2024-01-15T09:30:00Z",
|
||||
|
|
@ -1531,6 +1534,9 @@ describe("SkyvernClient", () => {
|
|||
sequential_key: "sequential_key",
|
||||
ai_fallback: true,
|
||||
code_gen: true,
|
||||
waiting_for_verification_code: true,
|
||||
verification_code_identifier: "verification_code_identifier",
|
||||
verification_code_polling_started_at: "2024-01-15T09:30:00Z",
|
||||
queued_at: "2024-01-15T09:30:00Z",
|
||||
started_at: "2024-01-15T09:30:00Z",
|
||||
finished_at: "2024-01-15T09:30:00Z",
|
||||
|
|
@ -2627,6 +2633,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
},
|
||||
];
|
||||
server.mockEndpoint().get("/v1/credentials").respondWith().statusCode(200).jsonBody(rawResponseBody).build();
|
||||
|
|
@ -2645,6 +2653,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
@ -2678,6 +2688,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
};
|
||||
server
|
||||
.mockEndpoint()
|
||||
|
|
@ -2706,6 +2718,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -2756,6 +2770,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
};
|
||||
server
|
||||
.mockEndpoint()
|
||||
|
|
@ -2783,6 +2799,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -2857,6 +2875,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
};
|
||||
server
|
||||
.mockEndpoint()
|
||||
|
|
@ -2876,6 +2896,8 @@ describe("SkyvernClient", () => {
|
|||
},
|
||||
credential_type: "password",
|
||||
name: "Amazon Login",
|
||||
browser_profile_id: "browser_profile_id",
|
||||
tested_url: "tested_url",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -3435,6 +3457,44 @@ describe("SkyvernClient", () => {
|
|||
}).rejects.toThrow(Skyvern.UnprocessableEntityError);
|
||||
});
|
||||
|
||||
test("clear_workflow_cache (1)", async () => {
|
||||
const server = mockServerPool.createServer();
|
||||
const client = new SkyvernClient({ apiKey: "test", environment: server.baseUrl });
|
||||
|
||||
const rawResponseBody = { deleted_count: 1, message: "message" };
|
||||
server
|
||||
.mockEndpoint()
|
||||
.delete("/v1/scripts/wpid_abc123/cache")
|
||||
.respondWith()
|
||||
.statusCode(200)
|
||||
.jsonBody(rawResponseBody)
|
||||
.build();
|
||||
|
||||
const response = await client.clearWorkflowCache("wpid_abc123");
|
||||
expect(response).toEqual({
|
||||
deleted_count: 1,
|
||||
message: "message",
|
||||
});
|
||||
});
|
||||
|
||||
test("clear_workflow_cache (2)", async () => {
|
||||
const server = mockServerPool.createServer();
|
||||
const client = new SkyvernClient({ apiKey: "test", environment: server.baseUrl });
|
||||
|
||||
const rawResponseBody = { key: "value" };
|
||||
server
|
||||
.mockEndpoint()
|
||||
.delete("/v1/scripts/workflow_permanent_id/cache")
|
||||
.respondWith()
|
||||
.statusCode(422)
|
||||
.jsonBody(rawResponseBody)
|
||||
.build();
|
||||
|
||||
await expect(async () => {
|
||||
return await client.clearWorkflowCache("workflow_permanent_id");
|
||||
}).rejects.toThrow(Skyvern.UnprocessableEntityError);
|
||||
});
|
||||
|
||||
test("run_sdk_action (1)", async () => {
|
||||
const server = mockServerPool.createServer();
|
||||
const client = new SkyvernClient({ apiKey: "test", environment: server.baseUrl });
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ if typing.TYPE_CHECKING:
|
|||
BrowserSessionResponse,
|
||||
ChangeTierResponse,
|
||||
CheckoutSessionResponse,
|
||||
ClearCacheResponse,
|
||||
ClickAction,
|
||||
ClickActionData,
|
||||
ClickContext,
|
||||
|
|
@ -581,6 +582,7 @@ _dynamic_imports: typing.Dict[str, str] = {
|
|||
"BrowserSessionResponse": ".types",
|
||||
"ChangeTierResponse": ".types",
|
||||
"CheckoutSessionResponse": ".types",
|
||||
"ClearCacheResponse": ".types",
|
||||
"ClickAction": ".types",
|
||||
"ClickActionData": ".types",
|
||||
"ClickContext": ".types",
|
||||
|
|
@ -1135,6 +1137,7 @@ __all__ = [
|
|||
"BrowserSessionResponse",
|
||||
"ChangeTierResponse",
|
||||
"CheckoutSessionResponse",
|
||||
"ClearCacheResponse",
|
||||
"ClickAction",
|
||||
"ClickActionData",
|
||||
"ClickContext",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from .types.browser_profile import BrowserProfile
|
|||
from .types.browser_session_response import BrowserSessionResponse
|
||||
from .types.change_tier_response import ChangeTierResponse
|
||||
from .types.checkout_session_response import CheckoutSessionResponse
|
||||
from .types.clear_cache_response import ClearCacheResponse
|
||||
from .types.create_credential_request_credential import CreateCredentialRequestCredential
|
||||
from .types.create_script_response import CreateScriptResponse
|
||||
from .types.credential_response import CredentialResponse
|
||||
|
|
@ -2101,6 +2102,39 @@ class Skyvern:
|
|||
_response = self._raw_client.deploy_script(script_id, files=files, request_options=request_options)
|
||||
return _response.data
|
||||
|
||||
def clear_workflow_cache(
|
||||
self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
|
||||
) -> ClearCacheResponse:
|
||||
"""
|
||||
Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
workflow_permanent_id : str
|
||||
The workflow permanent ID to clear cache for
|
||||
|
||||
request_options : typing.Optional[RequestOptions]
|
||||
Request-specific configuration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ClearCacheResponse
|
||||
Successful Response
|
||||
|
||||
Examples
|
||||
--------
|
||||
from skyvern import Skyvern
|
||||
|
||||
client = Skyvern(
|
||||
api_key="YOUR_API_KEY",
|
||||
)
|
||||
client.clear_workflow_cache(
|
||||
workflow_permanent_id="wpid_abc123",
|
||||
)
|
||||
"""
|
||||
_response = self._raw_client.clear_workflow_cache(workflow_permanent_id, request_options=request_options)
|
||||
return _response.data
|
||||
|
||||
def run_sdk_action(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -4647,6 +4681,47 @@ class AsyncSkyvern:
|
|||
_response = await self._raw_client.deploy_script(script_id, files=files, request_options=request_options)
|
||||
return _response.data
|
||||
|
||||
async def clear_workflow_cache(
|
||||
self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
|
||||
) -> ClearCacheResponse:
|
||||
"""
|
||||
Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
workflow_permanent_id : str
|
||||
The workflow permanent ID to clear cache for
|
||||
|
||||
request_options : typing.Optional[RequestOptions]
|
||||
Request-specific configuration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ClearCacheResponse
|
||||
Successful Response
|
||||
|
||||
Examples
|
||||
--------
|
||||
import asyncio
|
||||
|
||||
from skyvern import AsyncSkyvern
|
||||
|
||||
client = AsyncSkyvern(
|
||||
api_key="YOUR_API_KEY",
|
||||
)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
await client.clear_workflow_cache(
|
||||
workflow_permanent_id="wpid_abc123",
|
||||
)
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
"""
|
||||
_response = await self._raw_client.clear_workflow_cache(workflow_permanent_id, request_options=request_options)
|
||||
return _response.data
|
||||
|
||||
async def run_sdk_action(
|
||||
self,
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ class BaseClientWrapper:
|
|||
|
||||
def get_headers(self) -> typing.Dict[str, str]:
|
||||
headers: typing.Dict[str, str] = {
|
||||
"User-Agent": "skyvern/1.0.18",
|
||||
"User-Agent": "skyvern/1.0.19",
|
||||
"X-Fern-Language": "Python",
|
||||
"X-Fern-SDK-Name": "skyvern",
|
||||
"X-Fern-SDK-Version": "1.0.18",
|
||||
"X-Fern-SDK-Version": "1.0.19",
|
||||
**(self.get_custom_headers() or {}),
|
||||
}
|
||||
if self._api_key is not None:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from .types.browser_profile import BrowserProfile
|
|||
from .types.browser_session_response import BrowserSessionResponse
|
||||
from .types.change_tier_response import ChangeTierResponse
|
||||
from .types.checkout_session_response import CheckoutSessionResponse
|
||||
from .types.clear_cache_response import ClearCacheResponse
|
||||
from .types.create_credential_request_credential import CreateCredentialRequestCredential
|
||||
from .types.create_script_response import CreateScriptResponse
|
||||
from .types.credential_response import CredentialResponse
|
||||
|
|
@ -2884,6 +2885,56 @@ class RawSkyvern:
|
|||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
||||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
||||
|
||||
def clear_workflow_cache(
|
||||
self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
|
||||
) -> HttpResponse[ClearCacheResponse]:
|
||||
"""
|
||||
Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
workflow_permanent_id : str
|
||||
The workflow permanent ID to clear cache for
|
||||
|
||||
request_options : typing.Optional[RequestOptions]
|
||||
Request-specific configuration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
HttpResponse[ClearCacheResponse]
|
||||
Successful Response
|
||||
"""
|
||||
_response = self._client_wrapper.httpx_client.request(
|
||||
f"v1/scripts/{jsonable_encoder(workflow_permanent_id)}/cache",
|
||||
method="DELETE",
|
||||
request_options=request_options,
|
||||
)
|
||||
try:
|
||||
if 200 <= _response.status_code < 300:
|
||||
_data = typing.cast(
|
||||
ClearCacheResponse,
|
||||
parse_obj_as(
|
||||
type_=ClearCacheResponse, # type: ignore
|
||||
object_=_response.json(),
|
||||
),
|
||||
)
|
||||
return HttpResponse(response=_response, data=_data)
|
||||
if _response.status_code == 422:
|
||||
raise UnprocessableEntityError(
|
||||
headers=dict(_response.headers),
|
||||
body=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, headers=dict(_response.headers), body=_response.text)
|
||||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
||||
|
||||
def run_sdk_action(
|
||||
self,
|
||||
*,
|
||||
|
|
@ -6013,6 +6064,56 @@ class AsyncRawSkyvern:
|
|||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
|
||||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
||||
|
||||
async def clear_workflow_cache(
|
||||
self, workflow_permanent_id: str, *, request_options: typing.Optional[RequestOptions] = None
|
||||
) -> AsyncHttpResponse[ClearCacheResponse]:
|
||||
"""
|
||||
Clear all cached scripts for a specific workflow. This will trigger script regeneration on subsequent runs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
workflow_permanent_id : str
|
||||
The workflow permanent ID to clear cache for
|
||||
|
||||
request_options : typing.Optional[RequestOptions]
|
||||
Request-specific configuration.
|
||||
|
||||
Returns
|
||||
-------
|
||||
AsyncHttpResponse[ClearCacheResponse]
|
||||
Successful Response
|
||||
"""
|
||||
_response = await self._client_wrapper.httpx_client.request(
|
||||
f"v1/scripts/{jsonable_encoder(workflow_permanent_id)}/cache",
|
||||
method="DELETE",
|
||||
request_options=request_options,
|
||||
)
|
||||
try:
|
||||
if 200 <= _response.status_code < 300:
|
||||
_data = typing.cast(
|
||||
ClearCacheResponse,
|
||||
parse_obj_as(
|
||||
type_=ClearCacheResponse, # type: ignore
|
||||
object_=_response.json(),
|
||||
),
|
||||
)
|
||||
return AsyncHttpResponse(response=_response, data=_data)
|
||||
if _response.status_code == 422:
|
||||
raise UnprocessableEntityError(
|
||||
headers=dict(_response.headers),
|
||||
body=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, headers=dict(_response.headers), body=_response.text)
|
||||
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
|
||||
|
||||
async def run_sdk_action(
|
||||
self,
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ if typing.TYPE_CHECKING:
|
|||
from .browser_session_response import BrowserSessionResponse
|
||||
from .change_tier_response import ChangeTierResponse
|
||||
from .checkout_session_response import CheckoutSessionResponse
|
||||
from .clear_cache_response import ClearCacheResponse
|
||||
from .click_action import ClickAction
|
||||
from .click_action_data import ClickActionData
|
||||
from .click_context import ClickContext
|
||||
|
|
@ -622,6 +623,7 @@ _dynamic_imports: typing.Dict[str, str] = {
|
|||
"BrowserSessionResponse": ".browser_session_response",
|
||||
"ChangeTierResponse": ".change_tier_response",
|
||||
"CheckoutSessionResponse": ".checkout_session_response",
|
||||
"ClearCacheResponse": ".clear_cache_response",
|
||||
"ClickAction": ".click_action",
|
||||
"ClickActionData": ".click_action_data",
|
||||
"ClickContext": ".click_context",
|
||||
|
|
@ -1166,6 +1168,7 @@ __all__ = [
|
|||
"BrowserSessionResponse",
|
||||
"ChangeTierResponse",
|
||||
"CheckoutSessionResponse",
|
||||
"ClearCacheResponse",
|
||||
"ClickAction",
|
||||
"ClickActionData",
|
||||
"ClickContext",
|
||||
|
|
|
|||
31
skyvern/client/types/clear_cache_response.py
Normal file
31
skyvern/client/types/clear_cache_response.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# This file was auto-generated by Fern from our API Definition.
|
||||
|
||||
import typing
|
||||
|
||||
import pydantic
|
||||
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
|
||||
|
||||
|
||||
class ClearCacheResponse(UniversalBaseModel):
|
||||
"""
|
||||
Response model for cache clearing operations.
|
||||
"""
|
||||
|
||||
deleted_count: int = pydantic.Field()
|
||||
"""
|
||||
Number of cached entries deleted
|
||||
"""
|
||||
|
||||
message: str = pydantic.Field()
|
||||
"""
|
||||
Status message
|
||||
"""
|
||||
|
||||
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
|
||||
|
|
@ -33,6 +33,16 @@ class CredentialResponse(UniversalBaseModel):
|
|||
Name of the credential
|
||||
"""
|
||||
|
||||
browser_profile_id: typing.Optional[str] = pydantic.Field(default=None)
|
||||
"""
|
||||
Browser profile ID linked to this credential
|
||||
"""
|
||||
|
||||
tested_url: typing.Optional[str] = pydantic.Field(default=None)
|
||||
"""
|
||||
Login page URL used during the credential test
|
||||
"""
|
||||
|
||||
if IS_PYDANTIC_V2:
|
||||
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ class WorkflowRun(UniversalBaseModel):
|
|||
sequential_key: typing.Optional[str] = None
|
||||
ai_fallback: typing.Optional[bool] = None
|
||||
code_gen: typing.Optional[bool] = None
|
||||
waiting_for_verification_code: typing.Optional[bool] = None
|
||||
verification_code_identifier: typing.Optional[str] = None
|
||||
verification_code_polling_started_at: typing.Optional[dt.datetime] = None
|
||||
queued_at: typing.Optional[dt.datetime] = None
|
||||
started_at: typing.Optional[dt.datetime] = None
|
||||
finished_at: typing.Optional[dt.datetime] = None
|
||||
|
|
|
|||
|
|
@ -33,12 +33,24 @@ class DiagnosticsResult(NamedTuple):
|
|||
def _is_local_request(request: Request) -> bool:
|
||||
host = request.client.host if request.client else None
|
||||
if not host:
|
||||
LOG.warning("No client host found in request", client=request.client)
|
||||
return False
|
||||
try:
|
||||
addr = ipaddress.ip_address(host)
|
||||
except ValueError:
|
||||
LOG.warning("Invalid IP address in request", host=host)
|
||||
return False
|
||||
return addr.is_loopback or addr.is_private
|
||||
# Check if request is from Docker host (gateway IP)
|
||||
# Docker typically uses 172.x.x.x or 192.168.x.x for bridge networks
|
||||
is_local = addr.is_loopback or addr.is_private
|
||||
LOG.info(
|
||||
"Checking if request is local",
|
||||
host=host,
|
||||
is_loopback=addr.is_loopback,
|
||||
is_private=addr.is_private,
|
||||
is_local=is_local,
|
||||
)
|
||||
return is_local
|
||||
|
||||
|
||||
def _require_local_access(request: Request) -> None:
|
||||
|
|
|
|||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -5581,7 +5581,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "skyvern"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aioboto3" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue