in which pylon replaces intercom (#2783)
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import { apiBaseUrl, artifactApiBaseUrl, envCredential } from "@/util/env";
|
||||
import axios from "axios";
|
||||
|
||||
type ApiVersion = "sans-api-v1" | "v1" | "v2";
|
||||
|
||||
const apiV1BaseUrl = apiBaseUrl;
|
||||
const apiV2BaseUrl = apiBaseUrl.replace("v1", "v2");
|
||||
const url = new URL(apiBaseUrl);
|
||||
const pathname = url.pathname.replace("/api", "");
|
||||
const apiSansApiV1BaseUrl = `${url.origin}${pathname}`;
|
||||
|
||||
const client = axios.create({
|
||||
baseURL: apiV1BaseUrl,
|
||||
@@ -20,6 +25,14 @@ const v2Client = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
const clientSansApiV1 = axios.create({
|
||||
baseURL: apiSansApiV1BaseUrl,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": envCredential,
|
||||
},
|
||||
});
|
||||
|
||||
const artifactApiClient = axios.create({
|
||||
baseURL: artifactApiBaseUrl,
|
||||
});
|
||||
@@ -52,18 +65,36 @@ export function removeApiKeyHeader() {
|
||||
|
||||
async function getClient(
|
||||
credentialGetter: CredentialGetter | null,
|
||||
version: string = "v1",
|
||||
version: ApiVersion = "v1",
|
||||
) {
|
||||
const get = () => {
|
||||
switch (version) {
|
||||
case "sans-api-v1":
|
||||
return clientSansApiV1;
|
||||
case "v1":
|
||||
return client;
|
||||
case "v2":
|
||||
return v2Client;
|
||||
default: {
|
||||
throw new Error(`Unknown version: ${version}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (credentialGetter) {
|
||||
removeApiKeyHeader();
|
||||
|
||||
const credential = await credentialGetter();
|
||||
|
||||
if (!credential) {
|
||||
console.warn("No credential found");
|
||||
return version === "v1" ? client : v2Client;
|
||||
return get();
|
||||
}
|
||||
|
||||
setAuthorizationHeader(credential);
|
||||
}
|
||||
return version === "v1" ? client : v2Client;
|
||||
|
||||
return get();
|
||||
}
|
||||
|
||||
export type CredentialGetter = () => Promise<string | null>;
|
||||
|
||||
@@ -411,3 +411,7 @@ export const RunEngine = {
|
||||
} as const;
|
||||
|
||||
export type RunEngine = (typeof RunEngine)[keyof typeof RunEngine];
|
||||
|
||||
export type PylonEmailHash = {
|
||||
hash: string;
|
||||
};
|
||||
|
||||
7
skyvern-frontend/src/global.d.ts
vendored
Normal file
7
skyvern-frontend/src/global.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
interface Window {
|
||||
pylon: {
|
||||
chat_settings: { [k: string]: string };
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Pylon: (method: string, ...args: any[]) => void;
|
||||
}
|
||||
@@ -275,10 +275,15 @@ class Settings(BaseSettings):
|
||||
|
||||
SKYVERN_BROWSER_VNC_PORT: int = 6080
|
||||
"""
|
||||
The websockified port on which the VNC server of a persistent browser is
|
||||
The websockified port on which the VNC server of a persistent browser is
|
||||
listening.
|
||||
"""
|
||||
|
||||
PYLON_IDENTITY_VERIFICATION_SECRET: str | None = None
|
||||
"""
|
||||
The secret used to sign the email/identity of the user.
|
||||
"""
|
||||
|
||||
def get_model_name_to_llm_key(self) -> dict[str, dict[str, str]]:
|
||||
"""
|
||||
Keys are model names available to blocks in the frontend. These map to key names
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from skyvern.forge.sdk.routes import agent_protocol # noqa: F401
|
||||
from skyvern.forge.sdk.routes import browser_sessions # noqa: F401
|
||||
from skyvern.forge.sdk.routes import credentials # noqa: F401
|
||||
from skyvern.forge.sdk.routes import pylon # noqa: F401
|
||||
from skyvern.forge.sdk.routes import streaming # noqa: F401
|
||||
from skyvern.forge.sdk.routes import streaming_commands # noqa: F401
|
||||
from skyvern.forge.sdk.routes import streaming_vnc # noqa: F401
|
||||
|
||||
34
skyvern/forge/sdk/routes/pylon.py
Normal file
34
skyvern/forge/sdk/routes/pylon.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
import structlog
|
||||
from fastapi import Query
|
||||
|
||||
from skyvern.config import settings
|
||||
from skyvern.forge.sdk.routes.routers import base_router
|
||||
from skyvern.forge.sdk.schemas.pylon import PylonHash
|
||||
|
||||
LOG = structlog.get_logger()
|
||||
|
||||
|
||||
@base_router.get(
|
||||
"/pylon/email_hash",
|
||||
include_in_schema=False,
|
||||
response_model=PylonHash,
|
||||
)
|
||||
def get_pylon_email_hash(email: str = Query(...)) -> PylonHash:
|
||||
no_hash = "???-no-hash-???"
|
||||
secret = settings.PYLON_IDENTITY_VERIFICATION_SECRET
|
||||
|
||||
if not secret:
|
||||
LOG.error("No Pylon identity verification secret", email=email)
|
||||
return PylonHash(hash=no_hash)
|
||||
|
||||
try:
|
||||
secret_bytes = bytes.fromhex(secret)
|
||||
signature = hmac.new(secret_bytes, email.encode(), hashlib.sha256).hexdigest()
|
||||
|
||||
return PylonHash(hash=signature)
|
||||
except Exception:
|
||||
LOG.exception("Failed to generate Pylon email hash", email=email)
|
||||
return PylonHash(hash=no_hash)
|
||||
5
skyvern/forge/sdk/schemas/pylon.py
Normal file
5
skyvern/forge/sdk/schemas/pylon.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class PylonHash(BaseModel):
|
||||
hash: str
|
||||
Reference in New Issue
Block a user